@arcanewizards/timecode-toolbox 0.1.3 → 0.1.5

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.
@@ -13970,7 +13970,7 @@ function cnd(condition, truthyClassName, falseyClassName) {
13970
13970
  return condition ? truthyClassName : falseyClassName;
13971
13971
  }
13972
13972
 
13973
- // ../../packages/sigil/dist/chunk-E3VUC3Q4.js
13973
+ // ../../packages/sigil/dist/chunk-LDUYBGRY.js
13974
13974
  import {
13975
13975
  createContext as createContext6,
13976
13976
  forwardRef as forwardRef42,
@@ -14014,7 +14014,7 @@ var TRANSPARENCY_SVG = `
14014
14014
  `;
14015
14015
  var TRANSPARENCY_SVG_URI = `data:image/svg+xml,${encodeURIComponent(TRANSPARENCY_SVG)}`;
14016
14016
 
14017
- // ../../packages/sigil/dist/chunk-E3VUC3Q4.js
14017
+ // ../../packages/sigil/dist/chunk-LDUYBGRY.js
14018
14018
  import {
14019
14019
  forwardRef as forwardRef15
14020
14020
  } from "react";
@@ -14087,7 +14087,8 @@ var clsControlButton = ({
14087
14087
  touching,
14088
14088
  position,
14089
14089
  className,
14090
- primary
14090
+ primary,
14091
+ destructive
14091
14092
  }) => cn(
14092
14093
  `sigil-control-button`,
14093
14094
  cnd(variant === "border", `sigil-control-button-variant-border`),
@@ -14100,6 +14101,7 @@ var clsControlButton = ({
14100
14101
  cnd(active, `sigil-control-button-active`),
14101
14102
  cnd(touching && active, `sigil-control-button-active-touching`),
14102
14103
  cnd(primary, `sigil-control-button-primary`),
14104
+ cnd(destructive, `sigil-control-button-destructive`),
14103
14105
  clsControlPosition(position),
14104
14106
  className
14105
14107
  );
@@ -14117,6 +14119,7 @@ var ControlButtonFrame = forwardRef15(
14117
14119
  tooltipSide,
14118
14120
  position,
14119
14121
  primary,
14122
+ destructive,
14120
14123
  ...props
14121
14124
  }, ref) => {
14122
14125
  const btn = /* @__PURE__ */ jsx17(
@@ -14132,6 +14135,7 @@ var ControlButtonFrame = forwardRef15(
14132
14135
  touching,
14133
14136
  position,
14134
14137
  primary,
14138
+ destructive,
14135
14139
  className
14136
14140
  }),
14137
14141
  children: /* @__PURE__ */ jsxs4("span", { children: [
@@ -14241,7 +14245,7 @@ ControlParagraph.displayName = "ControlParagraph";
14241
14245
  var ControlLabel = forwardRef22(
14242
14246
  ({ className, disabled, nonMicro, position = "label", subgrid, ...props }, ref) => {
14243
14247
  return /* @__PURE__ */ jsx23(
14244
- "div",
14248
+ "label",
14245
14249
  {
14246
14250
  ...props,
14247
14251
  ref,
@@ -14290,10 +14294,17 @@ var InputSpanningTitle = forwardRef22(({ className, position = "row", ...props }
14290
14294
  ));
14291
14295
  InputSpanningTitle.displayName = "InputSpanningTitle";
14292
14296
  var ControlDialogButtons = forwardRef32(({ children, className, ...props }, ref) => /* @__PURE__ */ jsx32(
14293
- ControlButtonGroup,
14297
+ "div",
14294
14298
  {
14295
14299
  ref,
14296
- className: cn("control-grid-pos-row", className),
14300
+ className: cn(
14301
+ `
14302
+ flex items-stretch gap-1
14303
+ [&>button]:grow
14304
+ `,
14305
+ clsControlPosition("row"),
14306
+ className
14307
+ ),
14297
14308
  ...props,
14298
14309
  children
14299
14310
  }
@@ -14476,19 +14487,9 @@ var ControlSelect = ({
14476
14487
  }
14477
14488
  );
14478
14489
  };
14479
- var COLOR_OPTIONS = {
14480
- red: { label: "Red", value: "red" },
14481
- blue: { label: "Blue", value: "blue" },
14482
- teal: { label: "Teal", value: "teal" },
14483
- green: { label: "Green", value: "green" },
14484
- yellow: { label: "Yellow", value: "yellow" },
14485
- purple: { label: "Purple", value: "purple" },
14486
- orange: { label: "Orange", value: "orange" },
14487
- brown: { label: "Brown", value: "brown" },
14488
- gray: { label: "Gray", value: "gray" }
14489
- };
14490
- var ControlColorSelect = ({
14491
- color,
14490
+ var ControlColoredSelect = ({
14491
+ options,
14492
+ value,
14492
14493
  onChange,
14493
14494
  variant,
14494
14495
  position,
@@ -14496,13 +14497,13 @@ var ControlColorSelect = ({
14496
14497
  placeholder,
14497
14498
  ...props
14498
14499
  }) => {
14499
- const options = Object.values(COLOR_OPTIONS);
14500
- const selectedColor = sigilColorUsage(color || "gray");
14500
+ const selectedOption = options.find((option) => option.value === value);
14501
+ const selectedColor = sigilColorUsage(selectedOption?.color ?? "gray");
14501
14502
  return /* @__PURE__ */ jsx52(
14502
14503
  ControlSelect,
14503
14504
  {
14504
14505
  options,
14505
- value: color,
14506
+ value,
14506
14507
  onChange,
14507
14508
  triggerButton: (option) => /* @__PURE__ */ jsx52(
14508
14509
  dist_exports2.Trigger,
@@ -14556,7 +14557,7 @@ var ControlColorSelect = ({
14556
14557
  {
14557
14558
  className: "",
14558
14559
  style: {
14559
- color: `var(--sigil-usage-${option.value}-foreground)`
14560
+ color: `var(--sigil-usage-${option.color}-foreground)`
14560
14561
  },
14561
14562
  children: option.label
14562
14563
  }
@@ -14568,6 +14569,31 @@ var ControlColorSelect = ({
14568
14569
  }
14569
14570
  );
14570
14571
  };
14572
+ var COLOR_OPTIONS = {
14573
+ red: { label: "Red", value: "red", color: "red" },
14574
+ blue: { label: "Blue", value: "blue", color: "blue" },
14575
+ teal: { label: "Teal", value: "teal", color: "teal" },
14576
+ green: { label: "Green", value: "green", color: "green" },
14577
+ yellow: { label: "Yellow", value: "yellow", color: "yellow" },
14578
+ purple: { label: "Purple", value: "purple", color: "purple" },
14579
+ orange: { label: "Orange", value: "orange", color: "orange" },
14580
+ brown: { label: "Brown", value: "brown", color: "brown" },
14581
+ gray: { label: "Gray", value: "gray", color: "gray" }
14582
+ };
14583
+ var COLOR_OPTIONS_ARRAY = Object.values(COLOR_OPTIONS);
14584
+ var ControlColorSelect = ({
14585
+ color,
14586
+ ...props
14587
+ }) => {
14588
+ return /* @__PURE__ */ jsx52(
14589
+ ControlColoredSelect,
14590
+ {
14591
+ options: COLOR_OPTIONS_ARRAY,
14592
+ value: color,
14593
+ ...props
14594
+ }
14595
+ );
14596
+ };
14571
14597
  var isDarkDialog = (variant) => variant === "dark" || variant === "dark-compact";
14572
14598
  var DialogContext = createContext6({
14573
14599
  createDialog: () => {
@@ -15603,6 +15629,7 @@ var createBrowserMediaSession = () => {
15603
15629
  };
15604
15630
  var createDefaultBrowserContext = (browser) => {
15605
15631
  const defaults = {
15632
+ appListenerChangesHandledExternally: false,
15606
15633
  openExternalLink: (url) => {
15607
15634
  window.open(url, "_blank", "noopener,noreferrer");
15608
15635
  },
@@ -15824,6 +15851,30 @@ var startSigilFrontend = ({
15824
15851
  return resolvedBrowser;
15825
15852
  };
15826
15853
 
15854
+ // ../../packages/sigil/dist/shared/config.js
15855
+ var APP_LISTENER_CONFIG = zod_default.object({
15856
+ port: zod_default.union([
15857
+ zod_default.number().int().min(1).max(65535),
15858
+ zod_default.object({
15859
+ from: zod_default.number().int().min(1).max(65535),
15860
+ to: zod_default.number().int().min(1).max(65535)
15861
+ }).refine((data) => data.to >= data.from, {
15862
+ message: '"to" must be greater than or equal to "from"'
15863
+ })
15864
+ ]),
15865
+ interface: zod_default.string().optional()
15866
+ });
15867
+ var ALL_APP_LISTENER_CONFIG = zod_default.record(
15868
+ zod_default.string(),
15869
+ APP_LISTENER_CONFIG
15870
+ );
15871
+ var portString = (port) => {
15872
+ if (typeof port === "number") {
15873
+ return port.toString();
15874
+ }
15875
+ return `${port.from}-${port.to}`;
15876
+ };
15877
+
15827
15878
  // src/components/proto.ts
15828
15879
  var NET_UTILS_GENERAL_TARGET_DEFINITION = zod_default.union([
15829
15880
  zod_default.object({
@@ -15901,6 +15952,7 @@ var OUTPUT_CONFIG = zod_default.object({
15901
15952
  link: INPUT_OR_GENERATOR_INSTANCE_ID.nullable()
15902
15953
  });
15903
15954
  var TOOLBOX_CONFIG = zod_default.object({
15955
+ appListener: APP_LISTENER_CONFIG.partial().optional(),
15904
15956
  inputs: zod_default.record(zod_default.string(), INPUT_CONFIG),
15905
15957
  generators: zod_default.record(zod_default.string(), GENERATOR_CONFIG),
15906
15958
  outputs: zod_default.record(zod_default.string(), OUTPUT_CONFIG),
@@ -15917,11 +15969,11 @@ var isTimecodeToolboxComponent = (component) => component.namespace === NAMESPAC
15917
15969
 
15918
15970
  // src/components/frontend/toolbox/root.tsx
15919
15971
  import {
15920
- useCallback as useCallback20,
15921
- useContext as useContext16,
15922
- useEffect as useEffect31,
15923
- useMemo as useMemo15,
15924
- useState as useState31
15972
+ useCallback as useCallback24,
15973
+ useContext as useContext17,
15974
+ useEffect as useEffect34,
15975
+ useMemo as useMemo19,
15976
+ useState as useState36
15925
15977
  } from "react";
15926
15978
 
15927
15979
  // ../../packages/artnet/dist/chunk-J2HDMITA.js
@@ -15964,10 +16016,16 @@ var STRINGS = {
15964
16016
  },
15965
16017
  inputs: {
15966
16018
  title: "INPUTS",
16019
+ unnamed: "Unnamed Input",
16020
+ enable: "Enable Input",
16021
+ disable: "Disable Input",
16022
+ edit: "Edit Input",
15967
16023
  noChildren: "No inputs yet. Please add one using the buttons below.",
15968
16024
  addButton: (protocol) => `Add ${protocol}`,
15969
16025
  addDialog: (protocol) => `Add ${protocol} Input`,
15970
- editDialog: (protocol, name) => `Edit ${protocol} Input ${name}`
16026
+ editDialog: (protocol, name) => `Edit ${protocol} Input ${name}`,
16027
+ deleteDialog: `Delete input?`,
16028
+ deleteDialogDetails: `Are you sure you want to delete this input? This action cannot be undone.`
15971
16029
  },
15972
16030
  smtpeModes: {
15973
16031
  SMPTE: `SMPTE ${TIMECODE_FPS.SMPTE}FPS`,
@@ -15985,22 +16043,49 @@ var STRINGS = {
15985
16043
  delay: (delayMs) => `Delay: ${MS_FORMAT.format(delayMs)}`,
15986
16044
  generators: {
15987
16045
  title: "GENERATORS",
16046
+ unnamed: "Unnamed Generator",
16047
+ edit: "Edit Generator",
15988
16048
  noChildren: "No generators yet. Please add one using the buttons below.",
15989
16049
  type: {
15990
16050
  clock: "Clock"
15991
16051
  },
15992
16052
  addDialog: (protocol) => `Add ${protocol} Generator`,
15993
- editDialog: (protocol, name) => `Edit ${protocol} Generator ${name}`
16053
+ editDialog: (protocol, name) => `Edit ${protocol} Generator ${name}`,
16054
+ deleteDialog: `Delete generator?`,
16055
+ deleteDialogDetails: `Are you sure you want to delete this generator? This action cannot be undone.`
15994
16056
  },
15995
16057
  outputs: {
15996
16058
  title: "OUTPUTS",
16059
+ unnamed: "Unnamed Output",
16060
+ enable: "Enable Output",
16061
+ disable: "Disable Output",
16062
+ link: "Link Output",
16063
+ edit: "Edit Output",
15997
16064
  noChildren: "No outputs yet. Please add one using the buttons below.",
15998
16065
  addButton: (protocol) => `Add ${protocol}`,
15999
16066
  addDialog: (protocol) => `Add ${protocol} Output`,
16000
- editDialog: (protocol, name) => `Edit ${protocol} Output ${name}`
16067
+ editDialog: (protocol, name) => `Edit ${protocol} Output ${name}`,
16068
+ deleteDialog: `Delete output?`,
16069
+ deleteDialogDetails: `Are you sure you want to delete this output? This action cannot be undone.`
16001
16070
  },
16002
16071
  settings: {
16003
- title: "Settings"
16072
+ title: "Settings",
16073
+ network: {
16074
+ appPortLabel: "Application Port",
16075
+ appInterfaceLabel: "Application Interface",
16076
+ anyInterface: "Any Interface",
16077
+ internalInterfaceUsed: (iface) => `Note: The interface ${iface} is only accessible from this device, which means that other devices will not be able to connect to Timecode Toolbox.`,
16078
+ appPortEnvOverride: (envPort) => `Note: The application port is currently set to ${envPort} via the PORT environment variable, and cannot be configured here.`,
16079
+ defaultPort: (port) => `Default: (${port})`,
16080
+ saveChanges: "Save Changes",
16081
+ saveWarning: {
16082
+ external: "When you hit Save, the UI will reload",
16083
+ internal: "When you hit Save, the app will attempt to reconnect using the new network settings, but may require you to adjust the URL manually."
16084
+ },
16085
+ invalidPortSingle: "Port numbers must be an integer between 1 and 65535",
16086
+ invalidPort: 'Please enter a valid port number or range (e.g. "1234" or "8000-8100")',
16087
+ invalidPortRange: 'The first port in a range must be less than or equal to the second port (e.g. "8000-8100")'
16088
+ }
16004
16089
  },
16005
16090
  controls: {
16006
16091
  play: "Play",
@@ -16320,7 +16405,9 @@ var TimecodeDisplay = ({
16320
16405
  id,
16321
16406
  timecode: { state, metadata },
16322
16407
  config,
16323
- headerComponents
16408
+ headerComponents,
16409
+ disabled,
16410
+ rootState
16324
16411
  }) => {
16325
16412
  const { handlers, callHandler } = useApplicationHandlers();
16326
16413
  const hooks = id && getTreeValue(handlers, id);
@@ -16371,29 +16458,41 @@ var TimecodeDisplay = ({
16371
16458
  )
16372
16459
  ),
16373
16460
  children: [
16374
- headerComponents && /* @__PURE__ */ jsx26("div", { className: "flex flex-wrap gap-0.25", children: headerComponents }),
16461
+ headerComponents && /* @__PURE__ */ jsx26("div", { className: "flex gap-0.25", children: headerComponents }),
16375
16462
  /* @__PURE__ */ jsx26(
16376
16463
  SizeAwareDiv,
16377
16464
  {
16378
- className: cn(
16379
- "relative min-h-timecode-min-height grow",
16380
- cnd(state?.state === "stopped", "opacity-50"),
16381
- cnd(
16382
- hooks?.play && hooks?.pause,
16383
- `
16384
- cursor-pointer
16385
- hover:opacity-100
16386
- `
16387
- )
16388
- ),
16465
+ className: "relative min-h-timecode-min-height grow",
16389
16466
  onClick: toggle,
16390
- children: /* @__PURE__ */ jsx26("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx26("span", { className: cn("font-mono text-timecode-adaptive"), children: state.state === "none" ? "--:--:--:---" : state.state === "stopped" ? displayMillis(state.positionMillis) : /* @__PURE__ */ jsx26(
16391
- ActiveTimecodeText,
16467
+ children: disabled ? /* @__PURE__ */ jsx26(
16468
+ SizeAwareDiv,
16469
+ {
16470
+ className: "\n pointer-events-none absolute inset-0 flex items-center\n justify-center\n ",
16471
+ children: /* @__PURE__ */ jsx26(Icon2, { icon: "pause", className: "text-timecode-adaptive" })
16472
+ }
16473
+ ) : /* @__PURE__ */ jsx26(
16474
+ "div",
16392
16475
  {
16393
- effectiveStartTimeMillis: state.effectiveStartTimeMillis,
16394
- speed: state.speed
16476
+ className: cn(
16477
+ "absolute inset-0 flex items-center justify-center",
16478
+ cnd(state?.state === "stopped", "opacity-50"),
16479
+ cnd(
16480
+ hooks?.play && hooks?.pause,
16481
+ `
16482
+ cursor-pointer
16483
+ hover:opacity-100
16484
+ `
16485
+ )
16486
+ ),
16487
+ children: /* @__PURE__ */ jsx26("span", { className: cn("font-mono text-timecode-adaptive"), children: state.state === "none" ? "--:--:--:---" : state.state === "stopped" ? displayMillis(state.positionMillis) : /* @__PURE__ */ jsx26(
16488
+ ActiveTimecodeText,
16489
+ {
16490
+ effectiveStartTimeMillis: state.effectiveStartTimeMillis,
16491
+ speed: state.speed
16492
+ }
16493
+ ) })
16395
16494
  }
16396
- ) }) })
16495
+ )
16397
16496
  }
16398
16497
  ),
16399
16498
  hooks?.pause || hooks?.play ? /* @__PURE__ */ jsxs9("div", { className: "flex justify-center gap-px", children: [
@@ -16481,9 +16580,53 @@ var TimecodeDisplay = ({
16481
16580
  metadata.artist && /* @__PURE__ */ jsx26("div", { className: "grow truncate bg-sigil-bg-light p-0.5", children: metadata.artist })
16482
16581
  ] })
16483
16582
  }
16484
- ) : null
16583
+ ) : null,
16584
+ rootState.errors.length > 0 && /* @__PURE__ */ jsx26("div", { className: "flex gap-px", children: rootState.errors.map((error, index2) => /* @__PURE__ */ jsx26(
16585
+ "div",
16586
+ {
16587
+ className: "\n grow truncate bg-sigil-usage-red-background p-0.5\n text-sigil-usage-red-text\n ",
16588
+ children: error
16589
+ },
16590
+ index2
16591
+ )) }),
16592
+ rootState.warnings.length > 0 && /* @__PURE__ */ jsx26("div", { className: "flex gap-px", children: rootState.warnings.map((warning, index2) => /* @__PURE__ */ jsx26(
16593
+ "div",
16594
+ {
16595
+ className: "\n grow truncate bg-sigil-usage-orange-background p-0.5\n text-sigil-usage-orange-text\n ",
16596
+ children: warning
16597
+ },
16598
+ index2
16599
+ )) })
16485
16600
  ] });
16486
16601
  };
16602
+ var getLinkedSourceInfo = (link, config) => {
16603
+ if (!link) {
16604
+ return void 0;
16605
+ }
16606
+ let info = void 0;
16607
+ if (link[0] === "input") {
16608
+ const input = config.inputs?.[link[1]];
16609
+ if (input) {
16610
+ info = {
16611
+ color: input.color,
16612
+ type: STRINGS.protocols[input.definition.type].short,
16613
+ name: input.name ? [input.name] : [],
16614
+ namePlaceholder: STRINGS.inputs.unnamed
16615
+ };
16616
+ }
16617
+ } else if (link[0] === "generator") {
16618
+ const generator = config.generators?.[link[1]];
16619
+ if (generator) {
16620
+ info = {
16621
+ color: generator.color,
16622
+ type: STRINGS.generators.type[generator.definition.type],
16623
+ name: generator.name ? [generator.name] : [],
16624
+ namePlaceholder: STRINGS.generators.unnamed
16625
+ };
16626
+ }
16627
+ }
16628
+ return info;
16629
+ };
16487
16630
  var EMPTY_TIMECODE = {
16488
16631
  name: null,
16489
16632
  state: {
@@ -16502,8 +16645,10 @@ var TimecodeTreeDisplay = ({
16502
16645
  id,
16503
16646
  type,
16504
16647
  name,
16648
+ link,
16505
16649
  color,
16506
16650
  timecode,
16651
+ rootState,
16507
16652
  namePlaceholder,
16508
16653
  buttons,
16509
16654
  assignToOutput
@@ -16517,8 +16662,8 @@ var TimecodeTreeDisplay = ({
16517
16662
  });
16518
16663
  }
16519
16664
  }, [id, openNewWidow]);
16520
- name = timecode?.name ? [...name, timecode.name] : name;
16521
- if (isTimecodeGroup(timecode) && Object.values(timecode.timecodes).length) {
16665
+ name = timecode !== "disabled" && timecode?.name ? [...name, timecode.name] : name;
16666
+ if (timecode !== "disabled" && isTimecodeGroup(timecode) && Object.values(timecode.timecodes).length) {
16522
16667
  return Object.entries(timecode.timecodes).map(([key, child]) => /* @__PURE__ */ jsx26(
16523
16668
  TimecodeTreeDisplay,
16524
16669
  {
@@ -16528,6 +16673,7 @@ var TimecodeTreeDisplay = ({
16528
16673
  name,
16529
16674
  color: timecode.color ?? color,
16530
16675
  timecode: child,
16676
+ rootState,
16531
16677
  namePlaceholder,
16532
16678
  buttons,
16533
16679
  assignToOutput
@@ -16545,28 +16691,67 @@ var TimecodeTreeDisplay = ({
16545
16691
  TimecodeDisplay,
16546
16692
  {
16547
16693
  id,
16548
- timecode: isTimecodeInstance(timecode) ? timecode : EMPTY_TIMECODE,
16694
+ timecode: timecode !== "disabled" && isTimecodeInstance(timecode) ? timecode : EMPTY_TIMECODE,
16695
+ rootState,
16696
+ disabled: timecode === "disabled",
16549
16697
  config,
16550
16698
  headerComponents: /* @__PURE__ */ jsxs9(Fragment8, { children: [
16551
- /* @__PURE__ */ jsxs9("div", { className: "flex grow items-start gap-0.25", children: [
16552
- /* @__PURE__ */ jsx26(
16553
- "div",
16554
- {
16555
- className: "\n m-0.25 rounded-md border border-sigil-bg-light\n bg-timecode-usage-foreground px-1 py-0.25 text-sigil-control\n text-timecode-usage-text\n ",
16556
- children: type
16557
- }
16558
- ),
16559
- /* @__PURE__ */ jsx26(
16699
+ /* @__PURE__ */ jsx26("div", { className: "flex grow basis-0 items-start gap-0.25", children: /* @__PURE__ */ jsxs9("div", { className: "grow", children: [
16700
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-0.25", children: [
16701
+ /* @__PURE__ */ jsx26(
16702
+ "div",
16703
+ {
16704
+ className: "\n m-0.25 rounded-md border border-sigil-bg-light\n bg-timecode-usage-foreground px-1 py-0.25\n text-sigil-control text-timecode-usage-text\n ",
16705
+ children: type
16706
+ }
16707
+ ),
16708
+ /* @__PURE__ */ jsx26(
16709
+ "div",
16710
+ {
16711
+ className: cn(
16712
+ "w-0 grow truncate p-0.5",
16713
+ cnd(name.length, "font-bold", "italic opacity-50")
16714
+ ),
16715
+ children: name.length ? name.join(" / ") : namePlaceholder
16716
+ }
16717
+ )
16718
+ ] }),
16719
+ link && /* @__PURE__ */ jsxs9(
16560
16720
  "div",
16561
16721
  {
16562
- className: cn(
16563
- "grow basis-0 truncate p-0.5",
16564
- cnd(name.length, "font-bold", "italic opacity-50")
16722
+ className: "\n flex items-center gap-0.25 text-timecode-usage-foreground\n ",
16723
+ style: cssSigilColorUsageVariables(
16724
+ "timecode-usage",
16725
+ // Override timecode color with the user hint preferences
16726
+ // when no color is specified
16727
+ // as that will be what's used by the linked input/generator
16728
+ sigilColorUsage(link.color ?? "hint")
16565
16729
  ),
16566
- children: name.length ? name.join(" / ") : namePlaceholder
16730
+ children: [
16731
+ /* @__PURE__ */ jsxs9(
16732
+ "div",
16733
+ {
16734
+ className: "\n m-0.25 flex items-center gap-0.25 rounded-md border\n border-sigil-bg-light bg-timecode-usage-foreground px-1\n py-0.25 text-sigil-control text-timecode-usage-text\n ",
16735
+ children: [
16736
+ /* @__PURE__ */ jsx26(Icon2, { icon: "link", className: "text-[120%]" }),
16737
+ /* @__PURE__ */ jsx26("span", { children: link.type })
16738
+ ]
16739
+ }
16740
+ ),
16741
+ /* @__PURE__ */ jsx26(
16742
+ "div",
16743
+ {
16744
+ className: cn(
16745
+ "w-0 grow truncate p-0.5",
16746
+ cnd(link.name.length, "font-bold", "italic opacity-50")
16747
+ ),
16748
+ children: link.name.length ? link.name.join(" / ") : link.namePlaceholder
16749
+ }
16750
+ )
16751
+ ]
16567
16752
  }
16568
16753
  )
16569
- ] }),
16754
+ ] }) }),
16570
16755
  /* @__PURE__ */ jsxs9(ControlButtonGroup, { className: "rounded-md bg-sigil-bg-light", children: [
16571
16756
  /* @__PURE__ */ jsx26(
16572
16757
  ControlButton,
@@ -16604,7 +16789,7 @@ var FullscreenTimecodeDisplay = ({
16604
16789
  return getTimecodeInstance(applicationState, id);
16605
16790
  } else {
16606
16791
  const c = config.outputs[id[1]];
16607
- if (!c) {
16792
+ if (!c || !c.enabled) {
16608
16793
  return null;
16609
16794
  }
16610
16795
  return augmentUpstreamTimecodeWithOutputMetadata(
@@ -16613,6 +16798,15 @@ var FullscreenTimecodeDisplay = ({
16613
16798
  );
16614
16799
  }
16615
16800
  }, [applicationState, id, config.outputs]);
16801
+ const linkedSourceInfo = useMemo13(() => {
16802
+ if (isOutputInstanceId(id)) {
16803
+ const c = config.outputs[id[1]];
16804
+ if (c?.link) {
16805
+ return getLinkedSourceInfo(c.link, config);
16806
+ }
16807
+ }
16808
+ return void 0;
16809
+ }, [id, config]);
16616
16810
  const instanceConfig = useMemo13(() => {
16617
16811
  if (isInputInstanceId(id)) {
16618
16812
  const c = config.inputs[id[1]];
@@ -16624,7 +16818,8 @@ var FullscreenTimecodeDisplay = ({
16624
16818
  type: STRINGS.protocols[c.definition.type].short,
16625
16819
  name: c.name ? [c.name] : [],
16626
16820
  color: c.color,
16627
- namePlaceholder: `Unnamed Input`
16821
+ namePlaceholder: STRINGS.inputs.unnamed,
16822
+ disabled: !c.enabled
16628
16823
  };
16629
16824
  } else if (isGeneratorInstanceId(id)) {
16630
16825
  const c = config.generators[id[1]];
@@ -16636,7 +16831,8 @@ var FullscreenTimecodeDisplay = ({
16636
16831
  type: STRINGS.generators.type[c.definition.type],
16637
16832
  name: c.name ? [c.name] : [],
16638
16833
  color: c.color,
16639
- namePlaceholder: `Unnamed Generator`
16834
+ namePlaceholder: STRINGS.generators.unnamed,
16835
+ disabled: false
16640
16836
  };
16641
16837
  } else {
16642
16838
  const c = config.outputs[id[1]];
@@ -16648,10 +16844,29 @@ var FullscreenTimecodeDisplay = ({
16648
16844
  type: STRINGS.protocols[c.definition.type].short,
16649
16845
  name: c.name ? [c.name] : [],
16650
16846
  color: c.color,
16651
- namePlaceholder: `Unnamed Output`
16847
+ namePlaceholder: STRINGS.outputs.unnamed,
16848
+ disabled: !c.enabled
16652
16849
  };
16653
16850
  }
16654
16851
  }, [id, config]);
16852
+ const rootState = useMemo13(() => {
16853
+ if (isInputInstanceId(id)) {
16854
+ return {
16855
+ errors: applicationState.inputs?.[id[1]]?.errors ?? [],
16856
+ warnings: applicationState.inputs?.[id[1]]?.warnings ?? []
16857
+ };
16858
+ } else if (isGeneratorInstanceId(id)) {
16859
+ return {
16860
+ errors: [],
16861
+ warnings: []
16862
+ };
16863
+ } else {
16864
+ return {
16865
+ errors: applicationState.outputs?.[id[1]]?.errors ?? [],
16866
+ warnings: applicationState.outputs?.[id[1]]?.warnings ?? []
16867
+ };
16868
+ }
16869
+ }, [id, applicationState]);
16655
16870
  if (!instanceConfig) {
16656
16871
  return /* @__PURE__ */ jsxs9(
16657
16872
  SizeAwareDiv,
@@ -16672,9 +16887,11 @@ var FullscreenTimecodeDisplay = ({
16672
16887
  TimecodeTreeDisplay,
16673
16888
  {
16674
16889
  id,
16675
- timecode,
16890
+ timecode: instanceConfig.disabled ? "disabled" : timecode,
16891
+ rootState,
16676
16892
  assignToOutput: null,
16677
16893
  buttons: null,
16894
+ link: linkedSourceInfo,
16678
16895
  ...instanceConfig
16679
16896
  }
16680
16897
  )
@@ -16934,7 +17151,7 @@ var DmxConnectionSettings = ({
16934
17151
  var OutputSettingsDialog = ({
16935
17152
  target,
16936
17153
  output,
16937
- close
17154
+ setDialogMode
16938
17155
  }) => {
16939
17156
  const { config, updateConfig } = useContext10(ConfigContext);
16940
17157
  const [newData, setNewData] = useState25({
@@ -16950,6 +17167,7 @@ var OutputSettingsDialog = ({
16950
17167
  },
16951
17168
  link: null
16952
17169
  });
17170
+ const close = useCallback14(() => setDialogMode(null), [setDialogMode]);
16953
17171
  const updateSettings = useCallback14(
16954
17172
  (change) => {
16955
17173
  if (target.type === "add") {
@@ -17080,10 +17298,25 @@ var OutputSettingsDialog = ({
17080
17298
  }
17081
17299
  }
17082
17300
  ),
17083
- resolvedTarget === "add" ? /* @__PURE__ */ jsxs11(ControlDialogButtons, { children: [
17301
+ target.type === "add" ? /* @__PURE__ */ jsxs11(ControlDialogButtons, { children: [
17084
17302
  /* @__PURE__ */ jsx28(ControlButton, { onClick: close, variant: "large", children: "Cancel" }),
17085
17303
  /* @__PURE__ */ jsx28(ControlButton, { onClick: addOutput, variant: "large", children: "Add Output" })
17086
- ] }) : /* @__PURE__ */ jsx28(ControlDialogButtons, { children: /* @__PURE__ */ jsx28(ControlButton, { onClick: close, variant: "large", children: "Close" }) })
17304
+ ] }) : target.type === "edit" ? /* @__PURE__ */ jsxs11(ControlDialogButtons, { children: [
17305
+ /* @__PURE__ */ jsx28(
17306
+ ControlButton,
17307
+ {
17308
+ onClick: () => setDialogMode({
17309
+ section: { type: "outputs", output },
17310
+ target: { type: "delete", uuid: target.uuid }
17311
+ }),
17312
+ variant: "large",
17313
+ destructive: true,
17314
+ icon: "delete",
17315
+ children: "Delete"
17316
+ }
17317
+ ),
17318
+ /* @__PURE__ */ jsx28(ControlButton, { onClick: close, variant: "large", children: "Close" })
17319
+ ] }) : null
17087
17320
  ]
17088
17321
  }
17089
17322
  ) });
@@ -17096,7 +17329,7 @@ var OutputDisplay = ({
17096
17329
  setAssignToOutput
17097
17330
  }) => {
17098
17331
  const applicationState = useApplicationState();
17099
- const { updateConfig } = useContext10(ConfigContext);
17332
+ const { config: allConfig, updateConfig } = useContext10(ConfigContext);
17100
17333
  const clearLink = useCallback14(() => {
17101
17334
  updateConfig((current) => {
17102
17335
  const currentOutput = current.outputs?.[uuid];
@@ -17124,6 +17357,10 @@ var OutputDisplay = ({
17124
17357
  const tc = config.link && getTimecodeInstance(applicationState, config.link);
17125
17358
  return augmentUpstreamTimecodeWithOutputMetadata(tc, config);
17126
17359
  }, [applicationState, config]);
17360
+ const link = useMemo14(
17361
+ () => getLinkedSourceInfo(config.link, allConfig),
17362
+ [config.link, allConfig]
17363
+ );
17127
17364
  const toggleEnabled = useCallback14(() => {
17128
17365
  updateConfig((current) => {
17129
17366
  const existing = current.outputs?.[uuid];
@@ -17142,6 +17379,13 @@ var OutputDisplay = ({
17142
17379
  };
17143
17380
  });
17144
17381
  }, [uuid, updateConfig]);
17382
+ const rootState = useMemo14(
17383
+ () => ({
17384
+ errors: applicationState.outputs[uuid]?.errors ?? [],
17385
+ warnings: applicationState.outputs[uuid]?.warnings ?? []
17386
+ }),
17387
+ [applicationState.outputs, uuid]
17388
+ );
17145
17389
  return /* @__PURE__ */ jsxs11(
17146
17390
  "div",
17147
17391
  {
@@ -17160,23 +17404,25 @@ var OutputDisplay = ({
17160
17404
  type: STRINGS.protocols[config.definition.type].short,
17161
17405
  name: config.name ? [config.name] : [],
17162
17406
  color: config.color,
17163
- timecode: config.enabled ? timecode : null,
17164
- namePlaceholder: `Unnamed Output`,
17407
+ timecode: config.enabled ? timecode : "disabled",
17408
+ rootState,
17409
+ namePlaceholder: STRINGS.outputs.unnamed,
17410
+ link,
17165
17411
  buttons: /* @__PURE__ */ jsxs11(Fragment9, { children: [
17166
17412
  /* @__PURE__ */ jsx28(
17167
17413
  ControlButton,
17168
17414
  {
17169
17415
  variant: "large",
17170
- title: config.enabled ? "Stop Input" : "Start Input",
17416
+ title: config.enabled ? STRINGS.outputs.disable : STRINGS.outputs.enable,
17171
17417
  onClick: toggleEnabled,
17172
- icon: config.enabled ? "stop" : "play_arrow"
17418
+ icon: config.enabled ? "pause" : "play_arrow"
17173
17419
  }
17174
17420
  ),
17175
17421
  /* @__PURE__ */ jsx28(
17176
17422
  ControlButton,
17177
17423
  {
17178
17424
  variant: "large",
17179
- title: "Link Output",
17425
+ title: STRINGS.outputs.link,
17180
17426
  active: assignToOutput === uuid,
17181
17427
  onClick: linkCallback,
17182
17428
  icon: config.link ? "link" : "link_off"
@@ -17186,7 +17432,7 @@ var OutputDisplay = ({
17186
17432
  ControlButton,
17187
17433
  {
17188
17434
  variant: "large",
17189
- title: "Edit Output",
17435
+ title: STRINGS.outputs.edit,
17190
17436
  onClick: () => setDialogMode({
17191
17437
  section: { type: "outputs", output: config.definition.type },
17192
17438
  target: { type: "edit", uuid }
@@ -17256,7 +17502,7 @@ var OutputsSection = ({
17256
17502
  };
17257
17503
 
17258
17504
  // src/components/frontend/toolbox/generators.tsx
17259
- import { useCallback as useCallback15, useContext as useContext11, useState as useState26 } from "react";
17505
+ import { useCallback as useCallback15, useContext as useContext11, useMemo as useMemo15, useState as useState26 } from "react";
17260
17506
  import { Fragment as Fragment10, jsx as jsx29, jsxs as jsxs12 } from "react/jsx-runtime";
17261
17507
  var ClockSpecificSettings = ({
17262
17508
  data,
@@ -17295,7 +17541,7 @@ var ClockSpecificSettings = ({
17295
17541
  var GeneratorSettingsDialog = ({
17296
17542
  target,
17297
17543
  generator,
17298
- close
17544
+ setDialogMode
17299
17545
  }) => {
17300
17546
  const { config, updateConfig } = useContext11(ConfigContext);
17301
17547
  const [newData, setNewData] = useState26({
@@ -17305,6 +17551,7 @@ var GeneratorSettingsDialog = ({
17305
17551
  speed: 1
17306
17552
  }
17307
17553
  });
17554
+ const close = useCallback15(() => setDialogMode(null), [setDialogMode]);
17308
17555
  const updateSettings = useCallback15(
17309
17556
  (change) => {
17310
17557
  if (target.type === "add") {
@@ -17412,10 +17659,25 @@ var GeneratorSettingsDialog = ({
17412
17659
  updateSettings: updateDefinition
17413
17660
  }
17414
17661
  ) : null,
17415
- resolvedTarget === "add" ? /* @__PURE__ */ jsxs12(ControlDialogButtons, { children: [
17662
+ target.type === "add" ? /* @__PURE__ */ jsxs12(ControlDialogButtons, { children: [
17416
17663
  /* @__PURE__ */ jsx29(ControlButton, { onClick: close, variant: "large", children: "Cancel" }),
17417
17664
  /* @__PURE__ */ jsx29(ControlButton, { onClick: addGenerator, variant: "large", children: "Add Generator" })
17418
- ] }) : /* @__PURE__ */ jsx29(ControlDialogButtons, { children: /* @__PURE__ */ jsx29(ControlButton, { onClick: close, variant: "large", children: "Close" }) })
17665
+ ] }) : target.type === "edit" ? /* @__PURE__ */ jsxs12(ControlDialogButtons, { children: [
17666
+ /* @__PURE__ */ jsx29(
17667
+ ControlButton,
17668
+ {
17669
+ onClick: () => setDialogMode({
17670
+ section: { type: "generators", generator },
17671
+ target: { type: "delete", uuid: target.uuid }
17672
+ }),
17673
+ variant: "large",
17674
+ destructive: true,
17675
+ icon: "delete",
17676
+ children: "Delete"
17677
+ }
17678
+ ),
17679
+ /* @__PURE__ */ jsx29(ControlButton, { onClick: close, variant: "large", children: "Close" })
17680
+ ] }) : null
17419
17681
  ]
17420
17682
  }
17421
17683
  ) });
@@ -17428,6 +17690,7 @@ var GeneratorDisplay = ({
17428
17690
  }) => {
17429
17691
  const { generators } = useApplicationState();
17430
17692
  const state = generators[uuid];
17693
+ const rootState = useMemo15(() => ({ errors: [], warnings: [] }), []);
17431
17694
  return /* @__PURE__ */ jsx29(
17432
17695
  TimecodeTreeDisplay,
17433
17696
  {
@@ -17437,12 +17700,13 @@ var GeneratorDisplay = ({
17437
17700
  name: config.name ? [config.name] : [],
17438
17701
  color: config.color,
17439
17702
  timecode: state?.timecode ?? null,
17440
- namePlaceholder: `Unnamed Generator`,
17703
+ rootState,
17704
+ namePlaceholder: STRINGS.generators.unnamed,
17441
17705
  buttons: /* @__PURE__ */ jsx29(Fragment10, { children: /* @__PURE__ */ jsx29(
17442
17706
  ControlButton,
17443
17707
  {
17444
17708
  variant: "large",
17445
- title: "Edit Generator",
17709
+ title: STRINGS.generators.edit,
17446
17710
  onClick: () => setDialogMode({
17447
17711
  section: {
17448
17712
  type: "generators",
@@ -17500,7 +17764,13 @@ var GeneratorsSection = ({
17500
17764
  };
17501
17765
 
17502
17766
  // src/components/frontend/toolbox/inputs.tsx
17503
- import { useCallback as useCallback16, useContext as useContext12, useEffect as useEffect27, useState as useState27 } from "react";
17767
+ import {
17768
+ useCallback as useCallback16,
17769
+ useContext as useContext12,
17770
+ useEffect as useEffect27,
17771
+ useMemo as useMemo16,
17772
+ useState as useState27
17773
+ } from "react";
17504
17774
  import { Fragment as Fragment11, jsx as jsx30, jsxs as jsxs13 } from "react/jsx-runtime";
17505
17775
  var DmxConnectionSettings2 = ({
17506
17776
  data,
@@ -17629,7 +17899,7 @@ var TCNetConnectionSettings = ({
17629
17899
  var InputSettingsDialog = ({
17630
17900
  target,
17631
17901
  input,
17632
- close
17902
+ setDialogMode
17633
17903
  }) => {
17634
17904
  const { config, updateConfig } = useContext12(ConfigContext);
17635
17905
  const [newData, setNewData] = useState27({
@@ -17644,6 +17914,7 @@ var InputSettingsDialog = ({
17644
17914
  iface: ""
17645
17915
  }
17646
17916
  });
17917
+ const close = useCallback16(() => setDialogMode(null), [setDialogMode]);
17647
17918
  const updateSettings = useCallback16(
17648
17919
  (change) => {
17649
17920
  if (target.type === "add") {
@@ -17780,10 +18051,25 @@ var InputSettingsDialog = ({
17780
18051
  }
17781
18052
  }
17782
18053
  ),
17783
- resolvedTarget === "add" ? /* @__PURE__ */ jsxs13(ControlDialogButtons, { children: [
18054
+ target.type === "add" ? /* @__PURE__ */ jsxs13(ControlDialogButtons, { children: [
17784
18055
  /* @__PURE__ */ jsx30(ControlButton, { onClick: close, variant: "large", children: "Cancel" }),
17785
- /* @__PURE__ */ jsx30(ControlButton, { onClick: addInput, variant: "large", children: "Add Input" })
17786
- ] }) : /* @__PURE__ */ jsx30(ControlDialogButtons, { children: /* @__PURE__ */ jsx30(ControlButton, { onClick: close, variant: "large", children: "Close" }) })
18056
+ /* @__PURE__ */ jsx30(ControlButton, { onClick: addInput, variant: "large", primary: true, children: "Add Input" })
18057
+ ] }) : target?.type === "edit" ? /* @__PURE__ */ jsxs13(ControlDialogButtons, { children: [
18058
+ /* @__PURE__ */ jsx30(
18059
+ ControlButton,
18060
+ {
18061
+ onClick: () => setDialogMode({
18062
+ section: { type: "inputs", input },
18063
+ target: { type: "delete", uuid: target.uuid }
18064
+ }),
18065
+ variant: "large",
18066
+ destructive: true,
18067
+ icon: "delete",
18068
+ children: "Delete"
18069
+ }
18070
+ ),
18071
+ /* @__PURE__ */ jsx30(ControlButton, { onClick: close, variant: "large", children: "Close" })
18072
+ ] }) : null
17787
18073
  ]
17788
18074
  }
17789
18075
  ) });
@@ -17815,6 +18101,10 @@ var InputDisplay = ({
17815
18101
  };
17816
18102
  });
17817
18103
  }, [uuid, updateConfig]);
18104
+ const rootState = useMemo16(
18105
+ () => ({ errors: state?.errors ?? [], warnings: state?.warnings ?? [] }),
18106
+ [state?.errors, state?.warnings]
18107
+ );
17818
18108
  return /* @__PURE__ */ jsx30(
17819
18109
  TimecodeTreeDisplay,
17820
18110
  {
@@ -17823,23 +18113,24 @@ var InputDisplay = ({
17823
18113
  type: STRINGS.protocols[config.definition.type].short,
17824
18114
  name: config.name ? [config.name] : [],
17825
18115
  color: config.color,
17826
- timecode: state?.timecode ?? null,
17827
- namePlaceholder: `Unnamed Input`,
18116
+ timecode: config.enabled ? state?.timecode ?? null : "disabled",
18117
+ rootState,
18118
+ namePlaceholder: STRINGS.inputs.unnamed,
17828
18119
  buttons: /* @__PURE__ */ jsxs13(Fragment11, { children: [
17829
18120
  /* @__PURE__ */ jsx30(
17830
18121
  ControlButton,
17831
18122
  {
17832
18123
  variant: "large",
17833
- title: config.enabled ? "Stop Input" : "Start Input",
18124
+ title: config.enabled ? STRINGS.inputs.disable : STRINGS.inputs.enable,
17834
18125
  onClick: toggleEnabled,
17835
- icon: config.enabled ? "stop" : "play_arrow"
18126
+ icon: config.enabled ? "pause" : "play_arrow"
17836
18127
  }
17837
18128
  ),
17838
18129
  /* @__PURE__ */ jsx30(
17839
18130
  ControlButton,
17840
18131
  {
17841
18132
  variant: "large",
17842
- title: "Edit Input",
18133
+ title: STRINGS.inputs.edit,
17843
18134
  onClick: () => setDialogMode({
17844
18135
  section: { type: "inputs", input: config.definition.type },
17845
18136
  target: { type: "edit", uuid }
@@ -17972,8 +18263,20 @@ var diffJson = (a, b) => {
17972
18263
  return { type: "value", after: b };
17973
18264
  };
17974
18265
 
18266
+ // src/components/frontend/toolbox/types.ts
18267
+ var isDeleteDialogMode = (mode) => {
18268
+ return mode?.target.type === "delete";
18269
+ };
18270
+
17975
18271
  // src/components/frontend/toolbox/settings.tsx
17976
- import { useContext as useContext13 } from "react";
18272
+ import {
18273
+ useCallback as useCallback18,
18274
+ useContext as useContext13,
18275
+ useEffect as useEffect29,
18276
+ useMemo as useMemo17,
18277
+ useRef as useRef17,
18278
+ useState as useState29
18279
+ } from "react";
17977
18280
 
17978
18281
  // ../../packages/sigil/dist/frontend/appearance.js
17979
18282
  import { useCallback as useCallback17 } from "react";
@@ -18118,7 +18421,7 @@ var TOOLBOX_PREFERENCES = {
18118
18421
  var useBrowserPreferences = createBrowserPreferencesHook(TOOLBOX_PREFERENCES);
18119
18422
 
18120
18423
  // src/components/frontend/toolbox/settings.tsx
18121
- import { jsx as jsx35, jsxs as jsxs15 } from "react/jsx-runtime";
18424
+ import { Fragment as Fragment12, jsx as jsx35, jsxs as jsxs15 } from "react/jsx-runtime";
18122
18425
  var ENABLED_DISABLED_OPTIONS = [
18123
18426
  { value: "enabled", label: STRINGS.general.enabled },
18124
18427
  { value: "disabled", label: STRINGS.general.disabled }
@@ -18126,6 +18429,180 @@ var ENABLED_DISABLED_OPTIONS = [
18126
18429
  var DATE_FORMATTER = new Intl.DateTimeFormat(void 0, {
18127
18430
  timeStyle: "medium"
18128
18431
  });
18432
+ var AppPortConfig = () => {
18433
+ const { getNetworkInterfaces } = useContext13(NetworkContext);
18434
+ const [interfaces, setInterfaces] = useState29(null);
18435
+ const refreshInterfaces = useCallback18(() => {
18436
+ setInterfaces(null);
18437
+ getNetworkInterfaces().then((ifs) => setInterfaces(ifs));
18438
+ }, [getNetworkInterfaces]);
18439
+ useEffect29(() => {
18440
+ refreshInterfaces();
18441
+ }, [refreshInterfaces]);
18442
+ const { appListenerChangesHandledExternally } = useBrowserContext();
18443
+ const { network, config, updateConfig } = useContext13(ConfigContext);
18444
+ const [nextPort, setNextPort] = useState29(null);
18445
+ const [nextInterface, setNextInterface] = useState29(
18446
+ null
18447
+ );
18448
+ const iface = nextInterface ?? (config.appListener?.interface ? `specific:${config.appListener?.interface}` : "any");
18449
+ const port = nextPort ?? config.appListener?.port;
18450
+ const currentPortString = !port ? "" : typeof port === "string" ? port : portString(port);
18451
+ const hasNetworkChanges = nextPort !== null || nextInterface !== null;
18452
+ const nextUrlRef = useRef17(null);
18453
+ const validatedPort = useMemo17(() => {
18454
+ if (nextPort === null) {
18455
+ return { type: "unchanged" };
18456
+ }
18457
+ if (nextPort.trim() === "") {
18458
+ return { type: "empty" };
18459
+ }
18460
+ const portParts = nextPort.split("-").map((part) => parseInt(part.trim(), 10));
18461
+ if (portParts.length === 1) {
18462
+ const [singlePort] = portParts;
18463
+ if (!singlePort || isNaN(singlePort) || singlePort < 1 || singlePort > 65535) {
18464
+ return {
18465
+ type: "invalid",
18466
+ error: STRINGS.settings.network.invalidPortSingle
18467
+ };
18468
+ }
18469
+ return { type: "valid", port: singlePort };
18470
+ }
18471
+ if (portParts.length === 2) {
18472
+ const [from, to] = portParts;
18473
+ if (!from || isNaN(from) || from < 1 || from > 65535 || !to || isNaN(to) || to < 1 || to > 65535) {
18474
+ return {
18475
+ type: "invalid",
18476
+ error: STRINGS.settings.network.invalidPortSingle
18477
+ };
18478
+ } else if (from > to) {
18479
+ return {
18480
+ type: "invalid",
18481
+ error: STRINGS.settings.network.invalidPortRange
18482
+ };
18483
+ }
18484
+ return { type: "valid", port: { from, to } };
18485
+ }
18486
+ return { type: "invalid", error: STRINGS.settings.network.invalidPort };
18487
+ }, [nextPort]);
18488
+ const canSave = hasNetworkChanges && validatedPort?.type !== "invalid";
18489
+ const saveNetworkConfig = useCallback18(() => {
18490
+ if (validatedPort?.type === "invalid") {
18491
+ return;
18492
+ }
18493
+ const newPort = validatedPort;
18494
+ updateConfig((current) => {
18495
+ const config2 = {
18496
+ ...current,
18497
+ appListener: {
18498
+ port: newPort.type === "empty" ? void 0 : newPort.type === "valid" ? newPort.port : current.appListener?.port,
18499
+ interface: nextInterface === "any" ? void 0 : nextInterface ? nextInterface.replace("specific:", "") : current.appListener?.interface
18500
+ }
18501
+ };
18502
+ const nextUrl = new URL(window.location.href);
18503
+ if (config2.appListener?.interface) {
18504
+ nextUrl.hostname = interfaces?.[config2.appListener.interface]?.address ?? nextUrl.hostname;
18505
+ }
18506
+ const port2 = config2.appListener?.port ?? network.defaultPort;
18507
+ nextUrl.port = (typeof port2 === "number" ? port2 : port2.from).toString();
18508
+ nextUrlRef.current = nextUrl;
18509
+ return config2;
18510
+ });
18511
+ setNextPort(null);
18512
+ setNextInterface(null);
18513
+ }, [
18514
+ interfaces,
18515
+ nextInterface,
18516
+ validatedPort,
18517
+ updateConfig,
18518
+ network.defaultPort
18519
+ ]);
18520
+ useEffect29(() => {
18521
+ return () => {
18522
+ if (!appListenerChangesHandledExternally && nextUrlRef.current) {
18523
+ window.location.href = nextUrlRef.current.href;
18524
+ }
18525
+ };
18526
+ }, [appListenerChangesHandledExternally]);
18527
+ return /* @__PURE__ */ jsxs15(Fragment12, { children: [
18528
+ /* @__PURE__ */ jsx35(ControlLabel, { children: STRINGS.settings.network.appInterfaceLabel }),
18529
+ /* @__PURE__ */ jsx35(
18530
+ ControlSelect,
18531
+ {
18532
+ value: iface,
18533
+ options: [
18534
+ { label: STRINGS.settings.network.anyInterface, value: "any" },
18535
+ ...!interfaces ? [] : Object.values(interfaces).map((iface2) => ({
18536
+ label: `${iface2.name} (${iface2.address})`,
18537
+ value: `specific:${iface2.name}`
18538
+ }))
18539
+ ],
18540
+ onChange: setNextInterface,
18541
+ position: "first",
18542
+ variant: "large",
18543
+ triggerClassName: cn("text-sigil-control")
18544
+ }
18545
+ ),
18546
+ iface && interfaces?.[iface]?.internal && /* @__PURE__ */ jsx35(
18547
+ ControlDetails,
18548
+ {
18549
+ position: "second",
18550
+ className: "text-sigil-warning-foreground",
18551
+ children: STRINGS.settings.network.internalInterfaceUsed(iface)
18552
+ }
18553
+ ),
18554
+ /* @__PURE__ */ jsx35(
18555
+ ControlButton,
18556
+ {
18557
+ onClick: refreshInterfaces,
18558
+ title: "Refresh Interfaces",
18559
+ position: "extra",
18560
+ variant: "large",
18561
+ children: /* @__PURE__ */ jsx35(Icon2, { icon: "refresh", className: "text-arcane-normal" })
18562
+ }
18563
+ ),
18564
+ /* @__PURE__ */ jsx35(ControlLabel, { children: STRINGS.settings.network.appPortLabel }),
18565
+ network.envPort ? /* @__PURE__ */ jsx35(ControlDetails, { position: "both", children: STRINGS.settings.network.appPortEnvOverride(network.envPort) }) : /* @__PURE__ */ jsxs15(Fragment12, { children: [
18566
+ /* @__PURE__ */ jsx35(
18567
+ ControlInput,
18568
+ {
18569
+ value: currentPortString,
18570
+ onChange: setNextPort,
18571
+ placeholder: STRINGS.settings.network.defaultPort(
18572
+ portString(network.defaultPort)
18573
+ )
18574
+ }
18575
+ ),
18576
+ validatedPort.type === "invalid" && /* @__PURE__ */ jsx35(
18577
+ ControlDetails,
18578
+ {
18579
+ position: "second",
18580
+ className: "text-sigil-error-foreground",
18581
+ children: validatedPort.error
18582
+ }
18583
+ )
18584
+ ] }),
18585
+ /* @__PURE__ */ jsx35(
18586
+ ControlButton,
18587
+ {
18588
+ onClick: saveNetworkConfig,
18589
+ variant: "large",
18590
+ position: "first",
18591
+ disabled: !canSave,
18592
+ children: STRINGS.settings.network.saveChanges
18593
+ }
18594
+ ),
18595
+ hasNetworkChanges && appListenerChangesHandledExternally && /* @__PURE__ */ jsx35(ControlDetails, { position: "second", children: STRINGS.settings.network.saveWarning.external }),
18596
+ hasNetworkChanges && !appListenerChangesHandledExternally && /* @__PURE__ */ jsx35(
18597
+ ControlDetails,
18598
+ {
18599
+ position: "second",
18600
+ className: "text-sigil-warning-foreground",
18601
+ children: STRINGS.settings.network.saveWarning.internal
18602
+ }
18603
+ )
18604
+ ] });
18605
+ };
18129
18606
  var Settings = ({ setWindowMode }) => {
18130
18607
  const { preferences, updateBrowserPrefs } = useBrowserPreferences();
18131
18608
  const { config, updateConfig } = useContext13(ConfigContext);
@@ -18173,7 +18650,8 @@ var Settings = ({ setWindowMode }) => {
18173
18650
  /* @__PURE__ */ jsx35(ControlDetails, { children: STRINGS.updates.settingsDetails }),
18174
18651
  updates && "lastCheckedMillis" in updates && /* @__PURE__ */ jsx35(ControlDetails, { children: STRINGS.updates.lastChecked(
18175
18652
  DATE_FORMATTER.format(updates.lastCheckedMillis)
18176
- ) })
18653
+ ) }),
18654
+ /* @__PURE__ */ jsx35(AppPortConfig, {})
18177
18655
  ] })
18178
18656
  }
18179
18657
  )
@@ -18181,7 +18659,7 @@ var Settings = ({ setWindowMode }) => {
18181
18659
  };
18182
18660
 
18183
18661
  // src/components/frontend/toolbox/license.tsx
18184
- import { useCallback as useCallback18, useContext as useContext15 } from "react";
18662
+ import { useCallback as useCallback19, useContext as useContext15 } from "react";
18185
18663
 
18186
18664
  // src/components/frontend/toolbox/logo.tsx
18187
18665
  import { jsx as jsx36, jsxs as jsxs16 } from "react/jsx-runtime";
@@ -18520,10 +18998,10 @@ var TimecodeToolboxLogo = (props) => /* @__PURE__ */ jsxs16(
18520
18998
  );
18521
18999
 
18522
19000
  // src/components/frontend/toolbox/core/layout.tsx
18523
- import { useContext as useContext14, useState as useState29 } from "react";
19001
+ import { useContext as useContext14, useState as useState30 } from "react";
18524
19002
 
18525
19003
  // src/components/frontend/toolbox/core/footer.tsx
18526
- import { Fragment as Fragment12, jsx as jsx37, jsxs as jsxs17 } from "react/jsx-runtime";
19004
+ import { Fragment as Fragment13, jsx as jsx37, jsxs as jsxs17 } from "react/jsx-runtime";
18527
19005
  var Footer = ({ openLicenseDetails }) => {
18528
19006
  return /* @__PURE__ */ jsxs17(
18529
19007
  "div",
@@ -18537,7 +19015,7 @@ var Footer = ({ openLicenseDetails }) => {
18537
19015
  ] }),
18538
19016
  /* @__PURE__ */ jsx37(ToolbarDivider, {}),
18539
19017
  /* @__PURE__ */ jsx37(ExternalLink, { href: SOURCE_CODE_URL, children: STRINGS.sourceCode }),
18540
- openLicenseDetails && /* @__PURE__ */ jsxs17(Fragment12, { children: [
19018
+ openLicenseDetails && /* @__PURE__ */ jsxs17(Fragment13, { children: [
18541
19019
  /* @__PURE__ */ jsx37(ToolbarDivider, {}),
18542
19020
  /* @__PURE__ */ jsx37(TextButton, { onClick: openLicenseDetails, children: STRINGS.license })
18543
19021
  ] })
@@ -18547,9 +19025,9 @@ var Footer = ({ openLicenseDetails }) => {
18547
19025
  };
18548
19026
 
18549
19027
  // ../../packages/sigil/dist/frontend/styling.hooks.js
18550
- import { useEffect as useEffect29 } from "react";
19028
+ import { useEffect as useEffect30 } from "react";
18551
19029
  var useRootHintVariables = (color) => {
18552
- useEffect29(() => {
19030
+ useEffect30(() => {
18553
19031
  const root = document.querySelector(".arcane-theme-root");
18554
19032
  if (!root) return;
18555
19033
  Object.entries(cssHintColorVariables(color)).forEach(([key, value]) => {
@@ -18559,18 +19037,18 @@ var useRootHintVariables = (color) => {
18559
19037
  };
18560
19038
 
18561
19039
  // src/components/frontend/toolbox/core/layout.tsx
18562
- import { Fragment as Fragment13, jsx as jsx38, jsxs as jsxs18 } from "react/jsx-runtime";
19040
+ import { Fragment as Fragment14, jsx as jsx38, jsxs as jsxs18 } from "react/jsx-runtime";
18563
19041
  var Layout = ({
18564
19042
  modes,
18565
19043
  children,
18566
19044
  licenseMode,
18567
19045
  footer
18568
19046
  }) => {
18569
- const [windowMode, setWindowMode] = useState29(null);
19047
+ const [windowMode, setWindowMode] = useState30(null);
18570
19048
  const { connection, reconnect } = useContext14(StageContext);
18571
19049
  const { preferences } = useBrowserPreferences();
18572
19050
  useRootHintVariables(preferences.color);
18573
- return /* @__PURE__ */ jsxs18("div", { className: "flex h-screen flex-col", children: [
19051
+ return /* @__PURE__ */ jsxs18("div", { className: "flex h-dvh flex-col", children: [
18574
19052
  /* @__PURE__ */ jsx38(ToolbarWrapper, { children: /* @__PURE__ */ jsxs18(ToolbarRow, { children: [
18575
19053
  /* @__PURE__ */ jsx38(
18576
19054
  "div",
@@ -18579,7 +19057,7 @@ var Layout = ({
18579
19057
  children: /* @__PURE__ */ jsx38("span", { className: "font-bold text-hint-gradient", children: STRINGS.title })
18580
19058
  }
18581
19059
  ),
18582
- modes && /* @__PURE__ */ jsxs18(Fragment13, { children: [
19060
+ modes && /* @__PURE__ */ jsxs18(Fragment14, { children: [
18583
19061
  /* @__PURE__ */ jsx38(ToolbarDivider, {}),
18584
19062
  Object.entries(modes).map(([key, { icon, title }]) => /* @__PURE__ */ jsx38(
18585
19063
  ControlButton,
@@ -18644,7 +19122,7 @@ var License = ({ license, setWindowMode }) => {
18644
19122
  };
18645
19123
  var LicenseGate = ({ info }) => {
18646
19124
  const { sendMessage } = useContext15(StageContext);
18647
- const acceptLicense = useCallback18(() => {
19125
+ const acceptLicense = useCallback19(() => {
18648
19126
  sendMessage?.({
18649
19127
  type: "component-message",
18650
19128
  namespace: "timecode-toolbox",
@@ -18691,19 +19169,19 @@ var LicenseGate = ({ info }) => {
18691
19169
  };
18692
19170
 
18693
19171
  // src/components/frontend/toolbox/core/updates.tsx
18694
- import { useCallback as useCallback19, useEffect as useEffect30, useState as useState30 } from "react";
19172
+ import { useCallback as useCallback20, useEffect as useEffect31, useState as useState31 } from "react";
18695
19173
  import { jsx as jsx40, jsxs as jsxs20 } from "react/jsx-runtime";
18696
19174
  var UpdateBanner = () => {
18697
19175
  const { updates } = useApplicationState();
18698
19176
  const { version } = useSystemInformation();
18699
19177
  const { openExternalLink } = useBrowserContext();
18700
- const [displayState, setDisplayState] = useState30();
18701
- useEffect30(() => {
19178
+ const [displayState, setDisplayState] = useState31();
19179
+ useEffect31(() => {
18702
19180
  if (updates?.type !== "loading") {
18703
19181
  setDisplayState(updates);
18704
19182
  }
18705
19183
  }, [updates]);
18706
- const openDownloadLink = useCallback19(() => {
19184
+ const openDownloadLink = useCallback20(() => {
18707
19185
  if (displayState?.type === "updates-available" && displayState.response.downloadUrl) {
18708
19186
  openExternalLink(displayState.response.downloadUrl);
18709
19187
  }
@@ -18749,68 +19227,679 @@ var UpdateBanner = () => {
18749
19227
  return null;
18750
19228
  };
18751
19229
 
18752
- // src/components/frontend/toolbox/root.tsx
18753
- import { Fragment as Fragment14, jsx as jsx41, jsxs as jsxs21 } from "react/jsx-runtime";
18754
- var ToolboxRoot = ({ info }) => {
18755
- const { config } = info;
18756
- const { sendMessage, call } = useContext16(StageContext);
18757
- const [dialogMode, setDialogMode] = useState31(null);
18758
- const [assignToOutput, setAssignToOutput] = useState31(null);
18759
- useEffect31(() => {
18760
- if (assignToOutput) {
18761
- const onEscape = (e) => {
18762
- if (e.key === "Escape") {
18763
- setAssignToOutput(null);
18764
- }
18765
- };
18766
- window.addEventListener("keydown", onEscape);
18767
- return () => {
18768
- window.removeEventListener("keydown", onEscape);
18769
- };
18770
- }
18771
- }, [assignToOutput]);
18772
- const updateConfig = useCallback20(
18773
- (change) => {
18774
- const diff = diffJson(config, change(config));
18775
- sendMessage?.({
18776
- type: "component-message",
18777
- namespace: "timecode-toolbox",
18778
- component: "toolbox-root",
18779
- componentKey: info.key,
18780
- action: "update-config",
18781
- diff
18782
- });
18783
- },
18784
- [sendMessage, info.key, config]
18785
- );
18786
- const configContext = useMemo15(
18787
- () => ({
18788
- config,
18789
- updateConfig
18790
- }),
18791
- [config, updateConfig]
18792
- );
18793
- const closeDialog = useCallback20(() => setDialogMode(null), []);
18794
- const getNetworkInterfaces = useCallback20(async () => {
18795
- if (!call) {
18796
- throw new Error("No call function available");
18797
- }
18798
- return call({
18799
- namespace: "timecode-toolbox",
18800
- type: "component-call",
18801
- componentKey: info.key,
18802
- action: "toolbox-root-get-network-interfaces"
18803
- });
18804
- }, [call, info.key]);
18805
- const networkContextValue = useMemo15(() => {
18806
- return {
18807
- getNetworkInterfaces
18808
- };
18809
- }, [getNetworkInterfaces]);
18810
- const assignToOutputCallback = useMemo15(() => {
18811
- if (!assignToOutput) {
18812
- return null;
18813
- }
19230
+ // ../../packages/sigil/src/frontend/controls/dialogs.tsx
19231
+ import { forwardRef as forwardRef20 } from "react";
19232
+
19233
+ // ../../packages/sigil/src/frontend/dialogs.tsx
19234
+ import {
19235
+ createContext as createContext11,
19236
+ forwardRef as forwardRef19,
19237
+ useEffect as useEffect33,
19238
+ useState as useState35
19239
+ } from "react";
19240
+
19241
+ // ../../packages/sigil/src/frontend/controls/buttons.tsx
19242
+ import {
19243
+ forwardRef as forwardRef17
19244
+ } from "react";
19245
+
19246
+ // ../../packages/sigil/src/frontend/styling.ts
19247
+ var SIGIL_COLOR2 = external_exports.enum([
19248
+ "purple",
19249
+ "blue",
19250
+ "teal",
19251
+ "red",
19252
+ "green",
19253
+ "yellow",
19254
+ "brown",
19255
+ "orange",
19256
+ "gray"
19257
+ ]);
19258
+ function cnd2(condition, truthyClassName, falseyClassName) {
19259
+ return condition ? truthyClassName : falseyClassName;
19260
+ }
19261
+
19262
+ // ../../packages/sigil/src/frontend/tooltip.tsx
19263
+ import {
19264
+ createContext as createContext10,
19265
+ forwardRef as forwardRef16,
19266
+ useContext as useContext16,
19267
+ useState as useState33
19268
+ } from "react";
19269
+ import { Fragment as Fragment15, jsx as jsx41, jsxs as jsxs21 } from "react/jsx-runtime";
19270
+ var Content4 = forwardRef16(({ className, ...props }, ref) => /* @__PURE__ */ jsx41(
19271
+ dist_exports4.Content,
19272
+ {
19273
+ ...props,
19274
+ ref,
19275
+ className: cn(
19276
+ `
19277
+ relative z-sigil-tooltip rounded-sigil-control
19278
+ bg-sigil-usage-hint-background px-1 py-0.5 leading-[1.5]
19279
+ text-sigil-usage-hint-text shadow-sigil-box
19280
+ `,
19281
+ className
19282
+ )
19283
+ }
19284
+ ));
19285
+ Content4.displayName = "Content";
19286
+ var Arrow4 = forwardRef16(({ className, ...props }, ref) => /* @__PURE__ */ jsx41(
19287
+ dist_exports4.Arrow,
19288
+ {
19289
+ ...props,
19290
+ ref,
19291
+ className: cn(
19292
+ "fill-sigil-usage-hint-background drop-shadow-sigil-tooltip-arrow",
19293
+ className
19294
+ )
19295
+ }
19296
+ ));
19297
+ Arrow4.displayName = "Arrow";
19298
+ var TooltipBoundaryContext2 = createContext10(null);
19299
+ var TooltipWrapper2 = ({
19300
+ tooltip,
19301
+ children,
19302
+ side = "top"
19303
+ }) => {
19304
+ const boundary = useContext16(TooltipBoundaryContext2);
19305
+ if (!tooltip) {
19306
+ return children;
19307
+ }
19308
+ return /* @__PURE__ */ jsxs21(dist_exports4.Root, { children: [
19309
+ /* @__PURE__ */ jsx41(dist_exports4.Trigger, { asChild: true, children }),
19310
+ /* @__PURE__ */ jsxs21(
19311
+ Content4,
19312
+ {
19313
+ side,
19314
+ align: "center",
19315
+ sideOffset: 5,
19316
+ alignOffset: 0,
19317
+ collisionBoundary: boundary ? [boundary] : [],
19318
+ collisionPadding: 10,
19319
+ children: [
19320
+ /* @__PURE__ */ jsx41(Arrow4, {}),
19321
+ tooltip
19322
+ ]
19323
+ }
19324
+ )
19325
+ ] });
19326
+ };
19327
+
19328
+ // ../../packages/sigil/src/frontend/input.ts
19329
+ import { useMemo as useMemo18, useState as useState34 } from "react";
19330
+ var usePressable3 = (click) => {
19331
+ const [touching, setTouching] = useState34(false);
19332
+ return {
19333
+ touching,
19334
+ handlers: {
19335
+ onClick: click,
19336
+ onTouchStart: () => {
19337
+ setTouching(true);
19338
+ },
19339
+ onTouchMove: () => {
19340
+ setTouching(false);
19341
+ },
19342
+ onTouchEnd: (event) => {
19343
+ if (touching) {
19344
+ event.preventDefault();
19345
+ setTouching(false);
19346
+ click(event);
19347
+ }
19348
+ }
19349
+ }
19350
+ };
19351
+ };
19352
+ var useLongPressable3 = ({
19353
+ onPress,
19354
+ onRelease
19355
+ }) => {
19356
+ const [touching, setTouching] = useState34(false);
19357
+ return useMemo18(
19358
+ () => ({
19359
+ touching,
19360
+ handlers: {
19361
+ onTouchStart: () => {
19362
+ setTouching(true);
19363
+ onPress();
19364
+ },
19365
+ onMouseDown: () => {
19366
+ setTouching(true);
19367
+ onPress();
19368
+ },
19369
+ onMouseUp: () => {
19370
+ setTouching(false);
19371
+ onRelease();
19372
+ },
19373
+ onTouchMove: () => {
19374
+ setTouching(false);
19375
+ onRelease();
19376
+ },
19377
+ onTouchEnd: (event) => {
19378
+ if (touching) {
19379
+ event.preventDefault();
19380
+ setTouching(false);
19381
+ onRelease();
19382
+ }
19383
+ }
19384
+ }
19385
+ }),
19386
+ [touching, onRelease, onPress]
19387
+ );
19388
+ };
19389
+
19390
+ // ../../packages/sigil/src/frontend/controls/utils.ts
19391
+ var clsControlPosition2 = (position) => {
19392
+ if (!position) return void 0;
19393
+ switch (position) {
19394
+ case "row":
19395
+ return "control-grid-pos-row";
19396
+ case "label":
19397
+ return "control-grid-pos-label";
19398
+ case "first":
19399
+ return "control-grid-pos-first";
19400
+ case "second":
19401
+ return "control-grid-pos-second";
19402
+ case "both":
19403
+ return "control-grid-pos-both";
19404
+ case "all":
19405
+ return "control-grid-pos-all";
19406
+ case "extra":
19407
+ return "control-grid-pos-extra";
19408
+ }
19409
+ };
19410
+ var clsControlSubgridPosition2 = (position, subgrid) => {
19411
+ if (!subgrid) return void 0;
19412
+ switch (position) {
19413
+ case "label":
19414
+ return "col-[1/span_1]";
19415
+ case "first":
19416
+ return "col-[2/span_1]";
19417
+ case "second":
19418
+ return "col-[3/span_1]";
19419
+ }
19420
+ };
19421
+
19422
+ // ../../packages/sigil/src/frontend/controls/buttons.tsx
19423
+ import { jsx as jsx45, jsxs as jsxs24 } from "react/jsx-runtime";
19424
+ var clsControlButton2 = ({
19425
+ variant,
19426
+ active,
19427
+ touching,
19428
+ position,
19429
+ className,
19430
+ primary,
19431
+ destructive
19432
+ }) => cn(
19433
+ `sigil-control-button`,
19434
+ cnd2(variant === "border", `sigil-control-button-variant-border`),
19435
+ cnd2(variant === "large", `sigil-control-button-variant-large`),
19436
+ cnd2(variant === "properties", `sigil-control-button-variant-properties`),
19437
+ cnd2(variant === "table-row", `sigil-control-button-variant-table-row`),
19438
+ cnd2(variant === "toolbar", `sigil-control-button-variant-toolbar`),
19439
+ cnd2(variant === "titlebar", `sigil-control-button-variant-titlebar`),
19440
+ cnd2(touching, `sigil-control-button-touching`),
19441
+ cnd2(active, `sigil-control-button-active`),
19442
+ cnd2(touching && active, `sigil-control-button-active-touching`),
19443
+ cnd2(primary, `sigil-control-button-primary`),
19444
+ cnd2(destructive, `sigil-control-button-destructive`),
19445
+ clsControlPosition2(position),
19446
+ className
19447
+ );
19448
+ var ControlButtonFrame2 = forwardRef17(
19449
+ ({
19450
+ children,
19451
+ className,
19452
+ type,
19453
+ variant = "toolbar",
19454
+ icon,
19455
+ active,
19456
+ touching,
19457
+ disabled,
19458
+ title,
19459
+ tooltipSide,
19460
+ position,
19461
+ primary,
19462
+ destructive,
19463
+ ...props
19464
+ }, ref) => {
19465
+ const btn = /* @__PURE__ */ jsx45(
19466
+ "button",
19467
+ {
19468
+ ...props,
19469
+ ref,
19470
+ type: type ?? "button",
19471
+ disabled,
19472
+ className: clsControlButton2({
19473
+ variant,
19474
+ active,
19475
+ touching,
19476
+ position,
19477
+ primary,
19478
+ destructive,
19479
+ className
19480
+ }),
19481
+ children: /* @__PURE__ */ jsxs24("span", { children: [
19482
+ icon && /* @__PURE__ */ jsx45(
19483
+ Icon2,
19484
+ {
19485
+ icon,
19486
+ className: cn(cnd2(children, "text-[120%]", "text-[150%]"))
19487
+ }
19488
+ ),
19489
+ children
19490
+ ] })
19491
+ }
19492
+ );
19493
+ if (!title) return btn;
19494
+ return /* @__PURE__ */ jsx45(TooltipWrapper2, { tooltip: title, side: tooltipSide, children: btn });
19495
+ }
19496
+ );
19497
+ ControlButtonFrame2.displayName = "ControlButtonFrame";
19498
+ var ControlButton2 = forwardRef17(
19499
+ ({ onClick, disabled, ...props }, ref) => {
19500
+ const { handlers, touching } = usePressable3(onClick);
19501
+ return /* @__PURE__ */ jsx45(
19502
+ ControlButtonFrame2,
19503
+ {
19504
+ ...props,
19505
+ ref,
19506
+ disabled,
19507
+ touching,
19508
+ ...!disabled ? handlers : {}
19509
+ }
19510
+ );
19511
+ }
19512
+ );
19513
+ ControlButton2.displayName = "ControlButton";
19514
+ var CheckboxControlButton2 = forwardRef17(({ active, label, ...props }, ref) => /* @__PURE__ */ jsxs24(ControlButton2, { ...props, ref, active, children: [
19515
+ /* @__PURE__ */ jsx45(Icon2, { icon: active ? "check_box" : "check_box_outline_blank" }),
19516
+ label
19517
+ ] }));
19518
+ CheckboxControlButton2.displayName = "CheckboxControlButton";
19519
+ var LongPressableControlButton2 = forwardRef17(({ active, disabled, onPress, onRelease, ...props }, ref) => {
19520
+ const { handlers, touching } = useLongPressable3({ onPress, onRelease });
19521
+ return /* @__PURE__ */ jsx45(
19522
+ ControlButtonFrame2,
19523
+ {
19524
+ ...props,
19525
+ ref,
19526
+ active: active || touching,
19527
+ disabled,
19528
+ ...!disabled ? handlers : {}
19529
+ }
19530
+ );
19531
+ });
19532
+ LongPressableControlButton2.displayName = "LongPressableControlButton";
19533
+ var ControlButtonGroup2 = forwardRef17(({ children, className, position, ...props }, ref) => /* @__PURE__ */ jsx45(
19534
+ "div",
19535
+ {
19536
+ ...props,
19537
+ ref,
19538
+ className: cn(
19539
+ `
19540
+ flex items-stretch gap-sigil-control-gap
19541
+ [&>button]:grow
19542
+ `,
19543
+ clsControlPosition2(position),
19544
+ className
19545
+ ),
19546
+ children
19547
+ }
19548
+ ));
19549
+ ControlButtonGroup2.displayName = "ControlButtonGroup";
19550
+
19551
+ // ../../packages/sigil/src/frontend/controls/content.tsx
19552
+ import { forwardRef as forwardRef18 } from "react";
19553
+ import { jsx as jsx46 } from "react/jsx-runtime";
19554
+ var ControlParagraph2 = forwardRef18(({ className, mode, position = "all", ...props }, ref) => /* @__PURE__ */ jsx46(
19555
+ "p",
19556
+ {
19557
+ ...props,
19558
+ ref,
19559
+ className: cn(
19560
+ "border border-transparent",
19561
+ clsControlPosition2(position),
19562
+ cnd2(
19563
+ mode === "success",
19564
+ `
19565
+ border-sigil-usage-green-dimmed-border
19566
+ bg-sigil-usage-green-dimmed-background p-1 text-sigil-usage-green-text
19567
+ `
19568
+ ),
19569
+ cnd2(
19570
+ mode === "warning",
19571
+ `
19572
+ border-sigil-usage-yellow-dimmed-border
19573
+ bg-sigil-usage-yellow-dimmed-background p-1
19574
+ text-sigil-usage-yellow-text
19575
+ `
19576
+ ),
19577
+ cnd2(
19578
+ mode === "error",
19579
+ `
19580
+ border-sigil-usage-red-dimmed-border
19581
+ bg-sigil-usage-red-dimmed-background p-1 text-sigil-usage-red-text
19582
+ `
19583
+ ),
19584
+ className
19585
+ )
19586
+ }
19587
+ ));
19588
+ ControlParagraph2.displayName = "ControlParagraph";
19589
+ var ControlLabel2 = forwardRef18(
19590
+ ({ className, disabled, nonMicro, position = "label", subgrid, ...props }, ref) => {
19591
+ return /* @__PURE__ */ jsx46(
19592
+ "label",
19593
+ {
19594
+ ...props,
19595
+ ref,
19596
+ className: cn(
19597
+ "flex items-center justify-end gap-0.6 p-0.6",
19598
+ clsControlPosition2(position),
19599
+ cnd2(nonMicro, "max-[550px]:hidden"),
19600
+ cnd2(disabled, "opacity-50"),
19601
+ clsControlSubgridPosition2(position, subgrid),
19602
+ className
19603
+ )
19604
+ }
19605
+ );
19606
+ }
19607
+ );
19608
+ ControlLabel2.displayName = "ControlLabel";
19609
+ var ControlDetails2 = forwardRef18(
19610
+ ({ align, className, position = "all", ...props }, ref) => /* @__PURE__ */ jsx46(
19611
+ "div",
19612
+ {
19613
+ ...props,
19614
+ ref,
19615
+ className: cn(
19616
+ "flex items-center px-0.3 text-sigil-foreground-muted",
19617
+ clsControlPosition2(position),
19618
+ cnd2(align === "start", "justify-start"),
19619
+ cnd2(align === "center", "justify-center"),
19620
+ cnd2(align === "end", "justify-end"),
19621
+ className
19622
+ )
19623
+ }
19624
+ )
19625
+ );
19626
+ ControlDetails2.displayName = "ControlDetails";
19627
+ var InputSpanningTitle2 = forwardRef18(({ className, position = "row", ...props }, ref) => /* @__PURE__ */ jsx46(
19628
+ "div",
19629
+ {
19630
+ ...props,
19631
+ ref,
19632
+ className: cn(
19633
+ "truncate p-0.6 text-center text-[0.8rem] font-bold",
19634
+ clsControlPosition2(position),
19635
+ className
19636
+ )
19637
+ }
19638
+ ));
19639
+ InputSpanningTitle2.displayName = "InputSpanningTitle";
19640
+
19641
+ // ../../packages/sigil/src/frontend/controls/input.tsx
19642
+ import {
19643
+ useCallback as useCallback21,
19644
+ useEffect as useEffect32,
19645
+ useRef as useRef18
19646
+ } from "react";
19647
+ import { jsx as jsx47 } from "react/jsx-runtime";
19648
+
19649
+ // ../../packages/sigil/src/frontend/controls/select.tsx
19650
+ import { useCallback as useCallback23 } from "react";
19651
+ import { Fragment as Fragment16, jsx as jsx48, jsxs as jsxs25 } from "react/jsx-runtime";
19652
+ var COLOR_OPTIONS2 = {
19653
+ red: { label: "Red", value: "red", color: "red" },
19654
+ blue: { label: "Blue", value: "blue", color: "blue" },
19655
+ teal: { label: "Teal", value: "teal", color: "teal" },
19656
+ green: { label: "Green", value: "green", color: "green" },
19657
+ yellow: { label: "Yellow", value: "yellow", color: "yellow" },
19658
+ purple: { label: "Purple", value: "purple", color: "purple" },
19659
+ orange: { label: "Orange", value: "orange", color: "orange" },
19660
+ brown: { label: "Brown", value: "brown", color: "brown" },
19661
+ gray: { label: "Gray", value: "gray", color: "gray" }
19662
+ };
19663
+ var COLOR_OPTIONS_ARRAY2 = Object.values(COLOR_OPTIONS2);
19664
+
19665
+ // ../../packages/sigil/src/frontend/dialogs.tsx
19666
+ import { Fragment as Fragment17, jsx as jsx49, jsxs as jsxs26 } from "react/jsx-runtime";
19667
+ var isDarkDialog2 = (variant) => variant === "dark" || variant === "dark-compact";
19668
+ var DialogContext2 = createContext11({
19669
+ createDialog: () => {
19670
+ throw new Error("DialogContext not provided");
19671
+ },
19672
+ displayMessage: () => {
19673
+ throw new Error("DialogContext not provided");
19674
+ },
19675
+ displayError: () => {
19676
+ throw new Error("DialogContext not provided");
19677
+ }
19678
+ });
19679
+ var DialogTitle2 = forwardRef19(
19680
+ ({ className, variant = "light", ...props }, ref) => /* @__PURE__ */ jsx49(
19681
+ "div",
19682
+ {
19683
+ ...props,
19684
+ ref,
19685
+ className: cn(
19686
+ `
19687
+ flex items-center justify-center gap-0.6 border-b border-sigil-border
19688
+ p-arcane font-bold
19689
+ `,
19690
+ cnd2(isDarkDialog2(variant), "bg-sigil-bg-light", "bg-sigil-bg-dark"),
19691
+ className
19692
+ )
19693
+ }
19694
+ )
19695
+ );
19696
+ DialogTitle2.displayName = "DialogTitle";
19697
+ var DialogButtons2 = forwardRef19(({ className, ...props }, ref) => /* @__PURE__ */ jsx49(
19698
+ "div",
19699
+ {
19700
+ ...props,
19701
+ ref,
19702
+ className: cn("mt-arcane flex justify-end gap-0.6", className)
19703
+ }
19704
+ ));
19705
+ DialogButtons2.displayName = "DialogButtons";
19706
+ var Dialog2 = ({
19707
+ children,
19708
+ dialogClosed,
19709
+ closable = true,
19710
+ title,
19711
+ variant = "light"
19712
+ }) => {
19713
+ const [dialogRef, setDialogRef] = useState35(null);
19714
+ useEffect33(() => {
19715
+ if (!dialogRef) return;
19716
+ dialogRef.showModal();
19717
+ }, [dialogRef]);
19718
+ const close = () => dialogRef?.close();
19719
+ const onClose = (event) => {
19720
+ if (closable) {
19721
+ dialogClosed();
19722
+ return;
19723
+ }
19724
+ event.preventDefault();
19725
+ event.currentTarget.showModal();
19726
+ };
19727
+ const onMouseDown = (event) => {
19728
+ if (event.target === event.currentTarget) {
19729
+ close();
19730
+ }
19731
+ };
19732
+ return /* @__PURE__ */ jsx49(
19733
+ "dialog",
19734
+ {
19735
+ ref: setDialogRef,
19736
+ onMouseDown,
19737
+ onClose,
19738
+ className: cn(
19739
+ `
19740
+ max-w-[80vw] min-w-[10vw] border-none p-0 text-sigil-dialog-foreground
19741
+ outline-none backdrop-sigil-dialog
19742
+ `,
19743
+ cnd2(isDarkDialog2(variant), "bg-sigil-bg-dark", "bg-sigil-bg-light")
19744
+ ),
19745
+ children: /* @__PURE__ */ jsxs26("div", { className: "border border-sigil-border", children: [
19746
+ title && /* @__PURE__ */ jsx49(DialogTitle2, { variant, children: title }),
19747
+ /* @__PURE__ */ jsx49(
19748
+ "div",
19749
+ {
19750
+ className: cn(
19751
+ "text-sigil-foreground",
19752
+ cnd2(
19753
+ variant === "light-compact" || variant === "dark-compact",
19754
+ "p-0"
19755
+ ),
19756
+ cnd2(
19757
+ variant !== "light-compact" && variant !== "dark-compact",
19758
+ "p-arcane"
19759
+ )
19760
+ ),
19761
+ children
19762
+ }
19763
+ )
19764
+ ] })
19765
+ }
19766
+ );
19767
+ };
19768
+
19769
+ // ../../packages/sigil/src/frontend/controls/dialogs.tsx
19770
+ import { jsx as jsx50 } from "react/jsx-runtime";
19771
+ var ControlDialogButtons2 = forwardRef20(({ children, className, ...props }, ref) => /* @__PURE__ */ jsx50(
19772
+ "div",
19773
+ {
19774
+ ref,
19775
+ className: cn(
19776
+ `
19777
+ flex items-stretch gap-1
19778
+ [&>button]:grow
19779
+ `,
19780
+ clsControlPosition2("row"),
19781
+ className
19782
+ ),
19783
+ ...props,
19784
+ children
19785
+ }
19786
+ ));
19787
+ var ControlDialog2 = ({
19788
+ children,
19789
+ large,
19790
+ ...props
19791
+ }) => /* @__PURE__ */ jsx50(Dialog2, { ...props, variant: "dark", children: /* @__PURE__ */ jsx50(
19792
+ "div",
19793
+ {
19794
+ className: cn(
19795
+ "gap-1 bg-sigil-bg-dark select-none",
19796
+ cnd2(large, "control-grid-large", "control-grid")
19797
+ ),
19798
+ children
19799
+ }
19800
+ ) });
19801
+
19802
+ // src/components/frontend/toolbox/root.tsx
19803
+ import { Fragment as Fragment18, jsx as jsx51, jsxs as jsxs27 } from "react/jsx-runtime";
19804
+ var DeleteConfirmationDialog = ({ dialogMode, setDialogMode }) => {
19805
+ const { updateConfig } = useContext17(ConfigContext);
19806
+ const deleteTarget = useCallback24(() => {
19807
+ updateConfig((current) => {
19808
+ return {
19809
+ ...current,
19810
+ [dialogMode.section.type]: Object.fromEntries(
19811
+ Object.entries(current[dialogMode.section.type]).filter(
19812
+ ([uuid]) => uuid !== dialogMode.target.uuid
19813
+ )
19814
+ )
19815
+ };
19816
+ });
19817
+ setDialogMode(null);
19818
+ }, [updateConfig, dialogMode, setDialogMode]);
19819
+ return /* @__PURE__ */ jsxs27(
19820
+ ControlDialog2,
19821
+ {
19822
+ dialogClosed: () => setDialogMode(null),
19823
+ title: STRINGS[dialogMode.section.type].deleteDialog,
19824
+ children: [
19825
+ /* @__PURE__ */ jsx51(ControlDetails, { position: "row", children: STRINGS[dialogMode.section.type].deleteDialogDetails }),
19826
+ /* @__PURE__ */ jsxs27(ControlDialogButtons2, { children: [
19827
+ /* @__PURE__ */ jsx51(ControlButton, { onClick: () => setDialogMode(null), variant: "large", children: "Cancel" }),
19828
+ /* @__PURE__ */ jsx51(
19829
+ ControlButton,
19830
+ {
19831
+ onClick: deleteTarget,
19832
+ variant: "large",
19833
+ destructive: true,
19834
+ icon: "delete",
19835
+ children: "Delete"
19836
+ }
19837
+ )
19838
+ ] })
19839
+ ]
19840
+ }
19841
+ );
19842
+ };
19843
+ var ToolboxRoot = ({ info }) => {
19844
+ const { config } = info;
19845
+ const { sendMessage, call } = useContext17(StageContext);
19846
+ const [dialogMode, setDialogMode] = useState36(null);
19847
+ const [assignToOutput, setAssignToOutput] = useState36(null);
19848
+ useEffect34(() => {
19849
+ if (assignToOutput) {
19850
+ const onEscape = (e) => {
19851
+ if (e.key === "Escape") {
19852
+ setAssignToOutput(null);
19853
+ }
19854
+ };
19855
+ window.addEventListener("keydown", onEscape);
19856
+ return () => {
19857
+ window.removeEventListener("keydown", onEscape);
19858
+ };
19859
+ }
19860
+ }, [assignToOutput]);
19861
+ const updateConfig = useCallback24(
19862
+ (change) => {
19863
+ const diff = diffJson(config, change(config));
19864
+ sendMessage?.({
19865
+ type: "component-message",
19866
+ namespace: "timecode-toolbox",
19867
+ component: "toolbox-root",
19868
+ componentKey: info.key,
19869
+ action: "update-config",
19870
+ diff
19871
+ });
19872
+ },
19873
+ [sendMessage, info.key, config]
19874
+ );
19875
+ const configContext = useMemo19(
19876
+ () => ({
19877
+ config,
19878
+ network: info.network,
19879
+ updateConfig
19880
+ }),
19881
+ [config, info.network, updateConfig]
19882
+ );
19883
+ const getNetworkInterfaces = useCallback24(async () => {
19884
+ if (!call) {
19885
+ throw new Error("No call function available");
19886
+ }
19887
+ return call({
19888
+ namespace: "timecode-toolbox",
19889
+ type: "component-call",
19890
+ componentKey: info.key,
19891
+ action: "toolbox-root-get-network-interfaces"
19892
+ });
19893
+ }, [call, info.key]);
19894
+ const networkContextValue = useMemo19(() => {
19895
+ return {
19896
+ getNetworkInterfaces
19897
+ };
19898
+ }, [getNetworkInterfaces]);
19899
+ const assignToOutputCallback = useMemo19(() => {
19900
+ if (!assignToOutput) {
19901
+ return null;
19902
+ }
18814
19903
  const outputUuid = assignToOutput;
18815
19904
  return (id) => {
18816
19905
  updateConfig((current) => {
@@ -18832,7 +19921,7 @@ var ToolboxRoot = ({ info }) => {
18832
19921
  setAssignToOutput(null);
18833
19922
  };
18834
19923
  }, [assignToOutput, updateConfig]);
18835
- const callHandler = useCallback20(
19924
+ const callHandler = useCallback24(
18836
19925
  async ({ path, handler, args }) => {
18837
19926
  if (!call) {
18838
19927
  throw new Error("No call function available");
@@ -18849,26 +19938,26 @@ var ToolboxRoot = ({ info }) => {
18849
19938
  },
18850
19939
  [call, info.key]
18851
19940
  );
18852
- const handlers = useMemo15(
19941
+ const handlers = useMemo19(
18853
19942
  () => ({
18854
19943
  handlers: info.handlers,
18855
19944
  callHandler
18856
19945
  }),
18857
19946
  [info.handlers, callHandler]
18858
19947
  );
18859
- const windowedTimecodeId = useMemo15(
19948
+ const windowedTimecodeId = useMemo19(
18860
19949
  () => getFragmentValue("tc", TIMECODE_INSTANCE_ID),
18861
19950
  []
18862
19951
  );
18863
- const root = useMemo15(
18864
- () => windowedTimecodeId ? /* @__PURE__ */ jsx41(Layout, { modes: null, children: /* @__PURE__ */ jsx41(FullscreenTimecodeDisplay, { id: windowedTimecodeId }) }) : /* @__PURE__ */ jsxs21(Fragment14, { children: [
18865
- /* @__PURE__ */ jsx41(
19952
+ const root = useMemo19(
19953
+ () => windowedTimecodeId ? /* @__PURE__ */ jsx51(Layout, { modes: null, children: /* @__PURE__ */ jsx51(FullscreenTimecodeDisplay, { id: windowedTimecodeId }) }) : /* @__PURE__ */ jsxs27(Fragment18, { children: [
19954
+ /* @__PURE__ */ jsx51(
18866
19955
  Layout,
18867
19956
  {
18868
19957
  footer: true,
18869
19958
  modes: {
18870
19959
  license: {
18871
- child: (setWindowMode) => /* @__PURE__ */ jsx41(
19960
+ child: (setWindowMode) => /* @__PURE__ */ jsx51(
18872
19961
  License,
18873
19962
  {
18874
19963
  license: info.license,
@@ -18879,39 +19968,39 @@ var ToolboxRoot = ({ info }) => {
18879
19968
  title: STRINGS.license
18880
19969
  },
18881
19970
  settings: {
18882
- child: (setWindowMode) => /* @__PURE__ */ jsx41(Settings, { setWindowMode }),
19971
+ child: (setWindowMode) => /* @__PURE__ */ jsx51(Settings, { setWindowMode }),
18883
19972
  icon: "settings",
18884
19973
  title: STRINGS.settings.title
18885
19974
  },
18886
19975
  debug: {
18887
- child: () => /* @__PURE__ */ jsx41(Debugger, { title: STRINGS.debugger, className: "size-full" }),
19976
+ child: () => /* @__PURE__ */ jsx51(Debugger, { title: STRINGS.debugger, className: "size-full" }),
18888
19977
  icon: "bug_report",
18889
19978
  title: STRINGS.debugger
18890
19979
  }
18891
19980
  },
18892
19981
  licenseMode: "license",
18893
- children: /* @__PURE__ */ jsxs21(Fragment14, { children: [
18894
- /* @__PURE__ */ jsx41(UpdateBanner, {}),
18895
- /* @__PURE__ */ jsxs21(
19982
+ children: /* @__PURE__ */ jsxs27(Fragment18, { children: [
19983
+ /* @__PURE__ */ jsx51(UpdateBanner, {}),
19984
+ /* @__PURE__ */ jsxs27(
18896
19985
  "div",
18897
19986
  {
18898
19987
  className: "\n flex h-0 grow flex-col gap-px overflow-y-auto bg-sigil-border\n scrollbar-sigil\n ",
18899
19988
  children: [
18900
- /* @__PURE__ */ jsx41(
19989
+ /* @__PURE__ */ jsx51(
18901
19990
  InputsSection,
18902
19991
  {
18903
19992
  setDialogMode,
18904
19993
  assignToOutput: assignToOutputCallback
18905
19994
  }
18906
19995
  ),
18907
- /* @__PURE__ */ jsx41(
19996
+ /* @__PURE__ */ jsx51(
18908
19997
  GeneratorsSection,
18909
19998
  {
18910
19999
  setDialogMode,
18911
20000
  assignToOutput: assignToOutputCallback
18912
20001
  }
18913
20002
  ),
18914
- /* @__PURE__ */ jsx41(
20003
+ /* @__PURE__ */ jsx51(
18915
20004
  OutputsSection,
18916
20005
  {
18917
20006
  setDialogMode,
@@ -18925,45 +20014,52 @@ var ToolboxRoot = ({ info }) => {
18925
20014
  ] })
18926
20015
  }
18927
20016
  ),
18928
- dialogMode?.section.type === "inputs" && /* @__PURE__ */ jsx41(
18929
- InputSettingsDialog,
20017
+ isDeleteDialogMode(dialogMode) ? /* @__PURE__ */ jsx51(
20018
+ DeleteConfirmationDialog,
18930
20019
  {
18931
- close: closeDialog,
18932
- input: dialogMode.section.input,
18933
- target: dialogMode.target
20020
+ dialogMode,
20021
+ setDialogMode
18934
20022
  }
18935
- ),
18936
- dialogMode?.section.type === "generators" && /* @__PURE__ */ jsx41(
18937
- GeneratorSettingsDialog,
18938
- {
18939
- close: closeDialog,
18940
- generator: dialogMode.section.generator,
18941
- target: dialogMode.target
18942
- }
18943
- ),
18944
- dialogMode?.section.type === "outputs" && /* @__PURE__ */ jsx41(
18945
- OutputSettingsDialog,
18946
- {
18947
- close: closeDialog,
18948
- output: dialogMode.section.output,
18949
- target: dialogMode.target
18950
- }
18951
- )
20023
+ ) : /* @__PURE__ */ jsxs27(Fragment18, { children: [
20024
+ dialogMode?.section.type === "inputs" && /* @__PURE__ */ jsx51(
20025
+ InputSettingsDialog,
20026
+ {
20027
+ setDialogMode,
20028
+ input: dialogMode.section.input,
20029
+ target: dialogMode.target
20030
+ }
20031
+ ),
20032
+ dialogMode?.section.type === "generators" && /* @__PURE__ */ jsx51(
20033
+ GeneratorSettingsDialog,
20034
+ {
20035
+ setDialogMode,
20036
+ generator: dialogMode.section.generator,
20037
+ target: dialogMode.target
20038
+ }
20039
+ ),
20040
+ dialogMode?.section.type === "outputs" && /* @__PURE__ */ jsx51(
20041
+ OutputSettingsDialog,
20042
+ {
20043
+ setDialogMode,
20044
+ output: dialogMode.section.output,
20045
+ target: dialogMode.target
20046
+ }
20047
+ )
20048
+ ] })
18952
20049
  ] }),
18953
20050
  [
18954
20051
  assignToOutput,
18955
20052
  assignToOutputCallback,
18956
- closeDialog,
18957
20053
  dialogMode,
18958
20054
  windowedTimecodeId,
18959
20055
  info.license
18960
20056
  ]
18961
20057
  );
18962
- return /* @__PURE__ */ jsx41(ConfigContext.Provider, { value: configContext, children: /* @__PURE__ */ jsx41(NetworkContext.Provider, { value: networkContextValue, children: /* @__PURE__ */ jsx41(ApplicationStateContext.Provider, { value: info.state, children: /* @__PURE__ */ jsx41(ApplicationHandlersContext.Provider, { value: handlers, children: root }) }) }) });
20058
+ return /* @__PURE__ */ jsx51(ConfigContext.Provider, { value: configContext, children: /* @__PURE__ */ jsx51(NetworkContext.Provider, { value: networkContextValue, children: /* @__PURE__ */ jsx51(ApplicationStateContext.Provider, { value: info.state, children: /* @__PURE__ */ jsx51(ApplicationHandlersContext.Provider, { value: handlers, children: root }) }) }) });
18963
20059
  };
18964
20060
 
18965
20061
  // src/components/frontend/index.tsx
18966
- import { jsx as jsx45 } from "react/jsx-runtime";
20062
+ import { jsx as jsx54 } from "react/jsx-runtime";
18967
20063
  var timecodeToolboxFrontendComponents = () => ({
18968
20064
  namespace: NAMESPACE,
18969
20065
  render: (info) => {
@@ -18972,9 +20068,9 @@ var timecodeToolboxFrontendComponents = () => ({
18972
20068
  }
18973
20069
  switch (info.component) {
18974
20070
  case "toolbox-root":
18975
- return /* @__PURE__ */ jsx45(ToolboxRoot, { info });
20071
+ return /* @__PURE__ */ jsx54(ToolboxRoot, { info });
18976
20072
  case "license-gate":
18977
- return /* @__PURE__ */ jsx45(LicenseGate, { info });
20073
+ return /* @__PURE__ */ jsx54(LicenseGate, { info });
18978
20074
  }
18979
20075
  }
18980
20076
  });
@@ -18982,10 +20078,11 @@ var startTimecodeToolboxServerFrontend = (browser) => {
18982
20078
  startSigilFrontend({
18983
20079
  browser,
18984
20080
  appRenderers: [timecodeToolboxFrontendComponents()],
18985
- loadingState: () => /* @__PURE__ */ jsx45("div", { style: { width: "100%", textAlign: "center", padding: "2rem" }, children: "Loading Toolbox..." })
20081
+ loadingState: () => /* @__PURE__ */ jsx54("div", { style: { width: "100%", textAlign: "center", padding: "2rem" }, children: "Loading Toolbox..." })
18986
20082
  });
18987
20083
  };
18988
20084
  window.startTimecodeToolboxServerFrontend = startTimecodeToolboxServerFrontend;
20085
+ window.createBrowserMediaSession = createBrowserMediaSession;
18989
20086
  export {
18990
20087
  startTimecodeToolboxServerFrontend,
18991
20088
  timecodeToolboxFrontendComponents