@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.
package/dist/start.mjs CHANGED
@@ -35,6 +35,53 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
35
35
  mod
36
36
  ));
37
37
 
38
+ // ../../node_modules/.pnpm/escape-html@1.0.3/node_modules/escape-html/index.js
39
+ var require_escape_html = __commonJS({
40
+ "../../node_modules/.pnpm/escape-html@1.0.3/node_modules/escape-html/index.js"(exports, module) {
41
+ "use strict";
42
+ var matchHtmlRegExp = /["'&<>]/;
43
+ module.exports = escapeHtml;
44
+ function escapeHtml(string) {
45
+ var str = "" + string;
46
+ var match = matchHtmlRegExp.exec(str);
47
+ if (!match) {
48
+ return str;
49
+ }
50
+ var escape;
51
+ var html = "";
52
+ var index = 0;
53
+ var lastIndex = 0;
54
+ for (index = match.index; index < str.length; index++) {
55
+ switch (str.charCodeAt(index)) {
56
+ case 34:
57
+ escape = "&quot;";
58
+ break;
59
+ case 38:
60
+ escape = "&amp;";
61
+ break;
62
+ case 39:
63
+ escape = "&#39;";
64
+ break;
65
+ case 60:
66
+ escape = "&lt;";
67
+ break;
68
+ case 62:
69
+ escape = "&gt;";
70
+ break;
71
+ default:
72
+ continue;
73
+ }
74
+ if (lastIndex !== index) {
75
+ html += str.substring(lastIndex, index);
76
+ }
77
+ lastIndex = index + 1;
78
+ html += escape;
79
+ }
80
+ return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
81
+ }
82
+ }
83
+ });
84
+
38
85
  // ../../node_modules/.pnpm/lodash@4.17.23/node_modules/lodash/_listCacheClear.js
39
86
  var require_listCacheClear = __commonJS({
40
87
  "../../node_modules/.pnpm/lodash@4.17.23/node_modules/lodash/_listCacheClear.js"(exports, module) {
@@ -1649,6 +1696,7 @@ var SIGIL_NAMESPACE = "sigil";
1649
1696
  var isSigilComponentCall = (call, action) => call.namespace === SIGIL_NAMESPACE && call.action === action;
1650
1697
 
1651
1698
  // ../../packages/sigil/dist/index.js
1699
+ var import_escape_html = __toESM(require_escape_html(), 1);
1652
1700
  import { useEffect as useEffect2, useMemo, useState } from "react";
1653
1701
  import {
1654
1702
  BaseParent,
@@ -1867,6 +1915,26 @@ var runSigilApp = ({
1867
1915
  debug: upstreamLogger.debug.bind(upstreamLogger)
1868
1916
  },
1869
1917
  title,
1918
+ htmlPage: (context) => `
1919
+ <html>
1920
+ <head>
1921
+ <title>${(0, import_escape_html.default)(context.title)}</title>
1922
+ <meta charset="utf-8">
1923
+ <meta name="viewport" content="width=device-width, user-scalable=no" />
1924
+ <style type="text/css">
1925
+ @font-face {
1926
+ font-family: 'Material Symbols Outlined';
1927
+ font-style: normal;
1928
+ src: url(${context.coreAssets.materialSymbolsOutlined}) format('woff');
1929
+ }
1930
+ </style>
1931
+ ${context.coreAssets.entrypointCss ? `<link rel="stylesheet" href="${context.coreAssets.entrypointCss}" />` : ""}
1932
+ </head>
1933
+ <body>
1934
+ <div id="root"></div>
1935
+ <script type="text/javascript" src="${context.coreAssets.entrypointJs}"></script>
1936
+ </body>
1937
+ </html>`,
1870
1938
  ...toolkitOptions
1871
1939
  });
1872
1940
  toolkit.start({
@@ -2062,7 +2130,8 @@ var AppListenerManager = ({
2062
2130
  const resolvedConnectionDetails = {
2063
2131
  config,
2064
2132
  host,
2065
- port
2133
+ port,
2134
+ internal: !!config.interface && !!interfaces[config.interface]?.internal
2066
2135
  };
2067
2136
  try {
2068
2137
  const listener = await toolkit.listen({
@@ -2133,11 +2202,21 @@ var AppListenerManager = ({
2133
2202
  }, [appRegistration, listenerConfig, logger2, toolkit, updateReactState]);
2134
2203
  useEffect3(() => {
2135
2204
  for (const state of Object.values(listenerState)) {
2205
+ let preferredConnection = null;
2136
2206
  if (state.state === "connected") {
2207
+ if (!preferredConnection) {
2208
+ preferredConnection = state;
2209
+ }
2210
+ if (!preferredConnection.internal && state.internal) {
2211
+ preferredConnection = state;
2212
+ }
2213
+ }
2214
+ if (preferredConnection) {
2137
2215
  setWindowUrl(
2138
- new URL(`http://${state.host ?? "localhost"}:${state.port}/`)
2216
+ new URL(
2217
+ `http://${preferredConnection.host ?? "localhost"}:${preferredConnection.port}/`
2218
+ )
2139
2219
  );
2140
- return;
2141
2220
  }
2142
2221
  }
2143
2222
  });
@@ -6218,6 +6297,24 @@ var SIGIL_COLOR = external_exports.enum([
6218
6297
  "gray"
6219
6298
  ]);
6220
6299
 
6300
+ // ../../packages/sigil/dist/shared/config.js
6301
+ var APP_LISTENER_CONFIG = zod_default.object({
6302
+ port: zod_default.union([
6303
+ zod_default.number().int().min(1).max(65535),
6304
+ zod_default.object({
6305
+ from: zod_default.number().int().min(1).max(65535),
6306
+ to: zod_default.number().int().min(1).max(65535)
6307
+ }).refine((data) => data.to >= data.from, {
6308
+ message: '"to" must be greater than or equal to "from"'
6309
+ })
6310
+ ]),
6311
+ interface: zod_default.string().optional()
6312
+ });
6313
+ var ALL_APP_LISTENER_CONFIG = zod_default.record(
6314
+ zod_default.string(),
6315
+ APP_LISTENER_CONFIG
6316
+ );
6317
+
6221
6318
  // src/components/proto.ts
6222
6319
  var NET_UTILS_GENERAL_TARGET_DEFINITION = zod_default.union([
6223
6320
  zod_default.object({
@@ -6298,6 +6395,7 @@ var OUTPUT_CONFIG = zod_default.object({
6298
6395
  link: INPUT_OR_GENERATOR_INSTANCE_ID.nullable()
6299
6396
  });
6300
6397
  var TOOLBOX_CONFIG = zod_default.object({
6398
+ appListener: APP_LISTENER_CONFIG.partial().optional(),
6301
6399
  inputs: zod_default.record(zod_default.string(), INPUT_CONFIG),
6302
6400
  generators: zod_default.record(zod_default.string(), GENERATOR_CONFIG),
6303
6401
  outputs: zod_default.record(zod_default.string(), OUTPUT_CONFIG),
@@ -6331,7 +6429,11 @@ var DEFAULT_PROPS2 = {
6331
6429
  updates: null
6332
6430
  },
6333
6431
  handlers: { children: {} },
6334
- license: ""
6432
+ license: "",
6433
+ network: {
6434
+ envPort: null,
6435
+ defaultPort: -1
6436
+ }
6335
6437
  };
6336
6438
  var ToolboxRoot = class extends Base {
6337
6439
  /** @hidden */
@@ -6360,7 +6462,8 @@ var ToolboxRoot = class extends Base {
6360
6462
  config: this.props.config,
6361
6463
  state: this.props.state,
6362
6464
  handlers: this.props.handlers,
6363
- license: this.props.license
6465
+ license: this.props.license,
6466
+ network: this.props.network
6364
6467
  };
6365
6468
  }
6366
6469
  /** @hidden */
@@ -6746,9 +6849,27 @@ var createArtnet = (config) => {
6746
6849
  })();
6747
6850
  return connectPromise;
6748
6851
  };
6852
+ const getNextFrameTiming = (mode, timeMillis) => {
6853
+ const timecode = getTimecodeFromMillis(mode, timeMillis);
6854
+ timecode.frame += 1;
6855
+ if (timecode.frame >= TIMECODE_FPS[mode]) {
6856
+ timecode.frame = 0;
6857
+ timecode.seconds += 1;
6858
+ if (timecode.seconds >= 60) {
6859
+ timecode.seconds = 0;
6860
+ timecode.minutes += 1;
6861
+ if (timecode.minutes >= 60) {
6862
+ timecode.minutes = 0;
6863
+ timecode.hours += 1;
6864
+ }
6865
+ }
6866
+ }
6867
+ const nextFrameTimeMillis = getTimeMillisFromTimecode(timecode);
6868
+ return { nextFrameTimeMillis };
6869
+ };
6749
6870
  const sendTimecode = (mode, timeMillis) => {
6750
6871
  if (timeMillis < 0) {
6751
- return Promise.resolve();
6872
+ return Promise.resolve(getNextFrameTiming(mode, timeMillis));
6752
6873
  }
6753
6874
  if (!sendSocket) {
6754
6875
  return Promise.reject(new Error("ArtNet connection has not been opened"));
@@ -6789,7 +6910,7 @@ var createArtnet = (config) => {
6789
6910
  events.emit("error", error);
6790
6911
  reject(error);
6791
6912
  } else {
6792
- resolve();
6913
+ resolve(getNextFrameTiming(mode, timeMillis));
6793
6914
  }
6794
6915
  }
6795
6916
  )
@@ -6802,6 +6923,7 @@ var createArtnet = (config) => {
6802
6923
  };
6803
6924
  return {
6804
6925
  connect,
6926
+ getNextFrameTiming,
6805
6927
  sendTimecode,
6806
6928
  on,
6807
6929
  addListener,
@@ -6882,7 +7004,7 @@ var ArtnetInputConnection = ({
6882
7004
  created.connect().then(() => {
6883
7005
  artnet = created;
6884
7006
  setArtnetInstance(created);
6885
- log.info("ArtNet Timecode output initialized");
7007
+ log.info("ArtNet Timecode input initialized");
6886
7008
  setConnection({ ...connectionConfig, status: "active" });
6887
7009
  }).catch((err) => {
6888
7010
  const error = new Error("Failed to start ArtNet Timecode output");
@@ -14217,12 +14339,30 @@ var ArtnetOutputConnection = ({
14217
14339
  }
14218
14340
  if (timecodeState?.state === "playing" || timecodeState?.state === "lagging") {
14219
14341
  const tcState = timecodeState;
14220
- const interval = setInterval(() => {
14342
+ let timeoutId = null;
14343
+ const sendNextFrame = () => {
14221
14344
  const time = (Date.now() - tcState.effectiveStartTimeMillis) * tcState.speed;
14222
- artnetInstance.sendTimecode(mode, time);
14223
- }, 1e3 / TIMECODE_FPS[mode]);
14345
+ artnetInstance.sendTimecode(mode, time).then(({ nextFrameTimeMillis }) => {
14346
+ const delay = nextFrameTimeMillis - time + 1;
14347
+ timeoutId = setTimeout(sendNextFrame, delay);
14348
+ }).catch(() => {
14349
+ scheduleNextFrame();
14350
+ });
14351
+ };
14352
+ const scheduleNextFrame = () => {
14353
+ const time = (Date.now() - tcState.effectiveStartTimeMillis) * tcState.speed;
14354
+ const { nextFrameTimeMillis } = artnetInstance.getNextFrameTiming(
14355
+ mode,
14356
+ time
14357
+ );
14358
+ const delay = nextFrameTimeMillis - time + 1;
14359
+ timeoutId = setTimeout(sendNextFrame, delay);
14360
+ };
14361
+ scheduleNextFrame();
14224
14362
  return () => {
14225
- clearInterval(interval);
14363
+ if (timeoutId) {
14364
+ clearTimeout(timeoutId);
14365
+ }
14226
14366
  };
14227
14367
  } else if (timecodeState?.state === "stopped") {
14228
14368
  artnetInstance.sendTimecode(mode, timecodeState?.positionMillis ?? 0);
@@ -14481,6 +14621,8 @@ var ClockGenerator = ({
14481
14621
  timecode: {
14482
14622
  metadata: null,
14483
14623
  name: null,
14624
+ errors: [],
14625
+ warnings: [],
14484
14626
  state: {
14485
14627
  accuracyMillis: null,
14486
14628
  smpteMode: null,
@@ -14682,10 +14824,7 @@ var UpdateChecker = ({
14682
14824
 
14683
14825
  // src/env.ts
14684
14826
  var getEnv = (logger2) => {
14685
- const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : {
14686
- from: 4100,
14687
- to: 4200
14688
- };
14827
+ const PORT = process.env.PORT && parseInt(process.env.PORT, 10) || null;
14689
14828
  let API_BASE_URL;
14690
14829
  try {
14691
14830
  API_BASE_URL = process.env.API_BASE_URL ? new URL(process.env.API_BASE_URL) : new URL("https://arcanewizards.com");
@@ -14703,6 +14842,7 @@ var getEnv = (logger2) => {
14703
14842
 
14704
14843
  // src/app.tsx
14705
14844
  import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
14845
+ var DEFAULT_PORT = { from: 4100, to: 4200 };
14706
14846
  var App = ({
14707
14847
  title,
14708
14848
  version: version2,
@@ -14760,14 +14900,16 @@ var App = ({
14760
14900
  [handlers]
14761
14901
  );
14762
14902
  const license = useLicense();
14763
- const appListenerConfig = useMemo6(
14764
- () => ({
14765
- default: {
14766
- port: env.PORT
14767
- }
14768
- }),
14769
- [env.PORT]
14770
- );
14903
+ const appListenerConfig = useMemo6(() => {
14904
+ const baseConfig = {
14905
+ port: data.appListener?.port ?? DEFAULT_PORT,
14906
+ interface: data.appListener?.interface ?? void 0
14907
+ };
14908
+ if (env.PORT) {
14909
+ baseConfig.port = env.PORT;
14910
+ }
14911
+ return baseConfig;
14912
+ }, [env.PORT, data.appListener]);
14771
14913
  if (!license) {
14772
14914
  return;
14773
14915
  }
@@ -14780,7 +14922,11 @@ var App = ({
14780
14922
  handlers: availableHandlers,
14781
14923
  onUpdateConfig,
14782
14924
  onCallHandler: callHandler,
14783
- license: license.text
14925
+ license: license.text,
14926
+ network: {
14927
+ envPort: env.PORT,
14928
+ defaultPort: DEFAULT_PORT
14929
+ }
14784
14930
  }
14785
14931
  ),
14786
14932
  /* @__PURE__ */ jsx9(InputConnections, { state, setState }),
@@ -14832,7 +14978,7 @@ var App = ({
14832
14978
  {
14833
14979
  toolkit,
14834
14980
  setWindowUrl,
14835
- listenerConfig: appListenerConfig
14981
+ listenerConfig: { appListenerConfig }
14836
14982
  }
14837
14983
  )
14838
14984
  ]
@@ -14850,7 +14996,7 @@ var createApp = (props) => {
14850
14996
  };
14851
14997
 
14852
14998
  // package.json
14853
- var version = "0.1.3";
14999
+ var version = "0.1.5";
14854
15000
 
14855
15001
  // src/index.ts
14856
15002
  var runTimecodeToolboxServer = ({
@@ -14915,3 +15061,14 @@ lodash/lodash.js:
14915
15061
  * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
14916
15062
  *)
14917
15063
  */
15064
+ /*! Bundled license information:
15065
+
15066
+ escape-html/index.js:
15067
+ (*!
15068
+ * escape-html
15069
+ * Copyright(c) 2012-2013 TJ Holowaychuk
15070
+ * Copyright(c) 2015 Andreas Lubbe
15071
+ * Copyright(c) 2015 Tiancheng "Timothy" Gu
15072
+ * MIT Licensed
15073
+ *)
15074
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcanewizards/timecode-toolbox",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -55,12 +55,12 @@
55
55
  "osc-js": "^2.4.1",
56
56
  "radix-ui": "^1.4.3",
57
57
  "@arcanewizards/apis": "^0.0.1",
58
- "@arcanewizards/artnet": "^0.1.3",
58
+ "@arcanewizards/artnet": "^0.1.4",
59
59
  "@arcanewizards/eslint-config": "^0.0.0",
60
60
  "@arcanewizards/net-utils": "^0.1.3",
61
- "@arcanewizards/sigil": "^0.1.4",
62
- "@arcanewizards/typescript-config": "^0.0.0",
63
- "@arcanewizards/tcnet": "^0.1.3"
61
+ "@arcanewizards/sigil": "^0.1.7",
62
+ "@arcanewizards/tcnet": "^0.1.3",
63
+ "@arcanewizards/typescript-config": "^0.0.0"
64
64
  },
65
65
  "dependencies": {
66
66
  "@arcanejs/react-toolkit": "^0.15.1",
@@ -70,10 +70,11 @@
70
70
  "react": "^19.2.0"
71
71
  },
72
72
  "scripts": {
73
- "build": "pnpm check:types && rm -rf dist && cp ../../LICENSE ./LICENSE && tsup && pnpm build:entrypoint && pnpm build:frontend && pnpm build:styles && check-export-map",
73
+ "build": "pnpm check:types && pnpm clean && cp ../../LICENSE ./LICENSE && tsup && pnpm build:entrypoint && pnpm build:frontend && pnpm build:styles && check-export-map",
74
74
  "build:entrypoint": "arcane-build-frontend --entry src/components/frontend/entrypoint.ts --outfile dist/entrypoint.js --sourcemap",
75
75
  "build:frontend": "arcane-build-frontend --entry src/components/frontend/index.tsx --outfile dist/frontend.js --sourcemap",
76
76
  "build:styles": "tailwindcss -i ./src/components/frontend/frontend.css -o ./dist/entrypoint.css",
77
+ "clean": "rm -rf dist",
77
78
  "check:types": "tsc --noEmit",
78
79
  "format:fix": "cd .. && pnpm format:fix",
79
80
  "lint": "eslint . --max-warnings 0",