tgui-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/.editorconfig +10 -0
  2. package/.eslintrc.cjs +78 -0
  3. package/.gitattributes +4 -0
  4. package/.prettierrc.yml +1 -0
  5. package/.vscode/extensions.json +6 -0
  6. package/.vscode/settings.json +5 -0
  7. package/README.md +1 -0
  8. package/global.d.ts +173 -0
  9. package/package.json +25 -0
  10. package/src/assets.ts +43 -0
  11. package/src/backend.ts +369 -0
  12. package/src/common/collections.ts +349 -0
  13. package/src/common/color.ts +94 -0
  14. package/src/common/events.ts +45 -0
  15. package/src/common/exhaustive.ts +19 -0
  16. package/src/common/fp.ts +38 -0
  17. package/src/common/keycodes.ts +86 -0
  18. package/src/common/keys.ts +39 -0
  19. package/src/common/math.ts +98 -0
  20. package/src/common/perf.ts +72 -0
  21. package/src/common/random.ts +32 -0
  22. package/src/common/react.ts +65 -0
  23. package/src/common/redux.ts +196 -0
  24. package/src/common/storage.js +196 -0
  25. package/src/common/string.ts +173 -0
  26. package/src/common/timer.ts +68 -0
  27. package/src/common/type-utils.ts +41 -0
  28. package/src/common/types.ts +9 -0
  29. package/src/common/uuid.ts +24 -0
  30. package/src/common/vector.ts +51 -0
  31. package/src/components/AnimatedNumber.tsx +185 -0
  32. package/src/components/Autofocus.tsx +23 -0
  33. package/src/components/Blink.jsx +69 -0
  34. package/src/components/BlockQuote.tsx +15 -0
  35. package/src/components/BodyZoneSelector.tsx +149 -0
  36. package/src/components/Box.tsx +255 -0
  37. package/src/components/Button.tsx +415 -0
  38. package/src/components/ByondUi.jsx +121 -0
  39. package/src/components/Chart.tsx +160 -0
  40. package/src/components/Collapsible.tsx +45 -0
  41. package/src/components/ColorBox.tsx +30 -0
  42. package/src/components/Dialog.tsx +85 -0
  43. package/src/components/Dimmer.tsx +19 -0
  44. package/src/components/Divider.tsx +26 -0
  45. package/src/components/DmIcon.tsx +72 -0
  46. package/src/components/DraggableControl.jsx +282 -0
  47. package/src/components/Dropdown.tsx +246 -0
  48. package/src/components/FakeTerminal.jsx +52 -0
  49. package/src/components/FitText.tsx +99 -0
  50. package/src/components/Flex.tsx +105 -0
  51. package/src/components/Grid.tsx +44 -0
  52. package/src/components/Icon.tsx +91 -0
  53. package/src/components/Image.tsx +63 -0
  54. package/src/components/InfinitePlane.jsx +192 -0
  55. package/src/components/Input.tsx +181 -0
  56. package/src/components/KeyListener.tsx +40 -0
  57. package/src/components/Knob.tsx +185 -0
  58. package/src/components/LabeledControls.tsx +50 -0
  59. package/src/components/LabeledList.tsx +130 -0
  60. package/src/components/MenuBar.tsx +238 -0
  61. package/src/components/Modal.tsx +25 -0
  62. package/src/components/NoticeBox.tsx +48 -0
  63. package/src/components/NumberInput.tsx +328 -0
  64. package/src/components/Popper.tsx +100 -0
  65. package/src/components/ProgressBar.tsx +79 -0
  66. package/src/components/RestrictedInput.jsx +301 -0
  67. package/src/components/RoundGauge.tsx +189 -0
  68. package/src/components/Section.tsx +125 -0
  69. package/src/components/Slider.tsx +173 -0
  70. package/src/components/Stack.tsx +101 -0
  71. package/src/components/StyleableSection.tsx +30 -0
  72. package/src/components/Table.tsx +90 -0
  73. package/src/components/Tabs.tsx +90 -0
  74. package/src/components/TextArea.tsx +198 -0
  75. package/src/components/TimeDisplay.jsx +64 -0
  76. package/src/components/Tooltip.tsx +147 -0
  77. package/src/components/TrackOutsideClicks.tsx +35 -0
  78. package/src/components/VirtualList.tsx +69 -0
  79. package/src/constants.ts +355 -0
  80. package/src/debug/KitchenSink.jsx +56 -0
  81. package/src/debug/actions.js +11 -0
  82. package/src/debug/hooks.js +10 -0
  83. package/src/debug/index.ts +10 -0
  84. package/src/debug/middleware.js +86 -0
  85. package/src/debug/reducer.js +22 -0
  86. package/src/debug/selectors.js +7 -0
  87. package/src/drag.ts +280 -0
  88. package/src/events.ts +237 -0
  89. package/src/focus.ts +25 -0
  90. package/src/format.ts +173 -0
  91. package/src/hotkeys.ts +212 -0
  92. package/src/http.ts +16 -0
  93. package/src/layouts/Layout.tsx +75 -0
  94. package/src/layouts/NtosWindow.tsx +162 -0
  95. package/src/layouts/Pane.tsx +56 -0
  96. package/src/layouts/Window.tsx +227 -0
  97. package/src/renderer.ts +50 -0
  98. package/styles/base.scss +32 -0
  99. package/styles/colors.scss +92 -0
  100. package/styles/components/BlockQuote.scss +20 -0
  101. package/styles/components/Button.scss +175 -0
  102. package/styles/components/ColorBox.scss +12 -0
  103. package/styles/components/Dialog.scss +105 -0
  104. package/styles/components/Dimmer.scss +22 -0
  105. package/styles/components/Divider.scss +27 -0
  106. package/styles/components/Dropdown.scss +72 -0
  107. package/styles/components/Flex.scss +31 -0
  108. package/styles/components/Icon.scss +25 -0
  109. package/styles/components/Input.scss +68 -0
  110. package/styles/components/Knob.scss +131 -0
  111. package/styles/components/LabeledList.scss +49 -0
  112. package/styles/components/MenuBar.scss +75 -0
  113. package/styles/components/Modal.scss +14 -0
  114. package/styles/components/NoticeBox.scss +65 -0
  115. package/styles/components/NumberInput.scss +76 -0
  116. package/styles/components/ProgressBar.scss +63 -0
  117. package/styles/components/RoundGauge.scss +88 -0
  118. package/styles/components/Section.scss +143 -0
  119. package/styles/components/Slider.scss +54 -0
  120. package/styles/components/Stack.scss +59 -0
  121. package/styles/components/Table.scss +44 -0
  122. package/styles/components/Tabs.scss +144 -0
  123. package/styles/components/TextArea.scss +84 -0
  124. package/styles/components/Tooltip.scss +24 -0
  125. package/styles/functions.scss +79 -0
  126. package/styles/layouts/Layout.scss +57 -0
  127. package/styles/layouts/NtosHeader.scss +20 -0
  128. package/styles/layouts/NtosWindow.scss +26 -0
  129. package/styles/layouts/TitleBar.scss +111 -0
  130. package/styles/layouts/Window.scss +103 -0
  131. package/styles/main.scss +97 -0
  132. package/styles/reset.scss +68 -0
  133. package/styles/themes/abductor.scss +68 -0
  134. package/styles/themes/admin.scss +38 -0
  135. package/styles/themes/cardtable.scss +57 -0
  136. package/styles/themes/hackerman.scss +70 -0
  137. package/styles/themes/malfunction.scss +67 -0
  138. package/styles/themes/neutral.scss +50 -0
  139. package/styles/themes/ntOS95.scss +166 -0
  140. package/styles/themes/ntos.scss +44 -0
  141. package/styles/themes/ntos_cat.scss +148 -0
  142. package/styles/themes/ntos_darkmode.scss +44 -0
  143. package/styles/themes/ntos_lightmode.scss +67 -0
  144. package/styles/themes/ntos_spooky.scss +69 -0
  145. package/styles/themes/ntos_synth.scss +99 -0
  146. package/styles/themes/ntos_terminal.scss +112 -0
  147. package/styles/themes/paper.scss +184 -0
  148. package/styles/themes/retro.scss +72 -0
  149. package/styles/themes/spookyconsole.scss +73 -0
  150. package/styles/themes/syndicate.scss +67 -0
  151. package/styles/themes/wizard.scss +68 -0
  152. package/tsconfig.json +34 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { classes } from '../common/react';
