@arcanewizards/timecode-toolbox 0.0.3 → 0.1.1

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/frontend/index.d.mts +13 -0
  3. package/dist/components/frontend/index.d.ts +13 -0
  4. package/dist/components/frontend/index.js +18978 -0
  5. package/dist/components/frontend/index.mjs +19016 -0
  6. package/dist/entrypoint.css +2788 -0
  7. package/dist/entrypoint.js +42821 -0
  8. package/dist/entrypoint.js.map +7 -0
  9. package/dist/frontend.js +42818 -0
  10. package/dist/frontend.js.map +7 -0
  11. package/dist/index.d.mts +59 -0
  12. package/dist/index.d.ts +59 -0
  13. package/dist/index.js +14894 -0
  14. package/dist/index.mjs +14921 -0
  15. package/dist/start.d.mts +1 -0
  16. package/dist/start.d.ts +1 -0
  17. package/dist/start.js +14886 -0
  18. package/dist/start.mjs +14918 -0
  19. package/package.json +37 -28
  20. package/.turbo/turbo-build.log +0 -55
  21. package/CHANGELOG.md +0 -24
  22. package/eslint.config.mjs +0 -49
  23. package/src/app.tsx +0 -147
  24. package/src/components/backend/index.ts +0 -6
  25. package/src/components/backend/toolbox-root.ts +0 -119
  26. package/src/components/frontend/constants.ts +0 -81
  27. package/src/components/frontend/entrypoint.ts +0 -12
  28. package/src/components/frontend/frontend.css +0 -108
  29. package/src/components/frontend/index.tsx +0 -46
  30. package/src/components/frontend/toolbox/content.tsx +0 -45
  31. package/src/components/frontend/toolbox/context.tsx +0 -63
  32. package/src/components/frontend/toolbox/core/size-aware-div.tsx +0 -51
  33. package/src/components/frontend/toolbox/core/timecode-display.tsx +0 -592
  34. package/src/components/frontend/toolbox/generators.tsx +0 -318
  35. package/src/components/frontend/toolbox/inputs.tsx +0 -484
  36. package/src/components/frontend/toolbox/outputs.tsx +0 -581
  37. package/src/components/frontend/toolbox/preferences.ts +0 -25
  38. package/src/components/frontend/toolbox/root.tsx +0 -335
  39. package/src/components/frontend/toolbox/settings.tsx +0 -54
  40. package/src/components/frontend/toolbox/types.ts +0 -28
  41. package/src/components/frontend/toolbox/util.tsx +0 -61
  42. package/src/components/proto.ts +0 -420
  43. package/src/config.ts +0 -7
  44. package/src/generators/clock.tsx +0 -206
  45. package/src/generators/index.tsx +0 -15
  46. package/src/index.ts +0 -38
  47. package/src/inputs/artnet.tsx +0 -305
  48. package/src/inputs/index.tsx +0 -13
  49. package/src/inputs/tcnet.tsx +0 -272
  50. package/src/outputs/artnet.tsx +0 -170
  51. package/src/outputs/index.tsx +0 -11
  52. package/src/start.ts +0 -47
  53. package/src/tree.ts +0 -133
  54. package/src/types.ts +0 -12
  55. package/src/urls.ts +0 -49
  56. package/src/util.ts +0 -82
  57. package/tailwind.config.cjs +0 -7
  58. package/tsconfig.json +0 -10
  59. package/tsup.config.ts +0 -10
