@arcanewizards/timecode-toolbox 0.1.0 → 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 (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/frontend/index.js +865 -272
  3. package/dist/components/frontend/index.mjs +855 -262
  4. package/dist/entrypoint.css +163 -53
  5. package/dist/entrypoint.js +1474 -493
  6. package/dist/entrypoint.js.map +4 -4
  7. package/dist/frontend.js +1474 -493
  8. package/dist/frontend.js.map +4 -4
  9. package/dist/index.d.mts +3 -1
  10. package/dist/index.d.ts +3 -1
  11. package/dist/index.js +308 -37
  12. package/dist/index.mjs +329 -49
  13. package/dist/start.d.mts +1 -2
  14. package/dist/start.d.ts +1 -2
  15. package/dist/start.js +311 -38
  16. package/dist/start.mjs +332 -50
  17. package/package.json +12 -6
  18. package/.turbo/turbo-build.log +0 -58
  19. package/.turbo/turbo-lint.log +0 -4
  20. package/CHANGELOG.md +0 -40
  21. package/eslint.config.mjs +0 -49
  22. package/src/app.tsx +0 -147
  23. package/src/components/backend/index.ts +0 -6
  24. package/src/components/backend/toolbox-root.ts +0 -119
  25. package/src/components/frontend/constants.ts +0 -81
  26. package/src/components/frontend/entrypoint.ts +0 -12
  27. package/src/components/frontend/frontend.css +0 -108
  28. package/src/components/frontend/index.tsx +0 -46
  29. package/src/components/frontend/toolbox/content.tsx +0 -45
  30. package/src/components/frontend/toolbox/context.tsx +0 -63
  31. package/src/components/frontend/toolbox/core/size-aware-div.tsx +0 -51
  32. package/src/components/frontend/toolbox/core/timecode-display.tsx +0 -592
  33. package/src/components/frontend/toolbox/generators.tsx +0 -318
  34. package/src/components/frontend/toolbox/inputs.tsx +0 -484
  35. package/src/components/frontend/toolbox/outputs.tsx +0 -581
  36. package/src/components/frontend/toolbox/preferences.ts +0 -25
  37. package/src/components/frontend/toolbox/root.tsx +0 -335
  38. package/src/components/frontend/toolbox/settings.tsx +0 -54
  39. package/src/components/frontend/toolbox/types.ts +0 -28
  40. package/src/components/frontend/toolbox/util.tsx +0 -61
  41. package/src/components/proto.ts +0 -420
  42. package/src/config.ts +0 -7
  43. package/src/generators/clock.tsx +0 -206
  44. package/src/generators/index.tsx +0 -15
  45. package/src/index.ts +0 -38
  46. package/src/inputs/artnet.tsx +0 -305
  47. package/src/inputs/index.tsx +0 -13
  48. package/src/inputs/tcnet.tsx +0 -272
  49. package/src/outputs/artnet.tsx +0 -170
  50. package/src/outputs/index.tsx +0 -11
  51. package/src/start.ts +0 -47
  52. package/src/tree.ts +0 -133
  53. package/src/types.ts +0 -12
  54. package/src/urls.ts +0 -49
  55. package/src/util.ts +0 -82
  56. package/tailwind.config.cjs +0 -7
  57. package/tsconfig.json +0 -10
  58. 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
- };