8
+ import { useEffect, useRef } from 'react';
9
+
10
+ import {
11
+ BoxProps,
12
+ computeBoxClassName,
13
+ computeBoxProps,
14
+ } from '../components/Box';
15
+ import { addScrollableNode, removeScrollableNode } from '../events';
16
+
17
+ type Props = Partial<{
18
+ theme: string;
19
+ }> &
20
+ BoxProps;
21
+
22
+ export function Layout(props: Props) {
23
+ const { className, theme = 'nanotrasen', children, ...rest } = props;
24
+
25
+ return (
26
+ <div className={'theme-' + theme}>
27
+ <div
28
+ className={classes(['Layout', className, computeBoxClassName(rest)])}
29
+ {...computeBoxProps(rest)}
30
+ >
31
+ {children}
32
+ </div>
33
+ </div>
34
+ );
35
+ }
36
+
37
+ type ContentProps = Partial<{
38
+ scrollable: boolean;
39
+ }> &
40
+ BoxProps;
41
+
42
+ function LayoutContent(props: ContentProps) {
43
+ const { className, scrollable, children, ...rest } = props;
44
+ const node = useRef<HTMLDivElement>(null);
45
+
46
+ useEffect(() => {
47
+ const self = node.current;
48
+
49
+ if (self && scrollable) {
50
+ addScrollableNode(self);
51
+ }
52
+ return () => {
53
+ if (self && scrollable) {
54
+ removeScrollableNode(self);
55
+ }
56
+ };
57
+ }, []);
58
+
59
+ return (
60
+ <div
61
+ className={classes([
62
+ 'Layout__content',
63
+ scrollable && 'Layout__content--scrollable',
64
+ className,
65
+ computeBoxClassName(rest),
66
+ ])}
67
+ ref={node}
68
+ {...computeBoxProps(rest)}
69
+ >
70
+ {children}
71
+ </div>
72
+ );
73
+ }
74
+
75
+ Layout.Content = LayoutContent;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { BooleanLike } from '../common/react';
8
+
9
+ import { resolveAsset } from '../assets';
10
+ import { useBackend } from '../backend';
11
+ import { Box } from '../components/Box';
12
+ import { Button } from '../components/Button';
13
+ import { Window } from './Window';
14
+
15
+ export type NTOSData = {
16
+ authenticatedUser: string | null;
17
+ authIDName: string;
18
+ comp_light_color: string;
19
+ has_id: BooleanLike;
20
+ has_light: BooleanLike;
21
+ id_name: string;
22
+ light_on: BooleanLike;
23
+ login: Login;
24
+ pai: string | null;
25
+ PC_batteryicon: string | null;
26
+ PC_batterypercent: string | null;
27
+ PC_device_theme: string;
28
+ PC_lowpower_mode: BooleanLike;
29
+ PC_ntneticon: string;
30
+ PC_programheaders: Program[];
31
+ PC_showexitprogram: BooleanLike;
32
+ PC_stationdate: string;
33
+ PC_stationtime: string;
34
+ programs: Program[];
35
+ proposed_login: Login;
36
+ removable_media: string[];
37
+ show_imprint: BooleanLike;
38
+ };
39
+
40
+ type Program = {
41
+ alert: BooleanLike;
42
+ desc: string;
43
+ header_program: BooleanLike;
44
+ icon: string;
45
+ name: string;
46
+ running: BooleanLike;
47
+ };
48
+
49
+ type Login = {
50
+ IDInserted?: BooleanLike;
51
+ IDJob: string | null;
52
+ IDName: string | null;
53
+ };
54
+
55
+ export const NtosWindow = (props) => {
56
+ const { title, width = 575, height = 700, children } = props;
57
+ const { act, data } = useBackend<NTOSData>();
58
+ const {
59
+ PC_device_theme,
60
+ PC_batteryicon,
61
+ PC_batterypercent,
62
+ PC_ntneticon,
63
+ PC_stationdate,
64
+ PC_stationtime,
65
+ PC_programheaders = [],
66
+ PC_showexitprogram,
67
+ PC_lowpower_mode,
68
+ } = data;
69
+
70
+ return (
71
+ <Window title={title} width={width} height={height} theme={PC_device_theme}>
72
+ <div className="NtosWindow">
73
+ <div className="NtosWindow__header NtosHeader">
74
+ <div className="NtosHeader__left">
75
+ <Box inline bold mr={2}>
76
+ <Button
77
+ width="26px"
78
+ lineHeight="22px"
79
+ textAlign="left"
80
+ tooltip={PC_stationdate}
81
+ color="transparent"
82
+ icon="calendar"
83
+ tooltipPosition="bottom"
84
+ />
85
+ {PC_stationtime}
86
+ </Box>
87
+ <Box inline italic mr={2} opacity={0.33}>
88
+ {(PC_device_theme === 'syndicate' && 'Syndix') || 'NtOS'}
89
+ {!!PC_lowpower_mode && ' - RUNNING ON LOW POWER MODE'}
90
+ </Box>
91
+ </div>
92
+ <div className="NtosHeader__right">
93
+ {PC_programheaders.map((header) => (
94
+ <Box key={header.icon} inline mr={1}>
95
+ <img
96
+ className="NtosHeader__icon"
97
+ src={resolveAsset(header.icon)}
98
+ />
99
+ </Box>
100
+ ))}
101
+ <Box inline>
102
+ {PC_ntneticon && (
103
+ <img
104
+ className="NtosHeader__icon"
105
+ src={resolveAsset(PC_ntneticon)}
106
+ />
107
+ )}
108
+ </Box>
109
+ {!!PC_batteryicon && (
110
+ <Box inline mr={1}>
111
+ <img
112
+ className="NtosHeader__icon"
113
+ src={resolveAsset(PC_batteryicon)}
114
+ />
115
+ {PC_batterypercent}
116
+ </Box>
117
+ )}
118
+ {!!PC_showexitprogram && (
119
+ <Button
120
+ color="transparent"
121
+ icon="window-minimize-o"
122
+ tooltip="Minimize"
123
+ tooltipPosition="bottom"
124
+ onClick={() => act('PC_minimize')}
125
+ />
126
+ )}
127
+ {!!PC_showexitprogram && (
128
+ <Button
129
+ color="transparent"
130
+ icon="window-close-o"
131
+ tooltip="Close"
132
+ tooltipPosition="bottom-start"
133
+ onClick={() => act('PC_exit')}
134
+ />
135
+ )}
136
+ {!PC_showexitprogram && (
137
+ <Button
138
+ textAlign="center"
139
+ color="transparent"
140
+ icon="power-off"
141
+ tooltip="Power off"
142
+ tooltipPosition="bottom-start"
143
+ onClick={() => act('PC_shutdown')}
144
+ />
145
+ )}
146
+ </div>
147
+ </div>
148
+ {children}
149
+ </div>
150
+ </Window>
151
+ );
152
+ };
153
+
154
+ const NtosWindowContent = (props) => {
155
+ return (
156
+ <div className="NtosWindow__content">
157
+ <Window.Content {...props} />
158
+ </div>
159
+ );
160
+ };
161
+
162
+ NtosWindow.Content = NtosWindowContent;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { classes } from '../common/react';
8
+
9
+ import { useBackend } from '../backend';
10
+ import { Box, BoxProps } from '../components/Box';
11
+ import { useDebug } from '../debug';
12
+ import { Layout } from './Layout';
13
+
14
+ type Props = Partial<{
15
+ theme: string;
16
+ }> &
17
+ BoxProps;
18
+
19
+ export function Pane(props: Props) {
20
+ const { theme, children, className, ...rest } = props;
21
+ const { suspended } = useBackend();
22
+ const { debugLayout = false } = useDebug();
23
+
24
+ return (
25
+ <Layout className={classes(['Window', className])} theme={theme} {...rest}>
26
+ <Box fillPositionedParent className={debugLayout && 'debug-layout'}>
27
+ {!suspended && children}
28
+ </Box>
29
+ </Layout>
30
+ );
31
+ }
32
+
33
+ type ContentProps = Partial<{
34
+ fitted: boolean;
35
+ scrollable: boolean;
36
+ }> &
37
+ BoxProps;
38
+
39
+ function PaneContent(props: ContentProps) {
40
+ const { className, fitted, children, ...rest } = props;
41
+
42
+ return (
43
+ <Layout.Content
44
+ className={classes(['Window__content', className])}
45
+ {...rest}
46
+ >
47
+ {fitted ? (
48
+ children
49
+ ) : (
50
+ <div className="Window__contentPadding">{children}</div>
51
+ )}
52
+ </Layout.Content>
53
+ );
54
+ }
55
+
56
+ Pane.Content = PaneContent;
@@ -0,0 +1,227 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { classes } from '../common/react';
8
+ import { decodeHtmlEntities, toTitleCase } from '../common/string';
9
+ import { PropsWithChildren, ReactNode, useEffect } from 'react';
10
+
11
+ import { backendSuspendStart, globalStore, useBackend } from '../backend';
12
+ import { Icon } from '../components/Icon';
13
+ import { BoxProps } from '../components/Box';
14
+ import { UI_DISABLED, UI_INTERACTIVE, UI_UPDATE } from '../constants';
15
+ import { useDebug } from '../debug';
16
+ import { toggleKitchenSink } from '../debug/actions';
17
+ import {
18
+ dragStartHandler,
19
+ recallWindowGeometry,
20
+ resizeStartHandler,
21
+ setWindowKey,
22
+ } from '../drag';
23
+ import { Layout } from './Layout';
24
+
25
+ const DEFAULT_SIZE: [number, number] = [400, 600];
26
+
27
+ type Props = Partial<{
28
+ buttons: ReactNode;
29
+ canClose: boolean;
30
+ height: number;
31
+ theme: string;
32
+ title: string;
33
+ width: number;
34
+ }> &
35
+ PropsWithChildren;
36
+
37
+ export const Window = (props: Props) => {
38
+ const {
39
+ canClose = true,
40
+ theme,
41
+ title,
42
+ children,
43
+ buttons,
44
+ width,
45
+ height,
46
+ } = props;
47
+
48
+ const { config, suspended } = useBackend();
49
+ const { debugLayout = false } = useDebug();
50
+
51
+ useEffect(() => {
52
+ const updateGeometry = () => {
53
+ const options = {
54
+ ...config.window,
55
+ size: DEFAULT_SIZE,
56
+ };
57
+
58
+ if (width && height) {
59
+ options.size = [width, height];
60
+ }
61
+ if (config.window?.key) {
62
+ setWindowKey(config.window.key);
63
+ }
64
+ recallWindowGeometry(options);
65
+ };
66
+
67
+ Byond.winset(Byond.windowId, {
68
+ 'can-close': Boolean(canClose),
69
+ });
70
+
71
+ updateGeometry();
72
+
73
+ return () => {};
74
+ }, [width, height]);
75
+
76
+ const dispatch = globalStore.dispatch;
77
+ const fancy = config.window?.fancy;
78
+
79
+ // Determine when to show dimmer
80
+ const showDimmer =
81
+ config.user &&
82
+ (config.user.observer
83
+ ? config.status < UI_DISABLED
84
+ : config.status < UI_INTERACTIVE);
85
+
86
+ return suspended ? null : (
87
+ <Layout className="Window" theme={theme}>
88
+ <TitleBar
89
+ className="Window__titleBar"
90
+ title={title || decodeHtmlEntities(config.title)}
91
+ status={config.status}
92
+ fancy={fancy}
93
+ onDragStart={dragStartHandler}
94
+ onClose={() => {
95
+ dispatch(backendSuspendStart());
96
+ }}
97
+ canClose={canClose}
98
+ >
99
+ {buttons}
100
+ </TitleBar>
101
+ <div className={classes(['Window__rest', debugLayout && 'debug-layout'])}>
102
+ {!suspended && children}
103
+ {showDimmer && <div className="Window__dimmer" />}
104
+ </div>
105
+ {fancy && (
106
+ <>
107
+ <div
108
+ className="Window__resizeHandle__e"
109
+ onMouseDown={resizeStartHandler(1, 0) as any}
110
+ />
111
+ <div
112
+ className="Window__resizeHandle__s"
113
+ onMouseDown={resizeStartHandler(0, 1) as any}
114
+ />
115
+ <div
116
+ className="Window__resizeHandle__se"
117
+ onMouseDown={resizeStartHandler(1, 1) as any}
118
+ />
119
+ </>
120
+ )}
121
+ </Layout>
122
+ );
123
+ };
124
+
125
+ type ContentProps = Partial<{
126
+ className: string;
127
+ fitted: boolean;
128
+ scrollable: boolean;
129
+ vertical: boolean;
130
+ }> &
131
+ BoxProps &
132
+ PropsWithChildren;
133
+
134
+ const WindowContent = (props: ContentProps) => {
135
+ const { className, fitted, children, ...rest } = props;
136
+
137
+ return (
138
+ <Layout.Content
139
+ className={classes(['Window__content', className])}
140
+ {...rest}
141
+ >
142
+ {(fitted && children) || (
143
+ <div className="Window__contentPadding">{children}</div>
144
+ )}
145
+ </Layout.Content>
146
+ );
147
+ };
148
+
149
+ Window.Content = WindowContent;
150
+
151
+ const statusToColor = (status) => {
152
+ switch (status) {
153
+ case UI_INTERACTIVE:
154
+ return 'good';
155
+ case UI_UPDATE:
156
+ return 'average';
157
+ case UI_DISABLED:
158
+ default:
159
+ return 'bad';
160
+ }
161
+ };
162
+
163
+ type TitleBarProps = Partial<{
164
+ canClose: boolean;
165
+ className: string;
166
+ fancy: boolean;
167
+ onClose: (e) => void;
168
+ onDragStart: (e) => void;
169
+ status: number;
170
+ title: string;
171
+ }> &
172
+ PropsWithChildren;
173
+
174
+ const TitleBar = (props: TitleBarProps) => {
175
+ const {
176
+ className,
177
+ title,
178
+ status,
179
+ canClose,
180
+ fancy,
181
+ onDragStart,
182
+ onClose,
183
+ children,
184
+ } = props;
185
+ const dispatch = globalStore.dispatch;
186
+
187
+ const finalTitle =
188
+ (typeof title === 'string' &&
189
+ title === title.toLowerCase() &&
190
+ toTitleCase(title)) ||
191
+ title;
192
+
193
+ return (
194
+ <div className={classes(['TitleBar', className])}>
195
+ {(status === undefined && (
196
+ <Icon className="TitleBar__statusIcon" name="tools" opacity={0.5} />
197
+ )) || (
198
+ <Icon
199
+ className="TitleBar__statusIcon"
200
+ color={statusToColor(status)}
201
+ name="eye"
202
+ />
203
+ )}
204
+ <div
205
+ className="TitleBar__dragZone"
206
+ onMouseDown={(e) => fancy && onDragStart && onDragStart(e)}
207
+ />
208
+ <div className="TitleBar__title">
209
+ {finalTitle}
210
+ {!!children && <div className="TitleBar__buttons">{children}</div>}
211
+ </div>
212
+ {process.env.NODE_ENV !== 'production' && (
213
+ <div
214
+ className="TitleBar__devBuildIndicator"
215
+ onClick={() => dispatch(toggleKitchenSink())}
216
+ >
217
+ <Icon name="bug" />
218
+ </div>
219
+ )}
220
+ {Boolean(fancy && canClose) && (
221
+ <div className="TitleBar__close TitleBar__clickable" onClick={onClose}>
222
+ ×
223
+ </div>
224
+ )}
225
+ </div>
226
+ );
227
+ };
@@ -0,0 +1,50 @@
1
+ import { perf } from './common/perf';
2
+ import { ReactNode } from 'react';
3
+ import { createRoot, Root } from 'react-dom/client';
4
+
5
+ let reactRoot: Root;
6
+ let initialRender: string | boolean = true;
7
+ let suspended = false;
8
+
9
+ // These functions are used purely for profiling.
10
+ export const resumeRenderer = () => {
11
+ initialRender = initialRender || 'resumed';
12
+ suspended = false;
13
+ };
14
+
15
+ export const suspendRenderer = () => {
16
+ suspended = true;
17
+ };
18
+
19
+ type CreateRenderer = <T extends unknown[] = [unknown]>(
20
+ getVNode?: (...args: T) => ReactNode
21
+ ) => (...args: T) => void;
22
+
23
+ enum Render {
24
+ Start = 'render/start',
25
+ Finish = 'render/finish',
26
+ }
27
+
28
+ // prettier-ignore
29
+ export const createRenderer: CreateRenderer = (getVNode) => (...args) => {
30
+ perf.mark(Render.Start);
31
+ // Start rendering
32
+ if (!reactRoot) {
33
+ const element = document.getElementById('react-root');
34
+ reactRoot = createRoot(element!);
35
+ }
36
+ if (getVNode) {
37
+ reactRoot.render(getVNode(...args));
38
+ }
39
+ else {
40
+ reactRoot.render(args[0] as any);
41
+ }
42
+ perf.mark(Render.Finish);
43
+ if (suspended) {
44
+ return;
45
+ }
46
+
47
+ if (initialRender) {
48
+ initialRender = false;
49
+ }
50
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) 2020 Aleksej Komarov
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ @use 'sass:color';
7
+ @use 'sass:math';
8
+
9
+ $color-fg: #ffffff !default;
10
+ $color-bg: #252525 !default;
11
+ $color-bg-section: rgba(0, 0, 0, 0.33) !default;
12
+ $color-bg-grad-spread: 2% !default;
13
+ $color-bg-start: color.adjust(
14
+ $color-bg,
15
+ $lightness: $color-bg-grad-spread
16
+ ) !default;
17
+ $color-bg-end: color.adjust(
18
+ $color-bg,
19
+ $lightness: -$color-bg-grad-spread
20
+ ) !default;
21
+
22
+ $unit: 12px;
23
+ $font-size: 1 * $unit !default;
24
+ $border-radius: 0.16em !default;
25
+
26
+ @function em($px) {
27
+ @return 1em * math.div($px, $unit);
28
+ }
29
+
30
+ @function rem($px) {
31
+ @return 1rem * math.div($px, $unit);
32
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Copyright (c) 2020 Aleksej Komarov
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ @use "sass:color";
7
+ @use "sass:map";
8
+ @use "sass:meta";
9
+
10
+ // Base colors
11
+ $black: #000000 !default;
12
+ $white: #ffffff !default;
13
+ $red: #db2828 !default;
14
+ $orange: #f2711c !default;
15
+ $yellow: #fbd608 !default;
16
+ $olive: #b5cc18 !default;
17
+ $green: #20b142 !default;
18
+ $teal: #00b5ad !default;
19
+ $blue: #2185d0 !default;
20
+ $violet: #6435c9 !default;
21
+ $purple: #a333c8 !default;
22
+ $pink: #e03997 !default;
23
+ $brown: #a5673f !default;
24
+ $grey: #767676 !default;
25
+ $light-grey: #aaa !default;
26
+
27
+ $primary: #4972a1 !default;
28
+ $good: #5baa27 !default;
29
+ $average: #f08f11 !default;
30
+ $bad: #db2828 !default;
31
+ $label: #7e90a7 !default;
32
+
33
+ // Background and foreground color lightness ratios
34
+ $bg-lightness: -15% !default;
35
+ $fg-lightness: 10% !default;
36
+
37
+ @function bg($color) {
38
+ @return color.scale($color, $lightness: $bg-lightness);
39
+ }
40
+
41
+ @function fg($color) {
42
+ @return color.scale($color, $lightness: $fg-lightness);
43
+ }
44
+
45
+ // Mappings of color names
46
+
47
+ $_gen_map: (
48
+ "black": $black,
49
+ "white": $white,
50
+ "red": $red,
51
+ "orange": $orange,
52
+ "yellow": $yellow,
53
+ "olive": $olive,
54
+ "green": $green,
55
+ "teal": $teal,
56
+ "blue": $blue,
57
+ "violet": $violet,
58
+ "purple": $purple,
59
+ "pink": $pink,
60
+ "brown": $brown,
61
+ "grey": $grey,
62
+ "light-grey": $light-grey,
63
+ "good": $good,
64
+ "average": $average,
65
+ "bad": $bad,
66
+ "label": $label,
67
+ );
68
+
69
+ // Foreground color names for which to generate a color map
70
+ $fg-map-keys: map.keys($_gen_map) !default;
71
+ // Background color names for which to generate a color map
72
+ $bg-map-keys: map.keys($_gen_map) !default;
73
+
74
+ $fg-map: ();
75
+ @each $color-name in $fg-map-keys {
76
+ $fg-map: map-merge(
77
+ $fg-map,
78
+ (
79
+ $color-name: fg(map.get($_gen_map, $color-name)),
80
+ )
81
+ );
82
+ }
83
+
84
+ $bg-map: ();
85
+ @each $color-name in $bg-map-keys {
86
+ $bg-map: map-merge(
87
+ $bg-map,
88
+ (
89
+ $color-name: bg(map.get($_gen_map, $color-name)),
90
+ )
91
+ );
92
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copyright (c) 2020 Aleksej Komarov
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ @use '../base.scss';
7
+ @use '../colors.scss';
8
+
9
+ $color-default: colors.fg(colors.$label) !default;
10
+
11
+ .BlockQuote {
12
+ color: $color-default;
13
+ border-left: base.em(2px) solid $color-default;
14
+ padding-left: 0.5em;
15
+ margin-bottom: 0.5em;
16
+
17
+ &:last-child {
18
+ margin-bottom: 0;
19
+ }
20
+ }