@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,484 +0,0 @@
1
- import { FC, useCallback, useContext, useEffect, useState } from 'react';
2
- import { STRINGS } from '../constants';
3
- import { PrimaryToolboxSection } from './util';
4
- import { ConfigContext, NetworkContext, useApplicationState } from './context';
5
- import {
6
- ControlButton,
7
- ControlColorSelect,
8
- ControlDialog,
9
- ControlDialogButtons,
10
- ControlInput,
11
- ControlLabel,
12
- ControlSelect,
13
- } from '@arcanewizards/sigil/frontend/controls';
14
- import { AssignToOutputCallback, DialogMode, SettingsProps } from './types';
15
- import {
16
- InputConfig,
17
- InputDefinition,
18
- ToolboxRootGetNetworkInterfacesReturn,
19
- } from '../../proto';
20
- import { Icon } from '@arcanejs/toolkit-frontend/components/core';
21
- import { ARTNET_PORT } from '@arcanewizards/artnet/constants';
22
- import { v4 as uuidv4 } from 'uuid';
23
- import { cn } from '@arcanejs/toolkit-frontend/util';
24
- import {
25
- ChangeCommitContext,
26
- useChangeCommitBoundary,
27
- } from '@arcanewizards/sigil/frontend/context';
28
- import { TimecodeTreeDisplay } from './core/timecode-display';
29
- import { NoToolboxChildren } from './content';
30
-
31
- const DmxConnectionSettings: FC<SettingsProps<InputDefinition>> = ({
32
- data,
33
- updateSettings,
34
- }) => {
35
- const { commitChanges } = useContext(ChangeCommitContext);
36
- const { getNetworkInterfaces } = useContext(NetworkContext);
37
- const [interfaces, setInterfaces] =
38
- useState<ToolboxRootGetNetworkInterfacesReturn | null>(null);
39
-
40
- const refreshInterfaces = useCallback(() => {
41
- setInterfaces(null);
42
- getNetworkInterfaces().then((ifs) => setInterfaces(ifs));
43
- }, [getNetworkInterfaces]);
44
-
45
- useEffect(() => {
46
- refreshInterfaces();
47
- }, [refreshInterfaces]);
48
-
49
- if (data.type !== 'artnet') {
50
- return null;
51
- }
52
-
53
- return (
54
- <>
55
- <ControlLabel>Interface</ControlLabel>
56
- <ControlButton
57
- onClick={refreshInterfaces}
58
- title="Refresh Interfaces"
59
- position="first"
60
- variant="large"
61
- >
62
- <Icon icon="refresh" className="text-arcane-normal" />
63
- </ControlButton>
64
- <ControlSelect
65
- value={data.iface ?? null}
66
- options={
67
- !interfaces
68
- ? []
69
- : Object.values(interfaces).map((iface) => ({
70
- label: `${iface.name} (${iface.address})`,
71
- value: iface.name,
72
- }))
73
- }
74
- placeholder="No Interface Selected"
75
- onChange={(value) => {
76
- updateSettings((current) => ({
77
- ...current,
78
- iface: value ?? '',
79
- }));
80
- }}
81
- position="second"
82
- variant="large"
83
- triggerClassName={cn('text-sigil-control')}
84
- />
85
-
86
- <ControlLabel>Port</ControlLabel>
87
- <ControlInput
88
- position="both"
89
- type="string"
90
- value={data.port?.toString() ?? ''}
91
- placeholder={`Default (${ARTNET_PORT})`}
92
- onChange={(value, enterPressed) => {
93
- const port = value ? parseInt(value, 10) : undefined;
94
- if (port !== undefined && isNaN(port)) {
95
- return;
96
- }
97
- updateSettings((current) => ({
98
- ...current,
99
- port,
100
- }));
101
- if (enterPressed) {
102
- commitChanges();
103
- }
104
- }}
105
- />
106
- </>
107
- );
108
- };
109
-
110
- const TCNetConnectionSettings: FC<SettingsProps<InputDefinition>> = ({
111
- data,
112
- updateSettings,
113
- }) => {
114
- const { getNetworkInterfaces } = useContext(NetworkContext);
115
- const [interfaces, setInterfaces] =
116
- useState<ToolboxRootGetNetworkInterfacesReturn | null>(null);
117
-
118
- const refreshInterfaces = useCallback(() => {
119
- setInterfaces(null);
120
- getNetworkInterfaces().then((ifs) => setInterfaces(ifs));
121
- }, [getNetworkInterfaces]);
122
-
123
- useEffect(() => {
124
- refreshInterfaces();
125
- }, [refreshInterfaces]);
126
-
127
- if (data.type !== 'tcnet') {
128
- return null;
129
- }
130
-
131
- return (
132
- <>
133
- <ControlLabel>Interface</ControlLabel>
134
- <ControlButton
135
- onClick={refreshInterfaces}
136
- title="Refresh Interfaces"
137
- position="first"
138
- variant="large"
139
- >
140
- <Icon icon="refresh" className="text-arcane-normal" />
141
- </ControlButton>
142
- <ControlSelect
143
- value={data.iface ?? null}
144
- options={
145
- !interfaces
146
- ? []
147
- : Object.values(interfaces).map((iface) => ({
148
- label: `${iface.name} (${iface.address})`,
149
- value: iface.name,
150
- }))
151
- }
152
- placeholder="No Interface Selected"
153
- onChange={(value) => {
154
- updateSettings((current) => ({
155
- ...current,
156
- iface: value ?? '',
157
- }));
158
- }}
159
- position="second"
160
- variant="large"
161
- triggerClassName={cn('text-sigil-control')}
162
- />
163
- </>
164
- );
165
- };
166
-
167
- type InputSettingsDialogProps = {
168
- target: DialogMode['target'];
169
- input: InputDefinition['type'];
170
- close: () => void;
171
- };
172
-
173
- export const InputSettingsDialog: FC<InputSettingsDialogProps> = ({
174
- target,
175
- input,
176
- close,
177
- }) => {
178
- const { config, updateConfig } = useContext(ConfigContext);
179
- const [newData, setNewData] = useState<InputConfig>({
180
- name: '',
181
- enabled: true,
182
- definition:
183
- input === 'artnet'
184
- ? {
185
- type: 'artnet',
186
- iface: '',
187
- port: undefined,
188
- }
189
- : {
190
- type: 'tcnet',
191
- iface: '',
192
- },
193
- });
194
-
195
- const updateSettings: SettingsProps<InputConfig>['updateSettings'] =
196
- useCallback(
197
- (change) => {
198
- if (target.type === 'add') {
199
- setNewData(change);
200
- } else {
201
- updateConfig((current) => {
202
- const existing = current.inputs?.[target.uuid];
203
- if (!existing) {
204
- return current;
205
- }
206
- return {
207
- ...current,
208
- inputs: {
209
- ...current.inputs,
210
- [target.uuid]: change(existing),
211
- },
212
- };
213
- });
214
- }
215
- },
216
- [target, updateConfig],
217
- );
218
-
219
- const updateDefinition = useCallback(
220
- (change: (current: InputDefinition) => InputDefinition) => {
221
- updateSettings((current) => ({
222
- ...current,
223
- definition: change(current.definition),
224
- }));
225
- },
226
- [updateSettings],
227
- );
228
-
229
- const addInput = useCallback(() => {
230
- updateConfig((current) => {
231
- return {
232
- ...current,
233
- inputs: {
234
- ...current.inputs,
235
- [uuidv4()]: newData,
236
- },
237
- };
238
- });
239
- close();
240
- }, [newData, close, updateConfig]);
241
-
242
- const resolvedTarget =
243
- target.type === 'add' ? 'add' : config.inputs?.[target.uuid];
244
-
245
- const data = resolvedTarget === 'add' ? newData : resolvedTarget;
246
-
247
- const commitChanges = useCallback(() => {
248
- if (target.type === 'add') {
249
- addInput();
250
- } else {
251
- close();
252
- }
253
- }, [target, addInput, close]);
254
-
255
- const commitBoundary = useChangeCommitBoundary(data, commitChanges);
256
-
257
- if (!data) {
258
- return null;
259
- }
260
-
261
- return (
262
- <ChangeCommitContext.Provider value={commitBoundary}>
263
- <ControlDialog
264
- dialogClosed={close}
265
- title={
266
- target.type === 'add'
267
- ? STRINGS.inputs.addDialog(STRINGS.protocols[input].short)
268
- : STRINGS.inputs.editDialog(
269
- STRINGS.protocols[input].short,
270
- data.name || '',
271
- )
272
- }
273
- >
274
- <ControlLabel>Name</ControlLabel>
275
- <ControlInput
276
- position="both"
277
- type="string"
278
- value={data.name ?? ''}
279
- placeholder={`No name specified`}
280
- onChange={(name, enterPressed) => {
281
- if (enterPressed) {
282
- commitBoundary.commitChanges();
283
- }
284
- updateSettings((current) => ({
285
- ...current,
286
- name,
287
- }));
288
- }}
289
- />
290
- <ControlLabel>Color</ControlLabel>
291
- <ControlColorSelect
292
- position="both"
293
- color={data.color ?? ''}
294
- variant="standard"
295
- placeholder="Default"
296
- onChange={(color) => {
297
- updateSettings((current) => ({
298
- ...current,
299
- color,
300
- }));
301
- }}
302
- />
303
- {data.definition.type === 'artnet' ? (
304
- <DmxConnectionSettings
305
- data={data.definition}
306
- updateSettings={updateDefinition}
307
- />
308
- ) : data.definition.type === 'tcnet' ? (
309
- <TCNetConnectionSettings
310
- data={data.definition}
311
- updateSettings={updateDefinition}
312
- />
313
- ) : null}
314
- <ControlLabel>Delay (ms)</ControlLabel>
315
- <ControlInput
316
- position="both"
317
- type="string"
318
- value={data.delayMs?.toString() ?? ''}
319
- placeholder={`Default (0ms)`}
320
- onChange={(value, enterPressed) => {
321
- const delayMs = value ? parseInt(value, 10) : undefined;
322
- if (delayMs !== undefined && isNaN(delayMs)) {
323
- return;
324
- }
325
- updateSettings((current) => ({
326
- ...current,
327
- delayMs,
328
- }));
329
- if (enterPressed) {
330
- commitChanges();
331
- }
332
- }}
333
- />
334
- {resolvedTarget === 'add' ? (
335
- <ControlDialogButtons>
336
- <ControlButton onClick={close} variant="large">
337
- Cancel
338
- </ControlButton>
339
- <ControlButton onClick={addInput} variant="large">
340
- Add Input
341
- </ControlButton>
342
- </ControlDialogButtons>
343
- ) : (
344
- <ControlDialogButtons>
345
- <ControlButton onClick={close} variant="large">
346
- Close
347
- </ControlButton>
348
- </ControlDialogButtons>
349
- )}
350
- </ControlDialog>
351
- </ChangeCommitContext.Provider>
352
- );
353
- };
354
-
355
- type InputDisplayProps = {
356
- uuid: string;
357
- config: InputConfig;
358
- setDialogMode: (mode: DialogMode | null) => void;
359
- assignToOutput: AssignToOutputCallback;
360
- };
361
-
362
- export const InputDisplay: FC<InputDisplayProps> = ({
363
- uuid,
364
- config,
365
- setDialogMode,
366
- assignToOutput,
367
- }) => {
368
- const { updateConfig } = useContext(ConfigContext);
369
-
370
- const { inputs } = useApplicationState();
371
- const state = inputs[uuid];
372
-
373
- const toggleEnabled = useCallback(() => {
374
- updateConfig((current) => {
375
- const existing = current.inputs?.[uuid];
376
- if (!existing) {
377
- return current;
378
- }
379
- return {
380
- ...current,
381
- inputs: {
382
- ...current.inputs,
383
- [uuid]: {
384
- ...existing,
385
- enabled: !existing.enabled,
386
- },
387
- },
388
- };
389
- });
390
- }, [uuid, updateConfig]);
391
-
392
- return (
393
- <TimecodeTreeDisplay
394
- id={['input', uuid]}
395
- config={{ delayMs: config.delayMs ?? null }}
396
- type={STRINGS.protocols[config.definition.type].short}
397
- name={config.name ? [config.name] : []}
398
- color={config.color}
399
- timecode={state?.timecode ?? null}
400
- namePlaceholder={`Unnamed Input`}
401
- buttons={
402
- <>
403
- <ControlButton
404
- variant="large"
405
- title={config.enabled ? 'Stop Input' : 'Start Input'}
406
- onClick={toggleEnabled}
407
- icon={config.enabled ? 'stop' : 'play_arrow'}
408
- />
409
- <ControlButton
410
- variant="large"
411
- title="Edit Input"
412
- onClick={() =>
413
- setDialogMode({
414
- section: { type: 'inputs', input: config.definition.type },
415
- target: { type: 'edit', uuid },
416
- })
417
- }
418
- icon="edit"
419
- />
420
- </>
421
- }
422
- assignToOutput={assignToOutput}
423
- />
424
- );
425
- };
426
-
427
- export type InputSectionProps = {
428
- setDialogMode: (mode: DialogMode | null) => void;
429
- assignToOutput: AssignToOutputCallback;
430
- };
431
-
432
- export const InputsSection: FC<InputSectionProps> = ({
433
- setDialogMode,
434
- assignToOutput,
435
- }) => {
436
- const { config } = useContext(ConfigContext);
437
-
438
- return (
439
- <PrimaryToolboxSection
440
- title={STRINGS.inputs.title}
441
- buttons={
442
- <>
443
- {(['artnet', 'tcnet'] as const).map((type) => (
444
- <ControlButton
445
- key={type}
446
- onClick={() =>
447
- setDialogMode({
448
- section: { type: 'inputs', input: type },
449
- target: { type: 'add' },
450
- })
451
- }
452
- variant="toolbar"
453
- icon="add"
454
- >
455
- {STRINGS.inputs.addButton(STRINGS.protocols[type].long)}
456
- </ControlButton>
457
- ))}
458
- </>
459
- }
460
- >
461
- {Object.entries(config.inputs ?? {}).length === 0 ? (
462
- <NoToolboxChildren text={STRINGS.inputs.noChildren} />
463
- ) : (
464
- <div
465
- className="
466
- grid grow grid-cols-1 gap-px
467
- min-[600px]:grid-cols-2
468
- min-[900px]:grid-cols-3
469
- "
470
- >
471
- {Object.entries(config.inputs).map(([uuid, input]) => (
472
- <InputDisplay
473
- key={uuid}
474
- uuid={uuid}
475
- config={input}
476
- setDialogMode={setDialogMode}
477
- assignToOutput={assignToOutput}
478
- />
479
- ))}
480
- </div>
481
- )}
482
- </PrimaryToolboxSection>
483
- );
484
- };