@@ -1,581 +0,0 @@
1
- import {
2
- Dispatch,
3
- FC,
4
- SetStateAction,
5
- useCallback,
6
- useContext,
7
- useEffect,
8
- useMemo,
9
- useState,
10
- } from 'react';
11
- import { STRINGS } from '../constants';
12
- import { PrimaryToolboxSection } from './util';
13
- import { DialogMode, SettingsProps } from './types';
14
- import {
15
- ControlButton,
16
- ControlColorSelect,
17
- ControlDialog,
18
- ControlDialogButtons,
19
- ControlInput,
20
- ControlLabel,
21
- ControlSelect,
22
- } from '@arcanewizards/sigil/frontend/controls';
23
- import { ConfigContext, NetworkContext, useApplicationState } from './context';
24
- import {
25
- OutputConfig,
26
- OutputDefinition,
27
- TimecodeInstance,
28
- ToolboxRootGetNetworkInterfacesReturn,
29
- } from '../../proto';
30
- import { TimecodeTreeDisplay } from './core/timecode-display';
31
- import { Icon } from '@arcanejs/toolkit-frontend/components/core';
32
- import {
33
- ChangeCommitContext,
34
- useChangeCommitBoundary,
35
- } from '@arcanewizards/sigil/frontend/context';
36
- import { v4 as uuidv4 } from 'uuid';
37
- import { cn } from '@arcanejs/toolkit-frontend/util';
38
- import { ARTNET_PORT, TimecodeMode } from '@arcanewizards/artnet/constants';
39
- import { SizeAwareDiv } from './core/size-aware-div';
40
- import { TooltipWrapper } from '@arcanewizards/sigil/frontend/tooltip';
41
- import {
42
- cssSigilColorUsageVariables,
43
- sigilColorUsage,
44
- } from '@arcanewizards/sigil/frontend/styling';
45
- import {
46
- augmentUpstreamTimecodeWithOutputMetadata,
47
- getTimecodeInstance,
48
- } from '../../../util';
49
- import { NoToolboxChildren } from './content';
50
-
51
- const DmxConnectionSettings: FC<SettingsProps<OutputDefinition>> = ({
52
- data,
53
- updateSettings,
54
- }) => {
55
- const { commitChanges } = useContext(ChangeCommitContext);
56
- const { getNetworkInterfaces } = useContext(NetworkContext);
57
- const [interfaces, setInterfaces] =
58
- useState<ToolboxRootGetNetworkInterfacesReturn | null>(null);
59
-
60
- const refreshInterfaces = useCallback(() => {
61
- setInterfaces(null);
62
- getNetworkInterfaces().then((ifs) => setInterfaces(ifs));
63
- }, [getNetworkInterfaces]);
64
-
65
- useEffect(() => {
66
- refreshInterfaces();
67
- }, [refreshInterfaces]);
68
-
69
- if (data.type !== 'artnet') {
70
- return null;
71
- }
72
-
73
- return (
74
- <>
75
- <ControlLabel>Target Type</ControlLabel>
76
- <ControlSelect
77
- value={data.target.type}
78
- options={[
79
- {
80
- value: 'interface',
81
- label: 'Broadcast',
82
- },
83
- {
84
- value: 'host',
85
- label: 'IP Address / Hostname',
86
- },
87
- ]}
88
- position="both"
89
- variant="large"
90
- onChange={(type) => {
91
- updateSettings((current) => ({
92
- ...current,
93
- target:
94
- type === 'interface'
95
- ? {
96
- type: 'interface',
97
- interface: '',
98
- }
99
- : {
100
- type: 'host',
101
- host: 'localhost',
102
- },
103
- }));
104
- }}
105
- />
106
- {data.target.type === 'interface' ? (
107
- <>
108
- <ControlLabel>Interface</ControlLabel>
109
- <ControlButton
110
- onClick={refreshInterfaces}
111
- title="Refresh Interfaces"
112
- position="first"
113
- variant="large"
114
- >
115
- <Icon icon="refresh" className="text-arcane-normal" />
116
- </ControlButton>
117
- <ControlSelect
118
- value={data.target.interface ?? null}
119
- options={
120
- !interfaces
121
- ? []
122
- : Object.values(interfaces).map((iface) => ({
123
- label: `${iface.name} (${iface.address})`,
124
- value: iface.name,
125
- }))
126
- }
127
- placeholder="No Interface Selected"
128
- onChange={(value) => {
129
- updateSettings((current) => ({
130
- ...current,
131
- target: {
132
- type: 'interface',
133
- interface: value ?? '',
134
- },
135
- }));
136
- }}
137
- position="second"
138
- variant="large"
139
- triggerClassName={cn('text-sigil-control')}
140
- />
141
- </>
142
- ) : (
143
- <>
144
- <ControlLabel>Hostname / IP</ControlLabel>
145
- <ControlInput
146
- position="both"
147
- type="string"
148
- value={data.target.host ?? ''}
149
- onChange={(value) => {
150
- updateSettings((current) => ({
151
- ...current,
152
- target: {
153
- type: 'host',
154
- host: value,
155
- },
156
- }));
157
- }}
158
- />
159
- </>
160
- )}
161
-
162
- <ControlLabel>Port</ControlLabel>
163
- <ControlInput
164
- position="both"
165
- type="string"
166
- value={data.target.port?.toString() ?? ''}
167
- placeholder={`Default (${ARTNET_PORT})`}
168
- onChange={(value, enterPressed) => {
169
- const port = value ? parseInt(value, 10) : undefined;
170
- if (port !== undefined && isNaN(port)) {
171
- return;
172
- }
173
- updateSettings((current) => ({
174
- ...current,
175
- target: {
176
- ...current.target,
177
- port,
178
- },
179
- }));
180
- if (enterPressed) {
181
- commitChanges();
182
- }
183
- }}
184
- />
185
- <ControlLabel>FPS</ControlLabel>
186
- <ControlSelect<TimecodeMode>
187
- position="both"
188
- variant="large"
189
- value={data.mode}
190
- options={(
191
- Object.entries(STRINGS.smtpeModeOptions) as [TimecodeMode, string][]
192
- ).map(([mode, label]) => ({
193
- label,
194
- value: mode,
195
- }))}
196
- onChange={(mode) => {
197
- updateSettings((current) => ({ ...current, mode }));
198
- }}
199
- />
200
- </>
201
- );
202
- };
203
-
204
- type OutputSettingsDialogProps = {
205
- target: DialogMode['target'];
206
- output: OutputDefinition['type'];
207
- close: () => void;
208
- };
209
-
210
- export const OutputSettingsDialog: FC<OutputSettingsDialogProps> = ({
211
- target,
212
- output,
213
- close,
214
- }) => {
215
- const { config, updateConfig } = useContext(ConfigContext);
216
- const [newData, setNewData] = useState<OutputConfig>({
217
- name: '',
218
- enabled: true,
219
- definition: {
220
- type: 'artnet',
221
- target: {
222
- type: 'host',
223
- host: 'localhost',
224
- },
225
- mode: 'SMPTE',
226
- },
227
- link: null,
228
- });
229
-
230
- const updateSettings: SettingsProps<OutputConfig>['updateSettings'] =
231
- useCallback(
232
- (change) => {
233
- if (target.type === 'add') {
234
- setNewData(change);
235
- } else {
236
- updateConfig((current) => {
237
- const existing = current.outputs?.[target.uuid];
238
- if (!existing) {
239
- return current;
240
- }
241
- return {
242
- ...current,
243
- outputs: {
244
- ...current.outputs,
245
- [target.uuid]: change(existing),
246
- },
247
- };
248
- });
249
- }
250
- },
251
- [target, updateConfig],
252
- );
253
-
254
- const updateDefinition = useCallback(
255
- (change: (current: OutputDefinition) => OutputDefinition) => {
256
- updateSettings((current) => ({
257
- ...current,
258
- definition: change(current.definition),
259
- }));
260
- },
261
- [updateSettings],
262
- );
263
-
264
- const addOutput = useCallback(() => {
265
- updateConfig((current) => {
266
- return {
267
- ...current,
268
- outputs: {
269
- ...current.outputs,
270
- [uuidv4()]: newData,
271
- },
272
- };
273
- });
274
- close();
275
- }, [newData, close, updateConfig]);
276
-
277
- const resolvedTarget =
278
- target.type === 'add' ? 'add' : config.outputs?.[target.uuid];
279
-
280
- const data = resolvedTarget === 'add' ? newData : resolvedTarget;
281
-
282
- const commitChanges = useCallback(() => {
283
- if (target.type === 'add') {
284
- addOutput();
285
- } else {
286
- close();
287
- }
288
- }, [target, addOutput, close]);
289
-
290
- const commitBoundary = useChangeCommitBoundary(data, commitChanges);
291
-
292
- if (!data) {
293
- return null;
294
- }
295
-
296
- return (
297
- <ChangeCommitContext.Provider value={commitBoundary}>
298
- <ControlDialog
299
- dialogClosed={close}
300
- title={
301
- target.type === 'add'
302
- ? STRINGS.outputs.addDialog(STRINGS.protocols[output].short)
303
- : STRINGS.outputs.editDialog(
304
- STRINGS.protocols[output].short,
305
- data.name || '',
306
- )
307
- }
308
- >
309
- <ControlLabel>Name</ControlLabel>
310
- <ControlInput
311
- position="both"
312
- type="string"
313
- value={data.name ?? ''}
314
- placeholder={`No name specified`}
315
- onChange={(name, enterPressed) => {
316
- if (enterPressed) {
317
- commitBoundary.commitChanges();
318
- }
319
- updateSettings((current) => ({
320
- ...current,
321
- name,
322
- }));
323
- }}
324
- />
325
- <ControlLabel>Color</ControlLabel>
326
- <ControlColorSelect
327
- position="both"
328
- color={data.color ?? ''}
329
- variant="standard"
330
- placeholder="Default"
331
- onChange={(color) => {
332
- updateSettings((current) => ({
333
- ...current,
334
- color,
335
- }));
336
- }}
337
- />
338
- {data.definition.type === 'artnet' ? (
339
- <DmxConnectionSettings
340
- data={data.definition}
341
- updateSettings={updateDefinition}
342
- />
343
- ) : null}
344
- <ControlLabel>Delay (ms)</ControlLabel>
345
- <ControlInput
346
- position="both"
347
- type="string"
348
- value={data.delayMs?.toString() ?? ''}
349
- placeholder={`Default (0ms)`}
350
- onChange={(value, enterPressed) => {
351
- const delayMs = value ? parseInt(value, 10) : undefined;
352
- if (delayMs !== undefined && isNaN(delayMs)) {
353
- return;
354
- }
355
- updateSettings((current) => ({
356
- ...current,
357
- delayMs,
358
- }));
359
- if (enterPressed) {
360
- commitChanges();
361
- }
362
- }}
363
- />
364
- {resolvedTarget === 'add' ? (
365
- <ControlDialogButtons>
366
- <ControlButton onClick={close} variant="large">
367
- Cancel
368
- </ControlButton>
369
- <ControlButton onClick={addOutput} variant="large">
370
- Add Output
371
- </ControlButton>
372
- </ControlDialogButtons>
373
- ) : (
374
- <ControlDialogButtons>
375
- <ControlButton onClick={close} variant="large">
376
- Close
377
- </ControlButton>
378
- </ControlDialogButtons>
379
- )}
380
- </ControlDialog>
381
- </ChangeCommitContext.Provider>
382
- );
383
- };
384
-
385
- type OutputDisplayProps = {
386
- uuid: string;
387
- config: OutputConfig;
388
- setDialogMode: (mode: DialogMode | null) => void;
389
- assignToOutput: string | null;
390
- setAssignToOutput: Dispatch<SetStateAction<string | null>>;
391
- };
392
-
393
- const OutputDisplay: FC<OutputDisplayProps> = ({
394
- uuid,
395
- config,
396
- setDialogMode,
397
- assignToOutput,
398
- setAssignToOutput,
399
- }) => {
400
- const applicationState = useApplicationState();
401
- const { updateConfig } = useContext(ConfigContext);
402
- const clearLink = useCallback(() => {
403
- updateConfig((current) => {
404
- const currentOutput = current.outputs?.[uuid];
405
- if (!currentOutput) {
406
- return current;
407
- }
408
- return {
409
- ...current,
410
- outputs: {
411
- ...current.outputs,
412
- [uuid]: {
413
- ...currentOutput,
414
- link: null,
415
- },
416
- },
417
- };
418
- });
419
- setAssignToOutput(null);
420
- }, [updateConfig, uuid, setAssignToOutput]);
421
-
422
- const linkCallback = useCallback(
423
- () => setAssignToOutput((current) => (current === uuid ? null : uuid)),
424
- [uuid, setAssignToOutput],
425
- );
426
-
427
- const timecode: TimecodeInstance = useMemo(() => {
428
- const tc =
429
- config.link && getTimecodeInstance(applicationState, config.link);
430
- return augmentUpstreamTimecodeWithOutputMetadata(tc, config);
431
- }, [applicationState, config]);
432
-
433
- const toggleEnabled = useCallback(() => {
434
- updateConfig((current) => {
435
- const existing = current.outputs?.[uuid];
436
- if (!existing) {
437
- return current;
438
- }
439
- return {
440
- ...current,
441
- outputs: {
442
- ...current.outputs,
443
- [uuid]: {
444
- ...existing,
445
- enabled: !existing.enabled,
446
- },
447
- },
448
- };
449
- });
450
- }, [uuid, updateConfig]);
451
-
452
- return (
453
- <div
454
- className="relative flex flex-col"
455
- style={
456
- config.color &&
457
- cssSigilColorUsageVariables(
458
- 'timecode-usage',
459
- sigilColorUsage(config.color),
460
- )
461
- }
462
- >
463
- <TimecodeTreeDisplay
464
- id={['output', uuid]}
465
- config={{ delayMs: config.delayMs ?? null }}
466
- assignToOutput={null}
467
- type={STRINGS.protocols[config.definition.type].short}
468
- name={config.name ? [config.name] : []}
469
- color={config.color}
470
- timecode={config.enabled ? timecode : null}
471
- namePlaceholder={`Unnamed Output`}
472
- buttons={
473
- <>
474
- <ControlButton
475
- variant="large"
476
- title={config.enabled ? 'Stop Input' : 'Start Input'}
477
- onClick={toggleEnabled}
478
- icon={config.enabled ? 'stop' : 'play_arrow'}
479
- />
480
- <ControlButton
481
- variant="large"
482
- title="Link Output"
483
- active={assignToOutput === uuid}
484
- onClick={linkCallback}
485
- icon={config.link ? 'link' : 'link_off'}
486
- />
487
- <ControlButton
488
- variant="large"
489
- title="Edit Output"
490
- onClick={() =>
491
- setDialogMode({
492
- section: { type: 'outputs', output: config.definition.type },
493
- target: { type: 'edit', uuid },
494
- })
495
- }
496
- icon="edit"
497
- />
498
- </>
499
- }
500
- />
501
- {assignToOutput === uuid && config.link && (
502
- <SizeAwareDiv className="absolute inset-0" onClick={clearLink}>
503
- <TooltipWrapper tooltip="Unlink from Input / Generator">
504
- <div
505
- className="
506
- flex size-full cursor-pointer flex-col items-center
507
- justify-center gap-0.5 bg-timecode-backdrop
508
- text-timecode-usage-text
509
- hover:bg-timecode-backdrop-hover
510
- "
511
- >
512
- <Icon icon="link_off" className="text-block-icon" />
513
- <p>Press ESC to cancel</p>
514
- </div>
515
- </TooltipWrapper>
516
- </SizeAwareDiv>
517
- )}
518
- </div>
519
- );
520
- };
521
-
522
- export type OutputSectionProps = {
523
- setDialogMode: (mode: DialogMode | null) => void;
524
- assignToOutput: string | null;
525
- setAssignToOutput: Dispatch<SetStateAction<string | null>>;
526
- };
527
-
528
- export const OutputsSection: FC<OutputSectionProps> = ({
529
- setDialogMode,
530
- assignToOutput,
531
- setAssignToOutput,
532
- }) => {
533
- const { config } = useContext(ConfigContext);
534
- return (
535
- <PrimaryToolboxSection
536
- title={STRINGS.outputs.title}
537
- buttons={
538
- <>
539
- {(['artnet'] as const).map((type) => (
540
- <ControlButton
541
- key={type}
542
- onClick={() =>
543
- setDialogMode({
544
- section: { type: 'outputs', output: type },
545
- target: { type: 'add' },
546
- })
547
- }
548
- variant="toolbar"
549
- icon="add"
550
- >
551
- {STRINGS.outputs.addButton(STRINGS.protocols[type].long)}
552
- </ControlButton>
553
- ))}
554
- </>
555
- }
556
- >
557
- {Object.entries(config.outputs ?? {}).length === 0 ? (
558
- <NoToolboxChildren text={STRINGS.outputs.noChildren} />
559
- ) : (
560
- <div
561
- className="
562
- grid grow grid-cols-1 gap-px
563
- min-[600px]:grid-cols-2
564
- min-[900px]:grid-cols-3
565
- "
566
- >
567
- {Object.entries(config.outputs).map(([uuid, output]) => (
568
- <OutputDisplay
569
- key={uuid}
570
- uuid={uuid}
571
- config={output}
572
- setDialogMode={setDialogMode}
573
- assignToOutput={assignToOutput}
574
- setAssignToOutput={setAssignToOutput}
575
- />
576
- ))}
577
- </div>
578
- )}
579
- </PrimaryToolboxSection>
580
- );
581
- };
@@ -1,25 +0,0 @@
1
- import {
2
- BrowserPreferencesDefinition,
3
- createBrowserPreferencesHook,
4
- } from '@arcanewizards/sigil/frontend/preferences';
5
- import { SIGIL_COLOR } from '@arcanewizards/sigil/frontend/styling';
6
- import z from 'zod';
7
-
8
- const BROWSER_PREFERENCES_KEY = 'timecode-toolbox-preferences';
9
-
10
- const BROWSER_PREFERENCES = z.object({
11
- color: SIGIL_COLOR,
12
- });
13
-
14
- type BrowserPreferences = z.infer<typeof BROWSER_PREFERENCES>;
15
-
16
- const TOOLBOX_PREFERENCES: BrowserPreferencesDefinition<BrowserPreferences> = {
17
- key: BROWSER_PREFERENCES_KEY,
18
- zodType: BROWSER_PREFERENCES,
19
- defaultValue: {
20
- color: 'orange',
21
- },
22
- };
23
-
24
- export const useBrowserPreferences =
25
- createBrowserPreferencesHook(TOOLBOX_PREFERENCES);