@telepath-computer/television 0.1.96 → 0.1.97

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/cli.cjs CHANGED
@@ -1194,8 +1194,8 @@ var require_command = __commonJS({
1194
1194
  "../../node_modules/commander/lib/command.js"(exports2) {
1195
1195
  var EventEmitter = require("node:events").EventEmitter;
1196
1196
  var childProcess = require("node:child_process");
1197
- var path7 = require("node:path");
1198
- var fs = require("node:fs");
1197
+ var path10 = require("node:path");
1198
+ var fs2 = require("node:fs");
1199
1199
  var process3 = require("node:process");
1200
1200
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1201
1201
  var { CommanderError: CommanderError2 } = require_error();
@@ -2189,7 +2189,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2189
2189
  * @param {string} subcommandName
2190
2190
  */
2191
2191
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2192
- if (fs.existsSync(executableFile)) return;
2192
+ if (fs2.existsSync(executableFile)) return;
2193
2193
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2194
2194
  const executableMissing = `'${executableFile}' does not exist
2195
2195
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2207,11 +2207,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2207
2207
  let launchWithNode = false;
2208
2208
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2209
2209
  function findFile(baseDir, baseName) {
2210
- const localBin = path7.resolve(baseDir, baseName);
2211
- if (fs.existsSync(localBin)) return localBin;
2212
- if (sourceExt.includes(path7.extname(baseName))) return void 0;
2210
+ const localBin = path10.resolve(baseDir, baseName);
2211
+ if (fs2.existsSync(localBin)) return localBin;
2212
+ if (sourceExt.includes(path10.extname(baseName))) return void 0;
2213
2213
  const foundExt = sourceExt.find(
2214
- (ext) => fs.existsSync(`${localBin}${ext}`)
2214
+ (ext) => fs2.existsSync(`${localBin}${ext}`)
2215
2215
  );
2216
2216
  if (foundExt) return `${localBin}${foundExt}`;
2217
2217
  return void 0;
@@ -2223,21 +2223,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2223
2223
  if (this._scriptPath) {
2224
2224
  let resolvedScriptPath;
2225
2225
  try {
2226
- resolvedScriptPath = fs.realpathSync(this._scriptPath);
2226
+ resolvedScriptPath = fs2.realpathSync(this._scriptPath);
2227
2227
  } catch {
2228
2228
  resolvedScriptPath = this._scriptPath;
2229
2229
  }
2230
- executableDir = path7.resolve(
2231
- path7.dirname(resolvedScriptPath),
2230
+ executableDir = path10.resolve(
2231
+ path10.dirname(resolvedScriptPath),
2232
2232
  executableDir
2233
2233
  );
2234
2234
  }
2235
2235
  if (executableDir) {
2236
2236
  let localFile = findFile(executableDir, executableFile);
2237
2237
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2238
- const legacyName = path7.basename(
2238
+ const legacyName = path10.basename(
2239
2239
  this._scriptPath,
2240
- path7.extname(this._scriptPath)
2240
+ path10.extname(this._scriptPath)
2241
2241
  );
2242
2242
  if (legacyName !== this._name) {
2243
2243
  localFile = findFile(
@@ -2248,7 +2248,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2248
2248
  }
2249
2249
  executableFile = localFile || executableFile;
2250
2250
  }
2251
- launchWithNode = sourceExt.includes(path7.extname(executableFile));
2251
+ launchWithNode = sourceExt.includes(path10.extname(executableFile));
2252
2252
  let proc;
2253
2253
  if (process3.platform !== "win32") {
2254
2254
  if (launchWithNode) {
@@ -3163,7 +3163,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3163
3163
  * @return {Command}
3164
3164
  */
3165
3165
  nameFromFilename(filename) {
3166
- this._name = path7.basename(filename, path7.extname(filename));
3166
+ this._name = path10.basename(filename, path10.extname(filename));
3167
3167
  return this;
3168
3168
  }
3169
3169
  /**
@@ -3177,9 +3177,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3177
3177
  * @param {string} [path]
3178
3178
  * @return {(string|null|Command)}
3179
3179
  */
3180
- executableDir(path8) {
3181
- if (path8 === void 0) return this._executableDir;
3182
- this._executableDir = path8;
3180
+ executableDir(path11) {
3181
+ if (path11 === void 0) return this._executableDir;
3182
+ this._executableDir = path11;
3183
3183
  return this;
3184
3184
  }
3185
3185
  /**
@@ -8692,8 +8692,8 @@ var require_node = __commonJS({
8692
8692
  }
8693
8693
  break;
8694
8694
  case "FILE":
8695
- var fs = require("fs");
8696
- stream2 = new fs.SyncWriteStream(fd2, { autoClose: false });
8695
+ var fs2 = require("fs");
8696
+ stream2 = new fs2.SyncWriteStream(fd2, { autoClose: false });
8697
8697
  stream2._type = "fs";
8698
8698
  break;
8699
8699
  case "PIPE":
@@ -21480,11 +21480,11 @@ var require_mime_types = __commonJS({
21480
21480
  }
21481
21481
  return exts[0];
21482
21482
  }
21483
- function lookup(path7) {
21484
- if (!path7 || typeof path7 !== "string") {
21483
+ function lookup(path10) {
21484
+ if (!path10 || typeof path10 !== "string") {
21485
21485
  return false;
21486
21486
  }
21487
- var extension3 = extname("x." + path7).toLowerCase().substr(1);
21487
+ var extension3 = extname("x." + path10).toLowerCase().substr(1);
21488
21488
  if (!extension3) {
21489
21489
  return false;
21490
21490
  }
@@ -23413,8 +23413,8 @@ var require_node2 = __commonJS({
23413
23413
  }
23414
23414
  break;
23415
23415
  case "FILE":
23416
- var fs = require("fs");
23417
- stream2 = new fs.SyncWriteStream(fd2, { autoClose: false });
23416
+ var fs2 = require("fs");
23417
+ stream2 = new fs2.SyncWriteStream(fd2, { autoClose: false });
23418
23418
  stream2._type = "fs";
23419
23419
  break;
23420
23420
  case "PIPE":
@@ -24132,8 +24132,8 @@ var require_node3 = __commonJS({
24132
24132
  }
24133
24133
  break;
24134
24134
  case "FILE":
24135
- var fs = require("fs");
24136
- stream2 = new fs.SyncWriteStream(fd2, { autoClose: false });
24135
+ var fs2 = require("fs");
24136
+ stream2 = new fs2.SyncWriteStream(fd2, { autoClose: false });
24137
24137
  stream2._type = "fs";
24138
24138
  break;
24139
24139
  case "PIPE":
@@ -24221,7 +24221,7 @@ var require_path_to_regexp = __commonJS({
24221
24221
  "../../node_modules/path-to-regexp/index.js"(exports2, module2) {
24222
24222
  module2.exports = pathToRegexp;
24223
24223
  var MATCHING_GROUP_REGEXP = /\\.|\((?:\?<(.*?)>)?(?!\?)/g;
24224
- function pathToRegexp(path7, keys, options) {
24224
+ function pathToRegexp(path10, keys, options) {
24225
24225
  options = options || {};
24226
24226
  keys = keys || [];
24227
24227
  var strict = options.strict;
@@ -24235,8 +24235,8 @@ var require_path_to_regexp = __commonJS({
24235
24235
  var pos = 0;
24236
24236
  var backtrack = "";
24237
24237
  var m;
24238
- if (path7 instanceof RegExp) {
24239
- while (m = MATCHING_GROUP_REGEXP.exec(path7.source)) {
24238
+ if (path10 instanceof RegExp) {
24239
+ while (m = MATCHING_GROUP_REGEXP.exec(path10.source)) {
24240
24240
  if (m[0][0] === "\\") continue;
24241
24241
  keys.push({
24242
24242
  name: m[1] || name++,
@@ -24244,18 +24244,18 @@ var require_path_to_regexp = __commonJS({
24244
24244
  offset: m.index
24245
24245
  });
24246
24246
  }
24247
- return path7;
24247
+ return path10;
24248
24248
  }
24249
- if (Array.isArray(path7)) {
24250
- path7 = path7.map(function(value) {
24249
+ if (Array.isArray(path10)) {
24250
+ path10 = path10.map(function(value) {
24251
24251
  return pathToRegexp(value, keys, options).source;
24252
24252
  });
24253
- return new RegExp(path7.join("|"), flags);
24253
+ return new RegExp(path10.join("|"), flags);
24254
24254
  }
24255
- if (typeof path7 !== "string") {
24255
+ if (typeof path10 !== "string") {
24256
24256
  throw new TypeError("path must be a string, array of strings, or regular expression");
24257
24257
  }
24258
- path7 = path7.replace(
24258
+ path10 = path10.replace(
24259
24259
  /\\.|(\/)?(\.)?:(\w+)(\(.*?\))?(\*)?(\?)?|[.*]|\/\(/g,
24260
24260
  function(match, slash, format, key, capture, star, optional2, offset) {
24261
24261
  if (match[0] === "\\") {
@@ -24272,7 +24272,7 @@ var require_path_to_regexp = __commonJS({
24272
24272
  if (slash || format) {
24273
24273
  backtrack = "";
24274
24274
  } else {
24275
- backtrack += path7.slice(pos, offset);
24275
+ backtrack += path10.slice(pos, offset);
24276
24276
  }
24277
24277
  pos = offset + match.length;
24278
24278
  if (match === "*") {
@@ -24302,7 +24302,7 @@ var require_path_to_regexp = __commonJS({
24302
24302
  return result;
24303
24303
  }
24304
24304
  );
24305
- while (m = MATCHING_GROUP_REGEXP.exec(path7)) {
24305
+ while (m = MATCHING_GROUP_REGEXP.exec(path10)) {
24306
24306
  if (m[0][0] === "\\") continue;
24307
24307
  if (keysOffset + i === keys.length || keys[keysOffset + i].offset > m.index) {
24308
24308
  keys.splice(keysOffset + i, 0, {
@@ -24314,13 +24314,13 @@ var require_path_to_regexp = __commonJS({
24314
24314
  }
24315
24315
  i++;
24316
24316
  }
24317
- path7 += strict ? "" : path7[path7.length - 1] === "/" ? "?" : "/?";
24317
+ path10 += strict ? "" : path10[path10.length - 1] === "/" ? "?" : "/?";
24318
24318
  if (end) {
24319
- path7 += "$";
24320
- } else if (path7[path7.length - 1] !== "/") {
24321
- path7 += lookahead ? "(?=/|$)" : "(?:/|$)";
24319
+ path10 += "$";
24320
+ } else if (path10[path10.length - 1] !== "/") {
24321
+ path10 += lookahead ? "(?=/|$)" : "(?:/|$)";
24322
24322
  }
24323
- return new RegExp("^" + path7, flags);
24323
+ return new RegExp("^" + path10, flags);
24324
24324
  }
24325
24325
  }
24326
24326
  });
@@ -24333,19 +24333,19 @@ var require_layer = __commonJS({
24333
24333
  var debug = require_src3()("express:router:layer");
24334
24334
  var hasOwnProperty = Object.prototype.hasOwnProperty;
24335
24335
  module2.exports = Layer;
24336
- function Layer(path7, options, fn) {
24336
+ function Layer(path10, options, fn) {
24337
24337
  if (!(this instanceof Layer)) {
24338
- return new Layer(path7, options, fn);
24338
+ return new Layer(path10, options, fn);
24339
24339
  }
24340
- debug("new %o", path7);
24340
+ debug("new %o", path10);
24341
24341
  var opts = options || {};
24342
24342
  this.handle = fn;
24343
24343
  this.name = fn.name || "<anonymous>";
24344
24344
  this.params = void 0;
24345
24345
  this.path = void 0;
24346
- this.regexp = pathRegexp(path7, this.keys = [], opts);
24347
- this.regexp.fast_star = path7 === "*";
24348
- this.regexp.fast_slash = path7 === "/" && opts.end === false;
24346
+ this.regexp = pathRegexp(path10, this.keys = [], opts);
24347
+ this.regexp.fast_star = path10 === "*";
24348
+ this.regexp.fast_slash = path10 === "/" && opts.end === false;
24349
24349
  }
24350
24350
  Layer.prototype.handle_error = function handle_error(error48, req, res, next) {
24351
24351
  var fn = this.handle;
@@ -24369,20 +24369,20 @@ var require_layer = __commonJS({
24369
24369
  next(err);
24370
24370
  }
24371
24371
  };
24372
- Layer.prototype.match = function match(path7) {
24372
+ Layer.prototype.match = function match(path10) {
24373
24373
  var match2;
24374
- if (path7 != null) {
24374
+ if (path10 != null) {
24375
24375
  if (this.regexp.fast_slash) {
24376
24376
  this.params = {};
24377
24377
  this.path = "";
24378
24378
  return true;
24379
24379
  }
24380
24380
  if (this.regexp.fast_star) {
24381
- this.params = { "0": decode_param(path7) };
24382
- this.path = path7;
24381
+ this.params = { "0": decode_param(path10) };
24382
+ this.path = path10;
24383
24383
  return true;
24384
24384
  }
24385
- match2 = this.regexp.exec(path7);
24385
+ match2 = this.regexp.exec(path10);
24386
24386
  }
24387
24387
  if (!match2) {
24388
24388
  this.params = void 0;
@@ -24475,10 +24475,10 @@ var require_route = __commonJS({
24475
24475
  var slice = Array.prototype.slice;
24476
24476
  var toString = Object.prototype.toString;
24477
24477
  module2.exports = Route;
24478
- function Route(path7) {
24479
- this.path = path7;
24478
+ function Route(path10) {
24479
+ this.path = path10;
24480
24480
  this.stack = [];
24481
- debug("new %o", path7);
24481
+ debug("new %o", path10);
24482
24482
  this.methods = {};
24483
24483
  }
24484
24484
  Route.prototype._handles_method = function _handles_method(method) {
@@ -24690,8 +24690,8 @@ var require_router = __commonJS({
24690
24690
  if (++sync > 100) {
24691
24691
  return setImmediate(next, err);
24692
24692
  }
24693
- var path7 = getPathname(req);
24694
- if (path7 == null) {
24693
+ var path10 = getPathname(req);
24694
+ if (path10 == null) {
24695
24695
  return done(layerError);
24696
24696
  }
24697
24697
  var layer;
@@ -24699,7 +24699,7 @@ var require_router = __commonJS({
24699
24699
  var route;
24700
24700
  while (match !== true && idx < stack.length) {
24701
24701
  layer = stack[idx++];
24702
- match = matchLayer(layer, path7);
24702
+ match = matchLayer(layer, path10);
24703
24703
  route = layer.route;
24704
24704
  if (typeof match !== "boolean") {
24705
24705
  layerError = layerError || match;
@@ -24737,18 +24737,18 @@ var require_router = __commonJS({
24737
24737
  } else if (route) {
24738
24738
  layer.handle_request(req, res, next);
24739
24739
  } else {
24740
- trim_prefix(layer, layerError, layerPath, path7);
24740
+ trim_prefix(layer, layerError, layerPath, path10);
24741
24741
  }
24742
24742
  sync = 0;
24743
24743
  });
24744
24744
  }
24745
- function trim_prefix(layer, layerError, layerPath, path7) {
24745
+ function trim_prefix(layer, layerError, layerPath, path10) {
24746
24746
  if (layerPath.length !== 0) {
24747
- if (layerPath !== path7.slice(0, layerPath.length)) {
24747
+ if (layerPath !== path10.slice(0, layerPath.length)) {
24748
24748
  next(layerError);
24749
24749
  return;
24750
24750
  }
24751
- var c = path7[layerPath.length];
24751
+ var c = path10[layerPath.length];
24752
24752
  if (c && c !== "/" && c !== ".") return next(layerError);
24753
24753
  debug("trim prefix (%s) from url %s", layerPath, req.url);
24754
24754
  removed = layerPath;
@@ -24826,7 +24826,7 @@ var require_router = __commonJS({
24826
24826
  };
24827
24827
  proto.use = function use(fn) {
24828
24828
  var offset = 0;
24829
- var path7 = "/";
24829
+ var path10 = "/";
24830
24830
  if (typeof fn !== "function") {
24831
24831
  var arg = fn;
24832
24832
  while (Array.isArray(arg) && arg.length !== 0) {
@@ -24834,7 +24834,7 @@ var require_router = __commonJS({
24834
24834
  }
24835
24835
  if (typeof arg !== "function") {
24836
24836
  offset = 1;
24837
- path7 = fn;
24837
+ path10 = fn;
24838
24838
  }
24839
24839
  }
24840
24840
  var callbacks = flatten(slice.call(arguments, offset));
@@ -24846,8 +24846,8 @@ var require_router = __commonJS({
24846
24846
  if (typeof fn !== "function") {
24847
24847
  throw new TypeError("Router.use() requires a middleware function but got a " + gettype(fn));
24848
24848
  }
24849
- debug("use %o %s", path7, fn.name || "<anonymous>");
24850
- var layer = new Layer(path7, {
24849
+ debug("use %o %s", path10, fn.name || "<anonymous>");
24850
+ var layer = new Layer(path10, {
24851
24851
  sensitive: this.caseSensitive,
24852
24852
  strict: false,
24853
24853
  end: false
@@ -24857,9 +24857,9 @@ var require_router = __commonJS({
24857
24857
  }
24858
24858
  return this;
24859
24859
  };
24860
- proto.route = function route(path7) {
24861
- var route2 = new Route(path7);
24862
- var layer = new Layer(path7, {
24860
+ proto.route = function route(path10) {
24861
+ var route2 = new Route(path10);
24862
+ var layer = new Layer(path10, {
24863
24863
  sensitive: this.caseSensitive,
24864
24864
  strict: this.strict,
24865
24865
  end: true
@@ -24869,8 +24869,8 @@ var require_router = __commonJS({
24869
24869
  return route2;
24870
24870
  };
24871
24871
  methods.concat("all").forEach(function(method) {
24872
- proto[method] = function(path7) {
24873
- var route = this.route(path7);
24872
+ proto[method] = function(path10) {
24873
+ var route = this.route(path10);
24874
24874
  route[method].apply(route, slice.call(arguments, 1));
24875
24875
  return this;
24876
24876
  };
@@ -24906,9 +24906,9 @@ var require_router = __commonJS({
24906
24906
  }
24907
24907
  return toString.call(obj).replace(objectRegExp, "$1");
24908
24908
  }
24909
- function matchLayer(layer, path7) {
24909
+ function matchLayer(layer, path10) {
24910
24910
  try {
24911
- return layer.match(path7);
24911
+ return layer.match(path10);
24912
24912
  } catch (err) {
24913
24913
  return err;
24914
24914
  }
@@ -25026,13 +25026,13 @@ var require_view = __commonJS({
25026
25026
  "../../node_modules/express/lib/view.js"(exports2, module2) {
25027
25027
  "use strict";
25028
25028
  var debug = require_src3()("express:view");
25029
- var path7 = require("path");
25030
- var fs = require("fs");
25031
- var dirname = path7.dirname;
25032
- var basename = path7.basename;
25033
- var extname = path7.extname;
25034
- var join2 = path7.join;
25035
- var resolve = path7.resolve;
25029
+ var path10 = require("path");
25030
+ var fs2 = require("fs");
25031
+ var dirname = path10.dirname;
25032
+ var basename = path10.basename;
25033
+ var extname = path10.extname;
25034
+ var join2 = path10.join;
25035
+ var resolve = path10.resolve;
25036
25036
  module2.exports = View;
25037
25037
  function View(name, options) {
25038
25038
  var opts = options || {};
@@ -25061,17 +25061,17 @@ var require_view = __commonJS({
25061
25061
  this.path = this.lookup(fileName);
25062
25062
  }
25063
25063
  View.prototype.lookup = function lookup(name) {
25064
- var path8;
25064
+ var path11;
25065
25065
  var roots = [].concat(this.root);
25066
25066
  debug('lookup "%s"', name);
25067
- for (var i = 0; i < roots.length && !path8; i++) {
25067
+ for (var i = 0; i < roots.length && !path11; i++) {
25068
25068
  var root = roots[i];
25069
25069
  var loc = resolve(root, name);
25070
25070
  var dir = dirname(loc);
25071
25071
  var file2 = basename(loc);
25072
- path8 = this.resolve(dir, file2);
25072
+ path11 = this.resolve(dir, file2);
25073
25073
  }
25074
- return path8;
25074
+ return path11;
25075
25075
  };
25076
25076
  View.prototype.render = function render(options, callback) {
25077
25077
  debug('render "%s"', this.path);
@@ -25079,21 +25079,21 @@ var require_view = __commonJS({
25079
25079
  };
25080
25080
  View.prototype.resolve = function resolve2(dir, file2) {
25081
25081
  var ext = this.ext;
25082
- var path8 = join2(dir, file2);
25083
- var stat = tryStat(path8);
25082
+ var path11 = join2(dir, file2);
25083
+ var stat = tryStat(path11);
25084
25084
  if (stat && stat.isFile()) {
25085
- return path8;
25085
+ return path11;
25086
25086
  }
25087
- path8 = join2(dir, basename(file2, ext), "index" + ext);
25088
- stat = tryStat(path8);
25087
+ path11 = join2(dir, basename(file2, ext), "index" + ext);
25088
+ stat = tryStat(path11);
25089
25089
  if (stat && stat.isFile()) {
25090
- return path8;
25090
+ return path11;
25091
25091
  }
25092
25092
  };
25093
- function tryStat(path8) {
25094
- debug('stat "%s"', path8);
25093
+ function tryStat(path11) {
25094
+ debug('stat "%s"', path11);
25095
25095
  try {
25096
- return fs.statSync(path8);
25096
+ return fs2.statSync(path11);
25097
25097
  } catch (e) {
25098
25098
  return void 0;
25099
25099
  }
@@ -25698,8 +25698,8 @@ var require_node4 = __commonJS({
25698
25698
  }
25699
25699
  break;
25700
25700
  case "FILE":
25701
- var fs = require("fs");
25702
- stream2 = new fs.SyncWriteStream(fd2, { autoClose: false });
25701
+ var fs2 = require("fs");
25702
+ stream2 = new fs2.SyncWriteStream(fd2, { autoClose: false });
25703
25703
  stream2._type = "fs";
25704
25704
  break;
25705
25705
  case "PIPE":
@@ -25871,8 +25871,8 @@ var require_types = __commonJS({
25871
25871
  // ../../node_modules/mime/mime.js
25872
25872
  var require_mime = __commonJS({
25873
25873
  "../../node_modules/mime/mime.js"(exports2, module2) {
25874
- var path7 = require("path");
25875
- var fs = require("fs");
25874
+ var path10 = require("path");
25875
+ var fs2 = require("fs");
25876
25876
  function Mime() {
25877
25877
  this.types = /* @__PURE__ */ Object.create(null);
25878
25878
  this.extensions = /* @__PURE__ */ Object.create(null);
@@ -25893,7 +25893,7 @@ var require_mime = __commonJS({
25893
25893
  };
25894
25894
  Mime.prototype.load = function(file2) {
25895
25895
  this._loading = file2;
25896
- var map2 = {}, content = fs.readFileSync(file2, "ascii"), lines = content.split(/[\r\n]+/);
25896
+ var map2 = {}, content = fs2.readFileSync(file2, "ascii"), lines = content.split(/[\r\n]+/);
25897
25897
  lines.forEach(function(line) {
25898
25898
  var fields = line.replace(/\s*#.*|^\s*|\s*$/g, "").split(/\s+/);
25899
25899
  map2[fields.shift()] = fields;
@@ -25901,8 +25901,8 @@ var require_mime = __commonJS({
25901
25901
  this.define(map2);
25902
25902
  this._loading = null;
25903
25903
  };
25904
- Mime.prototype.lookup = function(path8, fallback) {
25905
- var ext = path8.replace(/^.*[\.\/\\]/, "").toLowerCase();
25904
+ Mime.prototype.lookup = function(path11, fallback) {
25905
+ var ext = path11.replace(/^.*[\.\/\\]/, "").toLowerCase();
25906
25906
  return this.types[ext] || fallback || this.default_type;
25907
25907
  };
25908
25908
  Mime.prototype.extension = function(mimeType) {
@@ -26131,33 +26131,33 @@ var require_send = __commonJS({
26131
26131
  var escapeHtml = require_escape_html();
26132
26132
  var etag = require_etag();
26133
26133
  var fresh = require_fresh();
26134
- var fs = require("fs");
26134
+ var fs2 = require("fs");
26135
26135
  var mime = require_mime();
26136
26136
  var ms = require_ms5();
26137
26137
  var onFinished = require_on_finished();
26138
26138
  var parseRange = require_range_parser();
26139
- var path7 = require("path");
26139
+ var path10 = require("path");
26140
26140
  var statuses = require_statuses();
26141
26141
  var Stream = require("stream");
26142
26142
  var util = require("util");
26143
- var extname = path7.extname;
26144
- var join2 = path7.join;
26145
- var normalize = path7.normalize;
26146
- var resolve = path7.resolve;
26147
- var sep = path7.sep;
26143
+ var extname = path10.extname;
26144
+ var join2 = path10.join;
26145
+ var normalize = path10.normalize;
26146
+ var resolve = path10.resolve;
26147
+ var sep = path10.sep;
26148
26148
  var BYTES_RANGE_REGEXP = /^ *bytes=/;
26149
26149
  var MAX_MAXAGE = 60 * 60 * 24 * 365 * 1e3;
26150
26150
  var UP_PATH_REGEXP = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
26151
26151
  module2.exports = send;
26152
26152
  module2.exports.mime = mime;
26153
- function send(req, path8, options) {
26154
- return new SendStream(req, path8, options);
26153
+ function send(req, path11, options) {
26154
+ return new SendStream(req, path11, options);
26155
26155
  }
26156
- function SendStream(req, path8, options) {
26156
+ function SendStream(req, path11, options) {
26157
26157
  Stream.call(this);
26158
26158
  var opts = options || {};
26159
26159
  this.options = opts;
26160
- this.path = path8;
26160
+ this.path = path11;
26161
26161
  this.req = req;
26162
26162
  this._acceptRanges = opts.acceptRanges !== void 0 ? Boolean(opts.acceptRanges) : true;
26163
26163
  this._cacheControl = opts.cacheControl !== void 0 ? Boolean(opts.cacheControl) : true;
@@ -26203,8 +26203,8 @@ var require_send = __commonJS({
26203
26203
  this._index = index2;
26204
26204
  return this;
26205
26205
  }, "send.index: pass index as option");
26206
- SendStream.prototype.root = function root(path8) {
26207
- this._root = resolve(String(path8));
26206
+ SendStream.prototype.root = function root(path11) {
26207
+ this._root = resolve(String(path11));
26208
26208
  debug("root %s", this._root);
26209
26209
  return this;
26210
26210
  };
@@ -26317,10 +26317,10 @@ var require_send = __commonJS({
26317
26317
  var lastModified = this.res.getHeader("Last-Modified");
26318
26318
  return parseHttpDate(lastModified) <= parseHttpDate(ifRange);
26319
26319
  };
26320
- SendStream.prototype.redirect = function redirect(path8) {
26320
+ SendStream.prototype.redirect = function redirect(path11) {
26321
26321
  var res = this.res;
26322
26322
  if (hasListeners(this, "directory")) {
26323
- this.emit("directory", res, path8);
26323
+ this.emit("directory", res, path11);
26324
26324
  return;
26325
26325
  }
26326
26326
  if (this.hasTrailingSlash()) {
@@ -26340,42 +26340,42 @@ var require_send = __commonJS({
26340
26340
  SendStream.prototype.pipe = function pipe2(res) {
26341
26341
  var root = this._root;
26342
26342
  this.res = res;
26343
- var path8 = decode3(this.path);
26344
- if (path8 === -1) {
26343
+ var path11 = decode3(this.path);
26344
+ if (path11 === -1) {
26345
26345
  this.error(400);
26346
26346
  return res;
26347
26347
  }
26348
- if (~path8.indexOf("\0")) {
26348
+ if (~path11.indexOf("\0")) {
26349
26349
  this.error(400);
26350
26350
  return res;
26351
26351
  }
26352
26352
  var parts;
26353
26353
  if (root !== null) {
26354
- if (path8) {
26355
- path8 = normalize("." + sep + path8);
26354
+ if (path11) {
26355
+ path11 = normalize("." + sep + path11);
26356
26356
  }
26357
- if (UP_PATH_REGEXP.test(path8)) {
26358
- debug('malicious path "%s"', path8);
26357
+ if (UP_PATH_REGEXP.test(path11)) {
26358
+ debug('malicious path "%s"', path11);
26359
26359
  this.error(403);
26360
26360
  return res;
26361
26361
  }
26362
- parts = path8.split(sep);
26363
- path8 = normalize(join2(root, path8));
26362
+ parts = path11.split(sep);
26363
+ path11 = normalize(join2(root, path11));
26364
26364
  } else {
26365
- if (UP_PATH_REGEXP.test(path8)) {
26366
- debug('malicious path "%s"', path8);
26365
+ if (UP_PATH_REGEXP.test(path11)) {
26366
+ debug('malicious path "%s"', path11);
26367
26367
  this.error(403);
26368
26368
  return res;
26369
26369
  }
26370
- parts = normalize(path8).split(sep);
26371
- path8 = resolve(path8);
26370
+ parts = normalize(path11).split(sep);
26371
+ path11 = resolve(path11);
26372
26372
  }
26373
26373
  if (containsDotFile(parts)) {
26374
26374
  var access = this._dotfiles;
26375
26375
  if (access === void 0) {
26376
26376
  access = parts[parts.length - 1][0] === "." ? this._hidden ? "allow" : "ignore" : "allow";
26377
26377
  }
26378
- debug('%s dotfile "%s"', access, path8);
26378
+ debug('%s dotfile "%s"', access, path11);
26379
26379
  switch (access) {
26380
26380
  case "allow":
26381
26381
  break;
@@ -26389,13 +26389,13 @@ var require_send = __commonJS({
26389
26389
  }
26390
26390
  }
26391
26391
  if (this._index.length && this.hasTrailingSlash()) {
26392
- this.sendIndex(path8);
26392
+ this.sendIndex(path11);
26393
26393
  return res;
26394
26394
  }
26395
- this.sendFile(path8);
26395
+ this.sendFile(path11);
26396
26396
  return res;
26397
26397
  };
26398
- SendStream.prototype.send = function send2(path8, stat) {
26398
+ SendStream.prototype.send = function send2(path11, stat) {
26399
26399
  var len = stat.size;
26400
26400
  var options = this.options;
26401
26401
  var opts = {};
@@ -26407,9 +26407,9 @@ var require_send = __commonJS({
26407
26407
  this.headersAlreadySent();
26408
26408
  return;
26409
26409
  }
26410
- debug('pipe "%s"', path8);
26411
- this.setHeader(path8, stat);
26412
- this.type(path8);
26410
+ debug('pipe "%s"', path11);
26411
+ this.setHeader(path11, stat);
26412
+ this.type(path11);
26413
26413
  if (this.isConditionalGET()) {
26414
26414
  if (this.isPreconditionFailure()) {
26415
26415
  this.error(412);
@@ -26458,28 +26458,28 @@ var require_send = __commonJS({
26458
26458
  res.end();
26459
26459
  return;
26460
26460
  }
26461
- this.stream(path8, opts);
26461
+ this.stream(path11, opts);
26462
26462
  };
26463
- SendStream.prototype.sendFile = function sendFile(path8) {
26463
+ SendStream.prototype.sendFile = function sendFile(path11) {
26464
26464
  var i = 0;
26465
26465
  var self2 = this;
26466
- debug('stat "%s"', path8);
26467
- fs.stat(path8, function onstat(err, stat) {
26468
- if (err && err.code === "ENOENT" && !extname(path8) && path8[path8.length - 1] !== sep) {
26466
+ debug('stat "%s"', path11);
26467
+ fs2.stat(path11, function onstat(err, stat) {
26468
+ if (err && err.code === "ENOENT" && !extname(path11) && path11[path11.length - 1] !== sep) {
26469
26469
  return next(err);
26470
26470
  }
26471
26471
  if (err) return self2.onStatError(err);
26472
- if (stat.isDirectory()) return self2.redirect(path8);
26473
- self2.emit("file", path8, stat);
26474
- self2.send(path8, stat);
26472
+ if (stat.isDirectory()) return self2.redirect(path11);
26473
+ self2.emit("file", path11, stat);
26474
+ self2.send(path11, stat);
26475
26475
  });
26476
26476
  function next(err) {
26477
26477
  if (self2._extensions.length <= i) {
26478
26478
  return err ? self2.onStatError(err) : self2.error(404);
26479
26479
  }
26480
- var p = path8 + "." + self2._extensions[i++];
26480
+ var p = path11 + "." + self2._extensions[i++];
26481
26481
  debug('stat "%s"', p);
26482
- fs.stat(p, function(err2, stat) {
26482
+ fs2.stat(p, function(err2, stat) {
26483
26483
  if (err2) return next(err2);
26484
26484
  if (stat.isDirectory()) return next();
26485
26485
  self2.emit("file", p, stat);
@@ -26487,7 +26487,7 @@ var require_send = __commonJS({
26487
26487
  });
26488
26488
  }
26489
26489
  };
26490
- SendStream.prototype.sendIndex = function sendIndex(path8) {
26490
+ SendStream.prototype.sendIndex = function sendIndex(path11) {
26491
26491
  var i = -1;
26492
26492
  var self2 = this;
26493
26493
  function next(err) {
@@ -26495,9 +26495,9 @@ var require_send = __commonJS({
26495
26495
  if (err) return self2.onStatError(err);
26496
26496
  return self2.error(404);
26497
26497
  }
26498
- var p = join2(path8, self2._index[i]);
26498
+ var p = join2(path11, self2._index[i]);
26499
26499
  debug('stat "%s"', p);
26500
- fs.stat(p, function(err2, stat) {
26500
+ fs2.stat(p, function(err2, stat) {
26501
26501
  if (err2) return next(err2);
26502
26502
  if (stat.isDirectory()) return next();
26503
26503
  self2.emit("file", p, stat);
@@ -26506,10 +26506,10 @@ var require_send = __commonJS({
26506
26506
  }
26507
26507
  next();
26508
26508
  };
26509
- SendStream.prototype.stream = function stream(path8, options) {
26509
+ SendStream.prototype.stream = function stream(path11, options) {
26510
26510
  var self2 = this;
26511
26511
  var res = this.res;
26512
- var stream2 = fs.createReadStream(path8, options);
26512
+ var stream2 = fs2.createReadStream(path11, options);
26513
26513
  this.emit("stream", stream2);
26514
26514
  stream2.pipe(res);
26515
26515
  function cleanup() {
@@ -26524,10 +26524,10 @@ var require_send = __commonJS({
26524
26524
  self2.emit("end");
26525
26525
  });
26526
26526
  };
26527
- SendStream.prototype.type = function type(path8) {
26527
+ SendStream.prototype.type = function type(path11) {
26528
26528
  var res = this.res;
26529
26529
  if (res.getHeader("Content-Type")) return;
26530
- var type2 = mime.lookup(path8);
26530
+ var type2 = mime.lookup(path11);
26531
26531
  if (!type2) {
26532
26532
  debug("no content-type");
26533
26533
  return;
@@ -26536,9 +26536,9 @@ var require_send = __commonJS({
26536
26536
  debug("content-type %s", type2);
26537
26537
  res.setHeader("Content-Type", type2 + (charset ? "; charset=" + charset : ""));
26538
26538
  };
26539
- SendStream.prototype.setHeader = function setHeader(path8, stat) {
26539
+ SendStream.prototype.setHeader = function setHeader(path11, stat) {
26540
26540
  var res = this.res;
26541
- this.emit("headers", res, path8, stat);
26541
+ this.emit("headers", res, path11, stat);
26542
26542
  if (this._acceptRanges && !res.getHeader("Accept-Ranges")) {
26543
26543
  debug("accept ranges");
26544
26544
  res.setHeader("Accept-Ranges", "bytes");
@@ -26597,9 +26597,9 @@ var require_send = __commonJS({
26597
26597
  }
26598
26598
  return err instanceof Error ? createError(status, err, { expose: false }) : createError(status, err);
26599
26599
  }
26600
- function decode3(path8) {
26600
+ function decode3(path11) {
26601
26601
  try {
26602
- return decodeURIComponent(path8);
26602
+ return decodeURIComponent(path11);
26603
26603
  } catch (err) {
26604
26604
  return -1;
26605
26605
  }
@@ -27508,10 +27508,10 @@ var require_utils2 = __commonJS({
27508
27508
  var querystring = require("querystring");
27509
27509
  exports2.etag = createETagGenerator({ weak: false });
27510
27510
  exports2.wetag = createETagGenerator({ weak: true });
27511
- exports2.isAbsolute = function(path7) {
27512
- if ("/" === path7[0]) return true;
27513
- if (":" === path7[1] && ("\\" === path7[2] || "/" === path7[2])) return true;
27514
- if ("\\\\" === path7.substring(0, 2)) return true;
27511
+ exports2.isAbsolute = function(path10) {
27512
+ if ("/" === path10[0]) return true;
27513
+ if (":" === path10[1] && ("\\" === path10[2] || "/" === path10[2])) return true;
27514
+ if ("\\\\" === path10.substring(0, 2)) return true;
27515
27515
  };
27516
27516
  exports2.flatten = deprecate.function(
27517
27517
  flatten,
@@ -27722,7 +27722,7 @@ var require_application = __commonJS({
27722
27722
  };
27723
27723
  app.use = function use(fn) {
27724
27724
  var offset = 0;
27725
- var path7 = "/";
27725
+ var path10 = "/";
27726
27726
  if (typeof fn !== "function") {
27727
27727
  var arg = fn;
27728
27728
  while (Array.isArray(arg) && arg.length !== 0) {
@@ -27730,7 +27730,7 @@ var require_application = __commonJS({
27730
27730
  }
27731
27731
  if (typeof arg !== "function") {
27732
27732
  offset = 1;
27733
- path7 = fn;
27733
+ path10 = fn;
27734
27734
  }
27735
27735
  }
27736
27736
  var fns = flatten(slice.call(arguments, offset));
@@ -27741,12 +27741,12 @@ var require_application = __commonJS({
27741
27741
  var router = this._router;
27742
27742
  fns.forEach(function(fn2) {
27743
27743
  if (!fn2 || !fn2.handle || !fn2.set) {
27744
- return router.use(path7, fn2);
27744
+ return router.use(path10, fn2);
27745
27745
  }
27746
- debug(".use app under %s", path7);
27747
- fn2.mountpath = path7;
27746
+ debug(".use app under %s", path10);
27747
+ fn2.mountpath = path10;
27748
27748
  fn2.parent = this;
27749
- router.use(path7, function mounted_app(req, res, next) {
27749
+ router.use(path10, function mounted_app(req, res, next) {
27750
27750
  var orig = req.app;
27751
27751
  fn2.handle(req, res, function(err) {
27752
27752
  setPrototypeOf(req, orig.request);
@@ -27758,9 +27758,9 @@ var require_application = __commonJS({
27758
27758
  }, this);
27759
27759
  return this;
27760
27760
  };
27761
- app.route = function route(path7) {
27761
+ app.route = function route(path10) {
27762
27762
  this.lazyrouter();
27763
- return this._router.route(path7);
27763
+ return this._router.route(path10);
27764
27764
  };
27765
27765
  app.engine = function engine(ext, fn) {
27766
27766
  if (typeof fn !== "function") {
@@ -27811,7 +27811,7 @@ var require_application = __commonJS({
27811
27811
  }
27812
27812
  return this;
27813
27813
  };
27814
- app.path = function path7() {
27814
+ app.path = function path10() {
27815
27815
  return this.parent ? this.parent.path() + this.mountpath : "";
27816
27816
  };
27817
27817
  app.enabled = function enabled(setting) {
@@ -27827,19 +27827,19 @@ var require_application = __commonJS({
27827
27827
  return this.set(setting, false);
27828
27828
  };
27829
27829
  methods.forEach(function(method) {
27830
- app[method] = function(path7) {
27830
+ app[method] = function(path10) {
27831
27831
  if (method === "get" && arguments.length === 1) {
27832
- return this.set(path7);
27832
+ return this.set(path10);
27833
27833
  }
27834
27834
  this.lazyrouter();
27835
- var route = this._router.route(path7);
27835
+ var route = this._router.route(path10);
27836
27836
  route[method].apply(route, slice.call(arguments, 1));
27837
27837
  return this;
27838
27838
  };
27839
27839
  });
27840
- app.all = function all(path7) {
27840
+ app.all = function all(path10) {
27841
27841
  this.lazyrouter();
27842
- var route = this._router.route(path7);
27842
+ var route = this._router.route(path10);
27843
27843
  var args = slice.call(arguments, 1);
27844
27844
  for (var i = 0; i < methods.length; i++) {
27845
27845
  route[methods[i]].apply(route, args);
@@ -28598,7 +28598,7 @@ var require_request = __commonJS({
28598
28598
  var subdomains2 = !isIP(hostname3) ? hostname3.split(".").reverse() : [hostname3];
28599
28599
  return subdomains2.slice(offset);
28600
28600
  });
28601
- defineGetter(req, "path", function path7() {
28601
+ defineGetter(req, "path", function path10() {
28602
28602
  return parse3(this).pathname;
28603
28603
  });
28604
28604
  defineGetter(req, "hostname", function hostname3() {
@@ -28920,7 +28920,7 @@ var require_response = __commonJS({
28920
28920
  var http2 = require("http");
28921
28921
  var isAbsolute = require_utils2().isAbsolute;
28922
28922
  var onFinished = require_on_finished();
28923
- var path7 = require("path");
28923
+ var path10 = require("path");
28924
28924
  var statuses = require_statuses();
28925
28925
  var merge2 = require_utils_merge();
28926
28926
  var sign = require_cookie_signature().sign;
@@ -28929,9 +28929,9 @@ var require_response = __commonJS({
28929
28929
  var setCharset = require_utils2().setCharset;
28930
28930
  var cookie = require_cookie();
28931
28931
  var send = require_send();
28932
- var extname = path7.extname;
28932
+ var extname = path10.extname;
28933
28933
  var mime = send.mime;
28934
- var resolve = path7.resolve;
28934
+ var resolve = path10.resolve;
28935
28935
  var vary = require_vary();
28936
28936
  var res = Object.create(http2.ServerResponse.prototype);
28937
28937
  module2.exports = res;
@@ -29108,26 +29108,26 @@ var require_response = __commonJS({
29108
29108
  this.type("txt");
29109
29109
  return this.send(body);
29110
29110
  };
29111
- res.sendFile = function sendFile(path8, options, callback) {
29111
+ res.sendFile = function sendFile(path11, options, callback) {
29112
29112
  var done = callback;
29113
29113
  var req = this.req;
29114
29114
  var res2 = this;
29115
29115
  var next = req.next;
29116
29116
  var opts = options || {};
29117
- if (!path8) {
29117
+ if (!path11) {
29118
29118
  throw new TypeError("path argument is required to res.sendFile");
29119
29119
  }
29120
- if (typeof path8 !== "string") {
29120
+ if (typeof path11 !== "string") {
29121
29121
  throw new TypeError("path must be a string to res.sendFile");
29122
29122
  }
29123
29123
  if (typeof options === "function") {
29124
29124
  done = options;
29125
29125
  opts = {};
29126
29126
  }
29127
- if (!opts.root && !isAbsolute(path8)) {
29127
+ if (!opts.root && !isAbsolute(path11)) {
29128
29128
  throw new TypeError("path must be absolute or specify root to res.sendFile");
29129
29129
  }
29130
- var pathname = encodeURI(path8);
29130
+ var pathname = encodeURI(path11);
29131
29131
  var file2 = send(req, pathname, opts);
29132
29132
  sendfile(res2, file2, opts, function(err) {
29133
29133
  if (done) return done(err);
@@ -29137,7 +29137,7 @@ var require_response = __commonJS({
29137
29137
  }
29138
29138
  });
29139
29139
  };
29140
- res.sendfile = function(path8, options, callback) {
29140
+ res.sendfile = function(path11, options, callback) {
29141
29141
  var done = callback;
29142
29142
  var req = this.req;
29143
29143
  var res2 = this;
@@ -29147,7 +29147,7 @@ var require_response = __commonJS({
29147
29147
  done = options;
29148
29148
  opts = {};
29149
29149
  }
29150
- var file2 = send(req, path8, opts);
29150
+ var file2 = send(req, path11, opts);
29151
29151
  sendfile(res2, file2, opts, function(err) {
29152
29152
  if (done) return done(err);
29153
29153
  if (err && err.code === "EISDIR") return next();
@@ -29160,7 +29160,7 @@ var require_response = __commonJS({
29160
29160
  res.sendfile,
29161
29161
  "res.sendfile: Use res.sendFile instead"
29162
29162
  );
29163
- res.download = function download(path8, filename, options, callback) {
29163
+ res.download = function download(path11, filename, options, callback) {
29164
29164
  var done = callback;
29165
29165
  var name = filename;
29166
29166
  var opts = options || null;
@@ -29177,7 +29177,7 @@ var require_response = __commonJS({
29177
29177
  opts = filename;
29178
29178
  }
29179
29179
  var headers = {
29180
- "Content-Disposition": contentDisposition(name || path8)
29180
+ "Content-Disposition": contentDisposition(name || path11)
29181
29181
  };
29182
29182
  if (opts && opts.headers) {
29183
29183
  var keys = Object.keys(opts.headers);
@@ -29190,7 +29190,7 @@ var require_response = __commonJS({
29190
29190
  }
29191
29191
  opts = Object.create(opts);
29192
29192
  opts.headers = headers;
29193
- var fullPath = !opts.root ? resolve(path8) : path8;
29193
+ var fullPath = !opts.root ? resolve(path11) : path11;
29194
29194
  return this.sendFile(fullPath, opts, done);
29195
29195
  };
29196
29196
  res.contentType = res.type = function contentType(type) {
@@ -29491,11 +29491,11 @@ var require_serve_static = __commonJS({
29491
29491
  }
29492
29492
  var forwardError = !fallthrough;
29493
29493
  var originalUrl = parseUrl.original(req);
29494
- var path7 = parseUrl(req).pathname;
29495
- if (path7 === "/" && originalUrl.pathname.substr(-1) !== "/") {
29496
- path7 = "";
29494
+ var path10 = parseUrl(req).pathname;
29495
+ if (path10 === "/" && originalUrl.pathname.substr(-1) !== "/") {
29496
+ path10 = "";
29497
29497
  }
29498
- var stream = send(req, path7, opts);
29498
+ var stream = send(req, path10, opts);
29499
29499
  stream.on("directory", onDirectory);
29500
29500
  if (setHeaders) {
29501
29501
  stream.on("headers", setHeaders);
@@ -31834,7 +31834,7 @@ var require_websocket = __commonJS({
31834
31834
  var http2 = require("http");
31835
31835
  var net = require("net");
31836
31836
  var tls = require("tls");
31837
- var { randomBytes: randomBytes2, createHash } = require("crypto");
31837
+ var { randomBytes: randomBytes2, createHash: createHash2 } = require("crypto");
31838
31838
  var { Duplex, Readable } = require("stream");
31839
31839
  var { URL: URL2 } = require("url");
31840
31840
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -32494,7 +32494,7 @@ var require_websocket = __commonJS({
32494
32494
  abortHandshake(websocket, socket, "Invalid Upgrade header");
32495
32495
  return;
32496
32496
  }
32497
- const digest = createHash("sha1").update(key + GUID).digest("base64");
32497
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
32498
32498
  if (res.headers["sec-websocket-accept"] !== digest) {
32499
32499
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
32500
32500
  return;
@@ -32861,7 +32861,7 @@ var require_websocket_server = __commonJS({
32861
32861
  var EventEmitter = require("events");
32862
32862
  var http2 = require("http");
32863
32863
  var { Duplex } = require("stream");
32864
- var { createHash } = require("crypto");
32864
+ var { createHash: createHash2 } = require("crypto");
32865
32865
  var extension2 = require_extension();
32866
32866
  var PerMessageDeflate2 = require_permessage_deflate();
32867
32867
  var subprotocol2 = require_subprotocol();
@@ -33162,7 +33162,7 @@ var require_websocket_server = __commonJS({
33162
33162
  );
33163
33163
  }
33164
33164
  if (this._state > RUNNING) return abortHandshake(socket, 503);
33165
- const digest = createHash("sha1").update(key + GUID).digest("base64");
33165
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
33166
33166
  const headers = [
33167
33167
  "HTTP/1.1 101 Switching Protocols",
33168
33168
  "Upgrade: websocket",
@@ -33259,9 +33259,9 @@ __export(index_exports, {
33259
33259
  });
33260
33260
  module.exports = __toCommonJS(index_exports);
33261
33261
  var import_node_child_process3 = require("node:child_process");
33262
- var import_node_fs4 = require("node:fs");
33262
+ var import_node_fs6 = require("node:fs");
33263
33263
  var import_node_os4 = __toESM(require("node:os"), 1);
33264
- var import_node_path7 = __toESM(require("node:path"), 1);
33264
+ var import_node_path10 = __toESM(require("node:path"), 1);
33265
33265
  var import_node_module = require("node:module");
33266
33266
  var import_node_url = require("node:url");
33267
33267
 
@@ -33376,22 +33376,22 @@ var Daemon = class {
33376
33376
  }
33377
33377
  async install() {
33378
33378
  if (this.platform === "darwin") {
33379
- const path7 = plistPath(this.home, this.name);
33380
- if ((0, import_node_fs.existsSync)(path7)) {
33381
- await this.exec("launchctl", ["unload", "-w", path7]).catch(() => void 0);
33379
+ const path10 = plistPath(this.home, this.name);
33380
+ if ((0, import_node_fs.existsSync)(path10)) {
33381
+ await this.exec("launchctl", ["unload", "-w", path10]).catch(() => void 0);
33382
33382
  }
33383
33383
  await (0, import_promises.mkdir)((0, import_node_path.join)(this.home, "Library", "LaunchAgents"), { recursive: true });
33384
- await (0, import_promises.writeFile)(path7, renderPlist(this.name, this.programArgs, this.env), "utf8");
33385
- await this.exec("launchctl", ["load", "-w", path7]);
33384
+ await (0, import_promises.writeFile)(path10, renderPlist(this.name, this.programArgs, this.env), "utf8");
33385
+ await this.exec("launchctl", ["load", "-w", path10]);
33386
33386
  return;
33387
33387
  }
33388
33388
  if (this.platform === "linux") {
33389
- const path7 = unitPath(this.home, this.name);
33390
- if ((0, import_node_fs.existsSync)(path7)) {
33389
+ const path10 = unitPath(this.home, this.name);
33390
+ if ((0, import_node_fs.existsSync)(path10)) {
33391
33391
  await this.exec("systemctl", ["--user", "disable", "--now", `${this.name}.service`]).catch(() => void 0);
33392
33392
  }
33393
33393
  await (0, import_promises.mkdir)((0, import_node_path.join)(this.home, ".config", "systemd", "user"), { recursive: true });
33394
- await (0, import_promises.writeFile)(path7, renderUnit(this.description, this.programArgs, this.env), "utf8");
33394
+ await (0, import_promises.writeFile)(path10, renderUnit(this.description, this.programArgs, this.env), "utf8");
33395
33395
  await this.exec("systemctl", ["--user", "daemon-reload"]);
33396
33396
  await this.exec("systemctl", ["--user", "enable", "--now", `${this.name}.service`]);
33397
33397
  return;
@@ -33400,17 +33400,17 @@ var Daemon = class {
33400
33400
  }
33401
33401
  async uninstall() {
33402
33402
  if (this.platform === "darwin") {
33403
- const path7 = plistPath(this.home, this.name);
33404
- if ((0, import_node_fs.existsSync)(path7)) {
33405
- await this.exec("launchctl", ["unload", "-w", path7]).catch(() => void 0);
33406
- await (0, import_promises.rm)(path7, { force: true });
33403
+ const path10 = plistPath(this.home, this.name);
33404
+ if ((0, import_node_fs.existsSync)(path10)) {
33405
+ await this.exec("launchctl", ["unload", "-w", path10]).catch(() => void 0);
33406
+ await (0, import_promises.rm)(path10, { force: true });
33407
33407
  }
33408
33408
  return;
33409
33409
  }
33410
33410
  if (this.platform === "linux") {
33411
- const path7 = unitPath(this.home, this.name);
33411
+ const path10 = unitPath(this.home, this.name);
33412
33412
  await this.exec("systemctl", ["--user", "disable", "--now", `${this.name}.service`]).catch(() => void 0);
33413
- await (0, import_promises.rm)(path7, { force: true });
33413
+ await (0, import_promises.rm)(path10, { force: true });
33414
33414
  await this.exec("systemctl", ["--user", "daemon-reload"]);
33415
33415
  return;
33416
33416
  }
@@ -33418,8 +33418,8 @@ var Daemon = class {
33418
33418
  }
33419
33419
  async status() {
33420
33420
  if (this.platform === "darwin") {
33421
- const path7 = plistPath(this.home, this.name);
33422
- if (!(0, import_node_fs.existsSync)(path7))
33421
+ const path10 = plistPath(this.home, this.name);
33422
+ if (!(0, import_node_fs.existsSync)(path10))
33423
33423
  return { installed: false, running: false };
33424
33424
  try {
33425
33425
  await this.exec("launchctl", ["list", this.name]);
@@ -33429,8 +33429,8 @@ var Daemon = class {
33429
33429
  }
33430
33430
  }
33431
33431
  if (this.platform === "linux") {
33432
- const path7 = unitPath(this.home, this.name);
33433
- if (!(0, import_node_fs.existsSync)(path7))
33432
+ const path10 = unitPath(this.home, this.name);
33433
+ if (!(0, import_node_fs.existsSync)(path10))
33434
33434
  return { installed: false, running: false };
33435
33435
  try {
33436
33436
  const result = await this.exec("systemctl", [
@@ -33765,8 +33765,9 @@ var ArtifactContentChangedEvent = defineEvent();
33765
33765
  var ScreenCreatedEvent = defineEvent();
33766
33766
  var ScreenUpdatedEvent = defineEvent();
33767
33767
  var ScreenRemovedEvent = defineEvent();
33768
- var ViewerActiveScreenChangedEvent = defineEvent();
33769
- var ViewerFocusEvent = defineEvent();
33768
+ var ScreenChangedEvent = defineEvent();
33769
+ var ArtifactFocusEvent = defineEvent();
33770
+ var ThemeChangedEvent = defineEvent();
33770
33771
 
33771
33772
  // ../shared/src/errors.ts
33772
33773
  function defineError(name) {
@@ -33827,8 +33828,13 @@ var HttpRequester = class {
33827
33828
  }
33828
33829
  return payload;
33829
33830
  }
33830
- async requestVoid(method, pathname) {
33831
- const response = await this.request(method, pathname);
33831
+ async requestVoid(method, pathname, body) {
33832
+ const response = await this.request(
33833
+ method,
33834
+ pathname,
33835
+ body !== void 0 ? JSON.stringify(body) : void 0,
33836
+ body !== void 0 ? { "content-type": "application/json" } : void 0
33837
+ );
33832
33838
  if (!response.ok) {
33833
33839
  throw this.createError(
33834
33840
  await extractErrorMessage(response, response.statusText),
@@ -34062,25 +34068,24 @@ var ArtifactClient = class {
34062
34068
  );
34063
34069
  }
34064
34070
  };
34065
- var ViewerClient = class {
34071
+ var DisplayClient = class {
34066
34072
  #http;
34067
34073
  constructor(http2) {
34068
34074
  this.#http = http2;
34069
34075
  }
34070
- state() {
34071
- return this.#http.requestJSON("GET", "/viewer/state");
34076
+ get() {
34077
+ return this.#http.requestJSON("GET", "/display");
34072
34078
  }
34073
- setActive(input) {
34074
- return this.#http.requestJSON(
34075
- "PUT",
34076
- "/viewer/active-screen",
34077
- { screenID: input.screenID }
34078
- );
34079
+ patch(input) {
34080
+ const body = {};
34081
+ if (input.activeScreenID !== void 0) body.activeScreenID = input.activeScreenID;
34082
+ if (input.activeThemeName !== void 0) body.activeThemeName = input.activeThemeName;
34083
+ return this.#http.requestVoid("PATCH", "/display", body);
34079
34084
  }
34080
34085
  focus(input) {
34081
34086
  const body = { artifactID: input.artifactID };
34082
34087
  if (input.screenID !== void 0) body.screenID = input.screenID;
34083
- return this.#http.requestJSON("POST", "/viewer/focus", body);
34088
+ return this.#http.requestJSON("POST", "/display/focus", body);
34084
34089
  }
34085
34090
  };
34086
34091
  var ViewClient = class {
@@ -34096,14 +34101,14 @@ var ViewClient = class {
34096
34101
  var TelevisionClient = class {
34097
34102
  screens;
34098
34103
  artifacts;
34099
- viewer;
34104
+ display;
34100
34105
  views;
34101
34106
  #http;
34102
34107
  constructor(serverURL, options = {}) {
34103
34108
  this.#http = new HttpRequester(serverURL, options);
34104
34109
  this.screens = new ScreenClient(this.#http);
34105
34110
  this.artifacts = new ArtifactClient(this.#http);
34106
- this.viewer = new ViewerClient(this.#http);
34111
+ this.display = new DisplayClient(this.#http);
34107
34112
  this.views = new ViewClient(this.#http);
34108
34113
  }
34109
34114
  health() {
@@ -34170,8 +34175,8 @@ function isVitestRuntime() {
34170
34175
 
34171
34176
  // ../server/src/routes.ts
34172
34177
  var import_express = __toESM(require_express2(), 1);
34173
- var import_node_fs2 = require("node:fs");
34174
- var import_node_path4 = __toESM(require("node:path"), 1);
34178
+ var import_node_fs3 = require("node:fs");
34179
+ var import_node_path5 = __toESM(require("node:path"), 1);
34175
34180
 
34176
34181
  // ../../node_modules/zod/v4/classic/external.js
34177
34182
  var external_exports = {};
@@ -34940,10 +34945,10 @@ function mergeDefs(...defs) {
34940
34945
  function cloneDef(schema) {
34941
34946
  return mergeDefs(schema._zod.def);
34942
34947
  }
34943
- function getElementAtPath(obj, path7) {
34944
- if (!path7)
34948
+ function getElementAtPath(obj, path10) {
34949
+ if (!path10)
34945
34950
  return obj;
34946
- return path7.reduce((acc, key) => acc?.[key], obj);
34951
+ return path10.reduce((acc, key) => acc?.[key], obj);
34947
34952
  }
34948
34953
  function promiseAllObject(promisesObj) {
34949
34954
  const keys = Object.keys(promisesObj);
@@ -35326,11 +35331,11 @@ function aborted(x, startIndex = 0) {
35326
35331
  }
35327
35332
  return false;
35328
35333
  }
35329
- function prefixIssues(path7, issues) {
35334
+ function prefixIssues(path10, issues) {
35330
35335
  return issues.map((iss) => {
35331
35336
  var _a2;
35332
35337
  (_a2 = iss).path ?? (_a2.path = []);
35333
- iss.path.unshift(path7);
35338
+ iss.path.unshift(path10);
35334
35339
  return iss;
35335
35340
  });
35336
35341
  }
@@ -35513,7 +35518,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
35513
35518
  }
35514
35519
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
35515
35520
  const result = { errors: [] };
35516
- const processError = (error49, path7 = []) => {
35521
+ const processError = (error49, path10 = []) => {
35517
35522
  var _a2, _b;
35518
35523
  for (const issue2 of error49.issues) {
35519
35524
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -35523,7 +35528,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
35523
35528
  } else if (issue2.code === "invalid_element") {
35524
35529
  processError({ issues: issue2.issues }, issue2.path);
35525
35530
  } else {
35526
- const fullpath = [...path7, ...issue2.path];
35531
+ const fullpath = [...path10, ...issue2.path];
35527
35532
  if (fullpath.length === 0) {
35528
35533
  result.errors.push(mapper(issue2));
35529
35534
  continue;
@@ -35555,8 +35560,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
35555
35560
  }
35556
35561
  function toDotPath(_path) {
35557
35562
  const segs = [];
35558
- const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
35559
- for (const seg of path7) {
35563
+ const path10 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
35564
+ for (const seg of path10) {
35560
35565
  if (typeof seg === "number")
35561
35566
  segs.push(`[${seg}]`);
35562
35567
  else if (typeof seg === "symbol")
@@ -47533,13 +47538,13 @@ function resolveRef(ref, ctx) {
47533
47538
  if (!ref.startsWith("#")) {
47534
47539
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
47535
47540
  }
47536
- const path7 = ref.slice(1).split("/").filter(Boolean);
47537
- if (path7.length === 0) {
47541
+ const path10 = ref.slice(1).split("/").filter(Boolean);
47542
+ if (path10.length === 0) {
47538
47543
  return ctx.rootSchema;
47539
47544
  }
47540
47545
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
47541
- if (path7[0] === defsKey) {
47542
- const key = path7[1];
47546
+ if (path10[0] === defsKey) {
47547
+ const key = path10[1];
47543
47548
  if (!key || !ctx.defs[key]) {
47544
47549
  throw new Error(`Reference not found: ${ref}`);
47545
47550
  }
@@ -48046,6 +48051,70 @@ function getArtifactEntryFilePath(storagePath, artifactID, type, state) {
48046
48051
  return import_node_path3.default.join(getArtifactPublicDirPath(storagePath, artifactID, state), `index.${extension2}`);
48047
48052
  }
48048
48053
 
48054
+ // ../server/src/themes.ts
48055
+ var import_node_fs2 = require("node:fs");
48056
+ var import_node_path4 = __toESM(require("node:path"), 1);
48057
+ var HTTP_NOT_FOUND_STATUS = 404;
48058
+ var THEME_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
48059
+ var THEME_ENTRY_FILE = "theme.css";
48060
+ var URL_RE = /url\(\s*(["']?)([^"')\s]+)\1\s*\)/g;
48061
+ function isValidThemeName(name) {
48062
+ return THEME_NAME_PATTERN.test(name);
48063
+ }
48064
+ function assertValidThemeName(name) {
48065
+ if (!isValidThemeName(name)) {
48066
+ throw new Error(`Invalid theme name: ${name}`);
48067
+ }
48068
+ }
48069
+ function getThemesDir(storagePath) {
48070
+ return import_node_path4.default.join(storagePath, "themes");
48071
+ }
48072
+ function getThemeDir(storagePath, name) {
48073
+ assertValidThemeName(name);
48074
+ return import_node_path4.default.join(getThemesDir(storagePath), name);
48075
+ }
48076
+ function getThemeEntryPath(storagePath, name) {
48077
+ return import_node_path4.default.join(getThemeDir(storagePath, name), THEME_ENTRY_FILE);
48078
+ }
48079
+ function themeExists(storagePath, name) {
48080
+ if (!isValidThemeName(name)) {
48081
+ return false;
48082
+ }
48083
+ try {
48084
+ return (0, import_node_fs2.statSync)(getThemeEntryPath(storagePath, name)).isFile();
48085
+ } catch {
48086
+ return false;
48087
+ }
48088
+ }
48089
+ function rewriteThemeURLs(css, themeName) {
48090
+ return css.replace(URL_RE, (whole, quote, url2) => {
48091
+ if (/^(data:|https?:)/.test(url2) || url2.startsWith("/")) {
48092
+ return whole;
48093
+ }
48094
+ const rewritten = `/themes/${themeName}/${url2}`;
48095
+ return `url(${quote}${rewritten}${quote})`;
48096
+ });
48097
+ }
48098
+ function serveThemeCSS(storagePath) {
48099
+ return (req, res, next) => {
48100
+ const { name } = req.params;
48101
+ if (!isValidThemeName(name) || !themeExists(storagePath, name)) {
48102
+ res.sendStatus(HTTP_NOT_FOUND_STATUS);
48103
+ return;
48104
+ }
48105
+ try {
48106
+ const css = (0, import_node_fs2.readFileSync)(getThemeEntryPath(storagePath, name), "utf8");
48107
+ res.type("text/css").setHeader("Cache-Control", "no-cache").send(rewriteThemeURLs(css, name));
48108
+ } catch (error48) {
48109
+ if (error48.code === "ENOENT") {
48110
+ res.sendStatus(HTTP_NOT_FOUND_STATUS);
48111
+ return;
48112
+ }
48113
+ next(error48);
48114
+ }
48115
+ };
48116
+ }
48117
+
48049
48118
  // ../server/src/routes.ts
48050
48119
  var layoutWidthSchema = external_exports.union([external_exports.number(), external_exports.literal("auto")]);
48051
48120
  var layoutHeightSchema = external_exports.union([external_exports.number(), external_exports.literal("auto")]);
@@ -48070,11 +48139,10 @@ var stackNodeSchema = external_exports.object({
48070
48139
  var layoutNodeSchema = external_exports.union([cardNodeSchema, rowNodeSchema, stackNodeSchema]);
48071
48140
  var patchScreenSchema = external_exports.object({
48072
48141
  name: external_exports.string().optional(),
48073
- layout: external_exports.array(layoutNodeSchema).optional(),
48074
- pinned: external_exports.boolean().optional()
48142
+ layout: external_exports.array(layoutNodeSchema).optional()
48075
48143
  }).refine(
48076
- (value) => value.name !== void 0 || value.layout !== void 0 || value.pinned !== void 0,
48077
- { message: "At least one of name, layout, or pinned must be provided" }
48144
+ (value) => value.name !== void 0 || value.layout !== void 0,
48145
+ { message: "At least one of name or layout must be provided" }
48078
48146
  );
48079
48147
  var createScreenSchema = external_exports.object({
48080
48148
  name: external_exports.string(),
@@ -48099,7 +48167,10 @@ var createArtifactBodySchema = external_exports.object({
48099
48167
  var patchArtifactSchema = external_exports.object({
48100
48168
  title: external_exports.string().optional()
48101
48169
  }).refine((value) => value.title !== void 0, { message: "title is required" });
48102
- var setActiveScreenSchema = external_exports.object({ screenID: external_exports.string() }).strict();
48170
+ var patchDisplaySchema = external_exports.object({
48171
+ activeScreenID: external_exports.string().nullable().optional(),
48172
+ activeThemeName: external_exports.string().regex(THEME_NAME_PATTERN).nullable().optional()
48173
+ }).strict();
48103
48174
  var focusSchema = external_exports.object({
48104
48175
  artifactID: external_exports.string(),
48105
48176
  screenID: external_exports.string().optional()
@@ -48146,12 +48217,12 @@ function handleStoreError(res, error48, fallback) {
48146
48217
  throw error48 instanceof Error ? error48 : new Error(fallback);
48147
48218
  }
48148
48219
  function registerRoutes(app, store, options) {
48149
- const getConnectedClientCount = options.getConnectedClientCount ?? (() => 0);
48150
48220
  const auth = options.requireAuth ? [options.requireAuth] : [];
48151
48221
  app.use("/screens", applyCorsMiddleware);
48152
48222
  app.use("/artifacts", applyCorsMiddleware);
48153
48223
  app.use("/views", applyCorsMiddleware);
48154
- app.use("/viewer", applyCorsMiddleware);
48224
+ app.use("/display", applyCorsMiddleware);
48225
+ app.use("/themes", applyCorsMiddleware);
48155
48226
  app.options("/screens", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48156
48227
  app.options("/screens/:id", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48157
48228
  app.options("/screens/:id/artifact", (_req, res) => res.status(HTTP_NO_CONTENT).end());
@@ -48159,9 +48230,10 @@ function registerRoutes(app, store, options) {
48159
48230
  app.options("/artifacts", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48160
48231
  app.options("/artifacts/:id/*", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48161
48232
  app.options("/artifacts/:id", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48162
- app.options("/viewer/state", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48163
- app.options("/viewer/active-screen", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48164
- app.options("/viewer/focus", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48233
+ app.options("/display", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48234
+ app.options("/display/focus", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48235
+ app.options("/themes/:name/theme.css", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48236
+ app.options("/themes/:name/*", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48165
48237
  app.options("/views", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48166
48238
  app.options("/views/:id", (_req, res) => res.status(HTTP_NO_CONTENT).end());
48167
48239
  app.options("/views/:id/*", (_req, res) => res.status(HTTP_NO_CONTENT).end());
@@ -48424,18 +48496,18 @@ function registerRoutes(app, store, options) {
48424
48496
  return;
48425
48497
  }
48426
48498
  const rawSubPath = req.params["0"] ?? "";
48427
- const requestedPath = import_node_path4.default.resolve(publicDir, rawSubPath);
48499
+ const requestedPath = import_node_path5.default.resolve(publicDir, rawSubPath);
48428
48500
  let realPublicDir;
48429
48501
  let realRequestedPath;
48430
48502
  try {
48431
- realPublicDir = (0, import_node_fs2.realpathSync)(publicDir);
48432
- realRequestedPath = (0, import_node_fs2.realpathSync)(requestedPath);
48503
+ realPublicDir = (0, import_node_fs3.realpathSync)(publicDir);
48504
+ realRequestedPath = (0, import_node_fs3.realpathSync)(requestedPath);
48433
48505
  } catch {
48434
48506
  sendError(res, HTTP_NOT_FOUND, "Not found");
48435
48507
  return;
48436
48508
  }
48437
- const relativePath = import_node_path4.default.relative(realPublicDir, realRequestedPath);
48438
- if (relativePath.startsWith("..") || import_node_path4.default.isAbsolute(relativePath)) {
48509
+ const relativePath = import_node_path5.default.relative(realPublicDir, realRequestedPath);
48510
+ if (relativePath.startsWith("..") || import_node_path5.default.isAbsolute(relativePath)) {
48439
48511
  sendError(res, HTTP_NOT_FOUND, "Not found");
48440
48512
  return;
48441
48513
  }
@@ -48462,26 +48534,23 @@ function registerRoutes(app, store, options) {
48462
48534
  app.put("/artifacts/:id/content/*", ...auth, (_req, res) => {
48463
48535
  sendError(res, HTTP_FORBIDDEN, "Only the primary content file is HTTP-writable");
48464
48536
  });
48465
- app.get("/viewer/state", ...auth, (_req, res) => {
48466
- res.json({
48467
- activeScreenID: store.getActiveScreenID(),
48468
- connectedClients: getConnectedClientCount()
48469
- });
48537
+ app.get("/display", ...auth, (_req, res) => {
48538
+ res.json(store.getDisplayState());
48470
48539
  });
48471
- app.put("/viewer/active-screen", ...auth, (req, res) => {
48472
- const parsed = setActiveScreenSchema.safeParse(req.body);
48540
+ app.patch("/display", ...auth, (req, res) => {
48541
+ const parsed = patchDisplaySchema.safeParse(req.body);
48473
48542
  if (!parsed.success) {
48474
48543
  sendError(res, HTTP_BAD_REQUEST, parsed.error.issues[0]?.message ?? "Invalid request body");
48475
48544
  return;
48476
48545
  }
48477
48546
  try {
48478
- store.setActiveScreen(parsed.data.screenID);
48479
- res.status(HTTP_OK2).json({ activeScreenID: parsed.data.screenID });
48547
+ store.patchDisplay(parsed.data);
48548
+ res.status(HTTP_NO_CONTENT).end();
48480
48549
  } catch (error48) {
48481
- handleStoreError(res, error48, "Failed to set active screen");
48550
+ handleStoreError(res, error48, "Failed to patch display");
48482
48551
  }
48483
48552
  });
48484
- app.post("/viewer/focus", ...auth, (req, res) => {
48553
+ app.post("/display/focus", ...auth, (req, res) => {
48485
48554
  const parsed = focusSchema.safeParse(req.body);
48486
48555
  if (!parsed.success) {
48487
48556
  sendError(res, HTTP_BAD_REQUEST, parsed.error.issues[0]?.message ?? "Invalid request body");
@@ -48516,7 +48585,7 @@ function registerRoutes(app, store, options) {
48516
48585
  sendError(res, HTTP_NOT_FOUND, "Not found");
48517
48586
  return;
48518
48587
  }
48519
- res.sendFile(import_node_path4.default.join(viewDir, "index.html"), (error48) => {
48588
+ res.sendFile(import_node_path5.default.join(viewDir, "index.html"), (error48) => {
48520
48589
  if (error48) sendError(res, HTTP_NOT_FOUND, "Not found");
48521
48590
  });
48522
48591
  };
@@ -48682,11 +48751,14 @@ var EventStreamServer = class extends withDisposable(class {
48682
48751
  const onScreenRemoved = (event) => {
48683
48752
  this.broadcast({ type: "screen-removed", screenID: event.screenID });
48684
48753
  };
48685
- const onViewerActiveScreenChanged = (event) => {
48686
- this.broadcast({ type: "viewer-active-screen-changed", screenID: event.screenID });
48754
+ const onScreenChanged = (event) => {
48755
+ this.broadcast({ type: "screen-changed", screenID: event.screenID });
48687
48756
  };
48688
- const onViewerFocus = (event) => {
48689
- this.broadcast({ type: "viewer-focus", screenID: event.screenID, artifactID: event.artifactID });
48757
+ const onThemeChanged = (event) => {
48758
+ this.broadcast({ type: "theme-changed", themeName: event.themeName });
48759
+ };
48760
+ const onArtifactFocus = (event) => {
48761
+ this.broadcast({ type: "artifact-focus", screenID: event.screenID, artifactID: event.artifactID });
48690
48762
  };
48691
48763
  this.store.addEventListener("artifact-created", onArtifactCreated);
48692
48764
  this.store.addEventListener("artifact-attached", onArtifactAttached);
@@ -48696,8 +48768,9 @@ var EventStreamServer = class extends withDisposable(class {
48696
48768
  this.store.addEventListener("screen-created", onScreenCreated);
48697
48769
  this.store.addEventListener("screen-updated", onScreenUpdated);
48698
48770
  this.store.addEventListener("screen-removed", onScreenRemoved);
48699
- this.store.addEventListener("viewer-active-screen-changed", onViewerActiveScreenChanged);
48700
- this.store.addEventListener("viewer-focus", onViewerFocus);
48771
+ this.store.addEventListener("screen-changed", onScreenChanged);
48772
+ this.store.addEventListener("theme-changed", onThemeChanged);
48773
+ this.store.addEventListener("artifact-focus", onArtifactFocus);
48701
48774
  this.unsubscribe.push(
48702
48775
  () => this.store.removeEventListener("artifact-created", onArtifactCreated),
48703
48776
  () => this.store.removeEventListener("artifact-attached", onArtifactAttached),
@@ -48707,8 +48780,9 @@ var EventStreamServer = class extends withDisposable(class {
48707
48780
  () => this.store.removeEventListener("screen-created", onScreenCreated),
48708
48781
  () => this.store.removeEventListener("screen-updated", onScreenUpdated),
48709
48782
  () => this.store.removeEventListener("screen-removed", onScreenRemoved),
48710
- () => this.store.removeEventListener("viewer-active-screen-changed", onViewerActiveScreenChanged),
48711
- () => this.store.removeEventListener("viewer-focus", onViewerFocus)
48783
+ () => this.store.removeEventListener("screen-changed", onScreenChanged),
48784
+ () => this.store.removeEventListener("theme-changed", onThemeChanged),
48785
+ () => this.store.removeEventListener("artifact-focus", onArtifactFocus)
48712
48786
  );
48713
48787
  }
48714
48788
  broadcast(event) {
@@ -48725,7 +48799,7 @@ var EventStreamServer = class extends withDisposable(class {
48725
48799
  var import_node_child_process2 = require("node:child_process");
48726
48800
  var import_promises2 = require("node:fs/promises");
48727
48801
  var import_node_os3 = require("node:os");
48728
- var import_node_path5 = __toESM(require("node:path"), 1);
48802
+ var import_node_path6 = __toESM(require("node:path"), 1);
48729
48803
  var launchACPProcess = (command, args, options) => {
48730
48804
  const child = (0, import_node_child_process2.spawn)(command, args, options);
48731
48805
  const directKill = child.kill.bind(child);
@@ -48747,7 +48821,7 @@ var launchACPProcess = (command, args, options) => {
48747
48821
  return child;
48748
48822
  };
48749
48823
  var ACP_STOP_TIMEOUT_MS = 5e3;
48750
- var ACP_STUB_CWD = import_node_path5.default.join((0, import_node_os3.homedir)(), ".television", "acp");
48824
+ var ACP_STUB_CWD = import_node_path6.default.join((0, import_node_os3.homedir)(), ".television", "acp");
48751
48825
  var ACPBridge = class extends withDisposable(class {
48752
48826
  }) {
48753
48827
  child = null;
@@ -48764,7 +48838,7 @@ var ACPBridge = class extends withDisposable(class {
48764
48838
  this.send = options.send;
48765
48839
  this.profile = options.profile ?? resolveACPAgentProfile();
48766
48840
  this.launchProcess = options.launchProcess ?? launchACPProcess;
48767
- this.sessionCwd = import_node_path5.default.resolve(process.cwd());
48841
+ this.sessionCwd = import_node_path6.default.resolve(process.cwd());
48768
48842
  }
48769
48843
  handleClientMessage(message) {
48770
48844
  switch (message.type) {
@@ -48967,6 +49041,85 @@ var ACPServer = class extends withDisposable(class {
48967
49041
  }
48968
49042
  };
48969
49043
 
49044
+ // ../server/src/canonical.ts
49045
+ var import_node_crypto3 = require("node:crypto");
49046
+ var import_node_fs4 = require("node:fs");
49047
+ var import_node_path7 = __toESM(require("node:path"), 1);
49048
+ var CACHE_CONTROL_NO_CACHE = "no-cache";
49049
+ var HTTP_NOT_MODIFIED_STATUS = 304;
49050
+ var HASH_HEX_LENGTH = 8;
49051
+ var NO_THEME_HASH = "none";
49052
+ var CANONICAL_STYLESHEET = "styles.css";
49053
+ function serveCanonicalStyles(options) {
49054
+ const bundle = loadCanonicalBundle(options.canonicalDir);
49055
+ return (req, res, next) => {
49056
+ try {
49057
+ const response = buildCanonicalResponse(bundle, options.store);
49058
+ res.type("text/css");
49059
+ res.setHeader("Cache-Control", CACHE_CONTROL_NO_CACHE);
49060
+ res.setHeader("ETag", response.etag);
49061
+ if (matchesIfNoneMatch(req.headers["if-none-match"], response.etag)) {
49062
+ res.status(HTTP_NOT_MODIFIED_STATUS).end();
49063
+ return;
49064
+ }
49065
+ res.send(response.body);
49066
+ } catch (error48) {
49067
+ next(error48);
49068
+ }
49069
+ };
49070
+ }
49071
+ function loadCanonicalBundle(canonicalDir) {
49072
+ const css = (0, import_node_fs4.readFileSync)(import_node_path7.default.join(canonicalDir, CANONICAL_STYLESHEET), "utf8");
49073
+ return { css, hash: hashContent(css) };
49074
+ }
49075
+ function buildCanonicalResponse(bundle, store) {
49076
+ const activeThemeName = store.getActiveThemeName();
49077
+ if (activeThemeName === null) {
49078
+ return { body: bundle.css, etag: buildETag(bundle.hash, NO_THEME_HASH) };
49079
+ }
49080
+ if (!themeExists(store.storagePath, activeThemeName)) {
49081
+ logMissingActiveTheme(store.storagePath, activeThemeName);
49082
+ return { body: bundle.css, etag: buildETag(bundle.hash, NO_THEME_HASH) };
49083
+ }
49084
+ try {
49085
+ const themeCSS = (0, import_node_fs4.readFileSync)(getThemeEntryPath(store.storagePath, activeThemeName), "utf8");
49086
+ return {
49087
+ body: `${bundle.css}
49088
+
49089
+ /* active theme: ${activeThemeName} */
49090
+ ${rewriteThemeURLs(themeCSS, activeThemeName)}`,
49091
+ etag: buildETag(bundle.hash, hashContent(themeCSS))
49092
+ };
49093
+ } catch (error48) {
49094
+ if (error48.code === "ENOENT") {
49095
+ logMissingActiveTheme(store.storagePath, activeThemeName);
49096
+ return { body: bundle.css, etag: buildETag(bundle.hash, NO_THEME_HASH) };
49097
+ }
49098
+ throw error48;
49099
+ }
49100
+ }
49101
+ function buildETag(bundleHash, themeHash) {
49102
+ return `W/"v1-${bundleHash}-${themeHash}"`;
49103
+ }
49104
+ function hashContent(content) {
49105
+ return (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, HASH_HEX_LENGTH);
49106
+ }
49107
+ function matchesIfNoneMatch(header, etag) {
49108
+ if (!header) {
49109
+ return false;
49110
+ }
49111
+ const rawValues = Array.isArray(header) ? header : [header];
49112
+ return rawValues.some(
49113
+ (rawValue) => rawValue.split(",").map((value) => value.trim()).some((value) => value === "*" || value === etag)
49114
+ );
49115
+ }
49116
+ function logMissingActiveTheme(storagePath, themeName) {
49117
+ const location = isValidThemeName(themeName) ? getThemeEntryPath(storagePath, themeName) : `(invalid theme name: ${themeName})`;
49118
+ console.warn(
49119
+ `[canonical] Active theme "${themeName}" missing at ${location}; serving canonical bundle only.`
49120
+ );
49121
+ }
49122
+
48970
49123
  // ../server/src/server.ts
48971
49124
  var HTTP_UNAUTHORIZED_STATUS = 401;
48972
49125
  var Server = class {
@@ -48993,12 +49146,18 @@ var Server = class {
48993
49146
  this.app.use(import_express2.default.json());
48994
49147
  this.events = new EventStreamServer({ store: this.store, publicServer: this.publicServer });
48995
49148
  registerRoutes(this.app, this.store, {
48996
- requireAuth: this.publicServer ? null : this.requireAuthorization,
48997
- getConnectedClientCount: () => this.events.getConnectedClientCount()
49149
+ requireAuth: this.publicServer ? null : this.requireAuthorization
48998
49150
  });
48999
49151
  if (options.canonicalDir) {
49152
+ this.app.get(
49153
+ "/canonical/v1/styles.css",
49154
+ serveCanonicalStyles({ canonicalDir: options.canonicalDir, store: this.store })
49155
+ );
49000
49156
  this.app.use("/canonical/v1", import_express2.default.static(options.canonicalDir));
49001
49157
  }
49158
+ const themesDir = getThemesDir(this.store.storagePath);
49159
+ this.app.get("/themes/:name/theme.css", serveThemeCSS(this.store.storagePath));
49160
+ this.app.use("/themes", import_express2.default.static(themesDir));
49002
49161
  if (options.staticDir) {
49003
49162
  this.app.use(import_express2.default.static(options.staticDir));
49004
49163
  }
@@ -49075,8 +49234,24 @@ var Server = class {
49075
49234
  };
49076
49235
 
49077
49236
  // ../server/src/server-store.ts
49078
- var import_node_fs3 = require("node:fs");
49079
- var import_node_path6 = __toESM(require("node:path"), 1);
49237
+ var import_node_fs5 = require("node:fs");
49238
+ var import_node_path9 = __toESM(require("node:path"), 1);
49239
+
49240
+ // ../server/src/file-watcher.ts
49241
+ var fs = __toESM(require("node:fs"), 1);
49242
+ var import_node_path8 = __toESM(require("node:path"), 1);
49243
+ var defaultWatchContentFile = (filePath, onChange) => {
49244
+ const dir = import_node_path8.default.dirname(filePath);
49245
+ const basename = import_node_path8.default.basename(filePath);
49246
+ const watcher = fs.watch(dir, (_event, filename) => {
49247
+ if (filename !== null && filename.toString() === basename) {
49248
+ onChange();
49249
+ }
49250
+ });
49251
+ return watcher;
49252
+ };
49253
+
49254
+ // ../server/src/server-store.ts
49080
49255
  var JSON_INDENT_SPACES = 2;
49081
49256
  var CONTENT_WATCH_DEBOUNCE_MS = 100;
49082
49257
  var JSON_FILE_SUFFIX = ".json";
@@ -49098,26 +49273,33 @@ var ServerStore = class extends EventTarget {
49098
49273
  authToken;
49099
49274
  screens = /* @__PURE__ */ new Map();
49100
49275
  activeScreenID = null;
49276
+ activeThemeName = null;
49101
49277
  _artifacts = /* @__PURE__ */ new Map();
49102
49278
  contentWatchers = /* @__PURE__ */ new Map();
49103
49279
  contentWatchDebounceTimers = /* @__PURE__ */ new Map();
49280
+ themeWatcher = null;
49281
+ themeWatcherFor = null;
49282
+ themeWatchDebounceTimer = null;
49104
49283
  watchContentFile;
49105
49284
  bundledViewsPath;
49106
49285
  views;
49107
49286
  constructor(options) {
49108
49287
  super();
49109
49288
  this.storagePath = options.storagePath;
49110
- this.watchContentFile = options.watchContentFile ?? ((contentPath, onChange) => (0, import_node_fs3.watch)(contentPath, onChange));
49111
- this.bundledViewsPath = options.bundledViewsPath && (0, import_node_fs3.existsSync)(options.bundledViewsPath) ? options.bundledViewsPath : null;
49289
+ this.watchContentFile = options.watchContentFile ?? defaultWatchContentFile;
49290
+ this.bundledViewsPath = options.bundledViewsPath && (0, import_node_fs5.existsSync)(options.bundledViewsPath) ? options.bundledViewsPath : null;
49112
49291
  this.ensureDirectories();
49113
49292
  this.views = this.scanViewRegistry();
49114
49293
  this.authToken = this.loadOrCreateAuthToken();
49115
49294
  this.load();
49116
- this.loadViewerState();
49117
- this.startContentWatchersForLoadedArtifacts();
49118
49295
  if (this.screens.size === 0) {
49119
49296
  this.createDefaultScreen();
49120
49297
  }
49298
+ if (!this.loadDisplayState()) {
49299
+ this.initializeDisplayState();
49300
+ }
49301
+ this.startContentWatchersForLoadedArtifacts();
49302
+ this.startWatchingActiveTheme();
49121
49303
  }
49122
49304
  /**
49123
49305
  * Snapshot of the view registry discovered at startup. Stable for the
@@ -49144,7 +49326,7 @@ var ServerStore = class extends EventTarget {
49144
49326
  if (!this.views.some((view) => view.id === id)) {
49145
49327
  return null;
49146
49328
  }
49147
- return import_node_path6.default.join(this.bundledViewsPath, id);
49329
+ return import_node_path9.default.join(this.bundledViewsPath, id);
49148
49330
  }
49149
49331
  getScreen(id) {
49150
49332
  const screen = this.screens.get(id);
@@ -49160,7 +49342,7 @@ var ServerStore = class extends EventTarget {
49160
49342
  return this._artifacts.get(id);
49161
49343
  }
49162
49344
  hasTrashedArtifact(id) {
49163
- return (0, import_node_fs3.existsSync)(getArtifactTrashMetadataPath(this.storagePath, id));
49345
+ return (0, import_node_fs5.existsSync)(getArtifactTrashMetadataPath(this.storagePath, id));
49164
49346
  }
49165
49347
  /**
49166
49348
  * Resolve the absolute filesystem path where an artifact's primary content
@@ -49201,11 +49383,11 @@ var ServerStore = class extends EventTarget {
49201
49383
  throw new NotFoundError(`Artifact not found: ${id}`, { entityType: "artifact", entityID: id });
49202
49384
  }
49203
49385
  try {
49204
- (0, import_node_fs3.accessSync)(contentPath, import_node_fs3.constants.R_OK);
49386
+ (0, import_node_fs5.accessSync)(contentPath, import_node_fs5.constants.R_OK);
49205
49387
  } catch {
49206
49388
  throw new NotFoundError(`Artifact content not readable: ${id}`, { entityType: "artifact content", entityID: id });
49207
49389
  }
49208
- return (0, import_node_fs3.readFileSync)(contentPath);
49390
+ return (0, import_node_fs5.readFileSync)(contentPath);
49209
49391
  }
49210
49392
  /**
49211
49393
  * Replace an artifact's content at the resolved content path.
@@ -49231,10 +49413,10 @@ var ServerStore = class extends EventTarget {
49231
49413
  `Artifact ${id} has no committed content yet. Use commit or abandon after writing the pending bundle`
49232
49414
  );
49233
49415
  }
49234
- if (!(0, import_node_fs3.existsSync)(import_node_path6.default.dirname(contentPath))) {
49416
+ if (!(0, import_node_fs5.existsSync)(import_node_path9.default.dirname(contentPath))) {
49235
49417
  throw new NotFoundError(`Artifact content not readable: ${id}`, { entityType: "artifact content", entityID: id });
49236
49418
  }
49237
- (0, import_node_fs3.writeFileSync)(contentPath, content);
49419
+ (0, import_node_fs5.writeFileSync)(contentPath, content);
49238
49420
  }
49239
49421
  listScreens() {
49240
49422
  return [...this.screens.values()];
@@ -49310,7 +49492,7 @@ var ServerStore = class extends EventTarget {
49310
49492
  if (artifact.status === "pending") {
49311
49493
  throw new InvalidRequestError(`Artifact ${artifactID} already has pending work. Use commit or abandon before editing again`);
49312
49494
  }
49313
- (0, import_node_fs3.cpSync)(
49495
+ (0, import_node_fs5.cpSync)(
49314
49496
  getArtifactCommittedBundlePath(this.storagePath, artifactID),
49315
49497
  getArtifactPendingBundlePath(this.storagePath, artifactID),
49316
49498
  { recursive: true }
@@ -49337,9 +49519,9 @@ var ServerStore = class extends EventTarget {
49337
49519
  const pendingPath = getArtifactPendingBundlePath(this.storagePath, artifactID);
49338
49520
  const committedPath = getArtifactCommittedBundlePath(this.storagePath, artifactID);
49339
49521
  this.stopWatchingArtifactContent(artifactID);
49340
- (0, import_node_fs3.rmSync)(committedPath, { recursive: true, force: true });
49341
- (0, import_node_fs3.cpSync)(pendingPath, committedPath, { recursive: true });
49342
- (0, import_node_fs3.rmSync)(pendingPath, { recursive: true, force: true });
49522
+ (0, import_node_fs5.rmSync)(committedPath, { recursive: true, force: true });
49523
+ (0, import_node_fs5.cpSync)(pendingPath, committedPath, { recursive: true });
49524
+ (0, import_node_fs5.rmSync)(pendingPath, { recursive: true, force: true });
49343
49525
  artifact.status = "committed";
49344
49526
  this.writeArtifact(artifact);
49345
49527
  this._artifacts.set(artifactID, artifact);
@@ -49359,7 +49541,7 @@ var ServerStore = class extends EventTarget {
49359
49541
  if (artifact.status !== "pending") {
49360
49542
  throw new InvalidRequestError(`Artifact ${artifactID} has no pending work to abandon`);
49361
49543
  }
49362
- (0, import_node_fs3.rmSync)(getArtifactPendingBundlePath(this.storagePath, artifactID), { recursive: true, force: true });
49544
+ (0, import_node_fs5.rmSync)(getArtifactPendingBundlePath(this.storagePath, artifactID), { recursive: true, force: true });
49363
49545
  if (!this.hasCommittedBundle(artifactID)) {
49364
49546
  this.cascadeArtifactRemoval(artifactID);
49365
49547
  return;
@@ -49480,8 +49662,7 @@ var ServerStore = class extends EventTarget {
49480
49662
  const screen = {
49481
49663
  id: input.id ?? ulid(),
49482
49664
  name: input.name,
49483
- layout: [],
49484
- pinned: false
49665
+ layout: []
49485
49666
  };
49486
49667
  this.screens.set(screen.id, screen);
49487
49668
  this.persistScreen(screen);
@@ -49496,40 +49677,74 @@ var ServerStore = class extends EventTarget {
49496
49677
  if (Array.isArray(input.fields.layout)) {
49497
49678
  screen.layout = input.fields.layout.map((node) => structuredClone(node));
49498
49679
  }
49499
- if (typeof input.fields.pinned === "boolean") {
49500
- screen.pinned = input.fields.pinned;
49501
- }
49502
49680
  this.persistScreen(screen);
49503
49681
  this.dispatchEvent(new ScreenUpdatedEvent("screen-updated", { screen }));
49504
49682
  return screen;
49505
49683
  }
49506
- /**
49507
- * The screen that Television's GUI is currently focused on, or `null` if
49508
- * no client has set one. Drift-tolerant: if the persisted ID points at a
49509
- * screen that no longer exists, returns `null` rather than the stale ID.
49510
- */
49684
+ /** The screen that Television's GUI is currently focused on. */
49511
49685
  getActiveScreenID() {
49512
- if (this.activeScreenID && !this.screens.has(this.activeScreenID)) {
49513
- return null;
49514
- }
49515
- return this.activeScreenID;
49686
+ return this.resolveActiveScreenID(this.activeScreenID);
49687
+ }
49688
+ getActiveThemeName() {
49689
+ return this.activeThemeName;
49516
49690
  }
49517
- setActiveScreen(screenID) {
49518
- this.requireScreen(screenID);
49519
- if (this.activeScreenID === screenID) {
49691
+ getDisplayState() {
49692
+ return {
49693
+ activeScreenID: this.getActiveScreenID(),
49694
+ activeThemeName: this.activeThemeName
49695
+ };
49696
+ }
49697
+ patchDisplay(input) {
49698
+ const currentActiveScreenID = this.getActiveScreenID();
49699
+ let nextActiveScreenID = this.activeScreenID;
49700
+ let nextActiveThemeName = this.activeThemeName;
49701
+ let activeScreenIDChanged = false;
49702
+ let themeChanged = false;
49703
+ if (input.activeScreenID !== void 0) {
49704
+ nextActiveScreenID = input.activeScreenID === null ? null : this.requireScreen(input.activeScreenID).id;
49705
+ activeScreenIDChanged = nextActiveScreenID !== this.activeScreenID;
49706
+ }
49707
+ if (input.activeThemeName !== void 0) {
49708
+ if (input.activeThemeName !== null) {
49709
+ if (!isValidThemeName(input.activeThemeName)) {
49710
+ throw new InvalidRequestError(`Invalid theme name: ${input.activeThemeName}`);
49711
+ }
49712
+ if (!themeExists(this.storagePath, input.activeThemeName)) {
49713
+ throw new NotFoundError(`Theme not found: ${input.activeThemeName}`, {
49714
+ entityType: "theme",
49715
+ entityID: input.activeThemeName
49716
+ });
49717
+ }
49718
+ }
49719
+ nextActiveThemeName = input.activeThemeName;
49720
+ themeChanged = nextActiveThemeName !== this.activeThemeName;
49721
+ }
49722
+ if (!activeScreenIDChanged && !themeChanged) {
49520
49723
  return;
49521
49724
  }
49522
- this.activeScreenID = screenID;
49523
- this.persistViewerState();
49524
- this.dispatchEvent(
49525
- new ViewerActiveScreenChangedEvent("viewer-active-screen-changed", { screenID })
49526
- );
49725
+ this.activeScreenID = nextActiveScreenID;
49726
+ this.activeThemeName = nextActiveThemeName;
49727
+ const nextActiveResolved = this.getActiveScreenID();
49728
+ this.persistDisplayState();
49729
+ if (themeChanged) {
49730
+ this.startWatchingActiveTheme();
49731
+ }
49732
+ if (currentActiveScreenID !== nextActiveResolved) {
49733
+ this.dispatchEvent(
49734
+ new ScreenChangedEvent("screen-changed", { screenID: nextActiveResolved })
49735
+ );
49736
+ }
49737
+ if (themeChanged) {
49738
+ this.dispatchEvent(
49739
+ new ThemeChangedEvent("theme-changed", { themeName: this.activeThemeName })
49740
+ );
49741
+ }
49527
49742
  }
49528
49743
  /**
49529
49744
  * Resolve a target screen for `artifactID` (preferring an explicit
49530
49745
  * `screenID`, then the current active screen, then any screen referencing
49531
- * the artifact), flip the active screen if necessary, then broadcast a
49532
- * `viewer-focus` event for clients to scroll-and-highlight.
49746
+ * the artifact), flip the active screen if necessary, then broadcast an
49747
+ * `artifact-focus` event for clients to scroll-and-highlight.
49533
49748
  *
49534
49749
  * Throws `NotFoundError` for missing artifact or unknown explicit screen,
49535
49750
  * `InvalidRequestError` if the artifact is not attached to an explicitly
@@ -49554,8 +49769,8 @@ var ServerStore = class extends EventTarget {
49554
49769
  }
49555
49770
  target = screen.id;
49556
49771
  } else {
49557
- const active = this.getActiveScreenID();
49558
- const activeScreen = active ? this.screens.get(active) : void 0;
49772
+ const activeScreenID = this.getActiveScreenID();
49773
+ const activeScreen = activeScreenID ? this.requireScreen(activeScreenID) : null;
49559
49774
  if (activeScreen && layoutContainsArtifactID(activeScreen.layout, input.artifactID)) {
49560
49775
  target = activeScreen.id;
49561
49776
  } else {
@@ -49569,15 +49784,16 @@ var ServerStore = class extends EventTarget {
49569
49784
  }
49570
49785
  }
49571
49786
  if (this.activeScreenID !== target) {
49572
- this.setActiveScreen(target);
49787
+ this.patchDisplay({ activeScreenID: target });
49573
49788
  }
49574
49789
  this.dispatchEvent(
49575
- new ViewerFocusEvent("viewer-focus", { screenID: target, artifactID: input.artifactID })
49790
+ new ArtifactFocusEvent("artifact-focus", { screenID: target, artifactID: input.artifactID })
49576
49791
  );
49577
49792
  return { screenID: target, artifactID: input.artifactID };
49578
49793
  }
49579
49794
  removeScreen(screenID) {
49580
49795
  const screen = this.requireScreen(screenID);
49796
+ const previousActiveScreenID = this.getActiveScreenID();
49581
49797
  const snapshot = { ...screen, layout: structuredClone(screen.layout) };
49582
49798
  const artifactIDs = [...new Set(getScreenArtifactIDs(screen))];
49583
49799
  const artifactResults = artifactIDs.map(
@@ -49585,22 +49801,29 @@ var ServerStore = class extends EventTarget {
49585
49801
  );
49586
49802
  const metadataPath = this.trashScreen(snapshot);
49587
49803
  this.dispatchEvent(new ScreenRemovedEvent("screen-removed", { screenID }));
49804
+ const nextActiveScreenID = this.getActiveScreenID();
49805
+ if (previousActiveScreenID !== nextActiveScreenID) {
49806
+ this.dispatchEvent(
49807
+ new ScreenChangedEvent("screen-changed", { screenID: nextActiveScreenID })
49808
+ );
49809
+ }
49588
49810
  return { screenID, metadataPath, artifactResults };
49589
49811
  }
49590
49812
  dispose() {
49591
49813
  this.stopAllContentWatchers();
49814
+ this.stopWatchingActiveTheme();
49592
49815
  }
49593
49816
  [Symbol.dispose]() {
49594
49817
  this.dispose();
49595
49818
  }
49596
49819
  load() {
49597
- if ((0, import_node_fs3.existsSync)(this.screensDir)) {
49598
- for (const file2 of (0, import_node_fs3.readdirSync)(this.screensDir)) {
49820
+ if ((0, import_node_fs5.existsSync)(this.screensDir)) {
49821
+ for (const file2 of (0, import_node_fs5.readdirSync)(this.screensDir)) {
49599
49822
  if (!file2.endsWith(JSON_FILE_SUFFIX)) continue;
49600
- const filePath = import_node_path6.default.join(this.screensDir, file2);
49823
+ const filePath = import_node_path9.default.join(this.screensDir, file2);
49601
49824
  let raw;
49602
49825
  try {
49603
- raw = JSON.parse((0, import_node_fs3.readFileSync)(filePath, "utf8"));
49826
+ raw = JSON.parse((0, import_node_fs5.readFileSync)(filePath, "utf8"));
49604
49827
  } catch (error48) {
49605
49828
  console.warn(`Skipping malformed JSON file: ${filePath}`, error48);
49606
49829
  continue;
@@ -49612,20 +49835,19 @@ var ServerStore = class extends EventTarget {
49612
49835
  this.screens.set(raw.id, {
49613
49836
  id: raw.id,
49614
49837
  name: raw.name,
49615
- layout: raw.layout,
49616
- pinned: typeof raw.pinned === "boolean" ? raw.pinned : false
49838
+ layout: raw.layout
49617
49839
  });
49618
49840
  }
49619
49841
  }
49620
- if (!(0, import_node_fs3.existsSync)(this.artifactsDir)) {
49842
+ if (!(0, import_node_fs5.existsSync)(this.artifactsDir)) {
49621
49843
  return;
49622
49844
  }
49623
- for (const file2 of (0, import_node_fs3.readdirSync)(this.artifactsDir)) {
49845
+ for (const file2 of (0, import_node_fs5.readdirSync)(this.artifactsDir)) {
49624
49846
  if (!file2.endsWith(JSON_FILE_SUFFIX)) {
49625
49847
  continue;
49626
49848
  }
49627
49849
  const id = file2.slice(0, -JSON_FILE_SUFFIX.length);
49628
- const metadataPath = import_node_path6.default.join(this.artifactsDir, file2);
49850
+ const metadataPath = import_node_path9.default.join(this.artifactsDir, file2);
49629
49851
  const metadata = this.readArtifactFile(metadataPath, id);
49630
49852
  this._artifacts.set(id, metadata);
49631
49853
  }
@@ -49647,6 +49869,7 @@ var ServerStore = class extends EventTarget {
49647
49869
  return;
49648
49870
  }
49649
49871
  try {
49872
+ (0, import_node_fs5.accessSync)(contentPath, import_node_fs5.constants.R_OK);
49650
49873
  const watcher = this.watchContentFile(contentPath, () => {
49651
49874
  this.scheduleArtifactContentChangedEvent(artifactID);
49652
49875
  });
@@ -49682,6 +49905,59 @@ var ServerStore = class extends EventTarget {
49682
49905
  this.stopWatchingArtifactContent(artifactID);
49683
49906
  }
49684
49907
  }
49908
+ startWatchingActiveTheme() {
49909
+ this.stopWatchingActiveTheme();
49910
+ if (this.activeThemeName === null || !themeExists(this.storagePath, this.activeThemeName)) {
49911
+ return;
49912
+ }
49913
+ const themeName = this.activeThemeName;
49914
+ const themePath = getThemeEntryPath(this.storagePath, themeName);
49915
+ try {
49916
+ const watcher = this.watchContentFile(themePath, () => {
49917
+ this.scheduleThemeChangedEvent(themeName);
49918
+ });
49919
+ watcher.on("error", (error48) => {
49920
+ console.warn(
49921
+ `Watcher error for active theme ${themeName}: ${themePath}`,
49922
+ error48
49923
+ );
49924
+ if (this.themeWatcher === watcher) {
49925
+ this.stopWatchingActiveTheme();
49926
+ }
49927
+ });
49928
+ this.themeWatcher = watcher;
49929
+ this.themeWatcherFor = themeName;
49930
+ } catch (error48) {
49931
+ console.warn(
49932
+ `Failed to watch active theme path for ${themeName}: ${themePath}`,
49933
+ error48
49934
+ );
49935
+ }
49936
+ }
49937
+ stopWatchingActiveTheme() {
49938
+ this.themeWatcher?.close();
49939
+ this.themeWatcher = null;
49940
+ this.themeWatcherFor = null;
49941
+ if (this.themeWatchDebounceTimer) {
49942
+ clearTimeout(this.themeWatchDebounceTimer);
49943
+ this.themeWatchDebounceTimer = null;
49944
+ }
49945
+ }
49946
+ scheduleThemeChangedEvent(themeName) {
49947
+ if (this.themeWatchDebounceTimer) {
49948
+ clearTimeout(this.themeWatchDebounceTimer);
49949
+ }
49950
+ this.themeWatchDebounceTimer = setTimeout(() => {
49951
+ this.themeWatchDebounceTimer = null;
49952
+ if (this.themeWatcher === null) {
49953
+ return;
49954
+ }
49955
+ if (this.themeWatcherFor !== themeName || this.activeThemeName !== themeName) {
49956
+ return;
49957
+ }
49958
+ this.dispatchEvent(new ThemeChangedEvent("theme-changed", { themeName }));
49959
+ }, CONTENT_WATCH_DEBOUNCE_MS);
49960
+ }
49685
49961
  scheduleArtifactContentChangedEvent(artifactID) {
49686
49962
  const existingTimer = this.contentWatchDebounceTimers.get(artifactID);
49687
49963
  if (existingTimer) {
@@ -49712,14 +49988,20 @@ var ServerStore = class extends EventTarget {
49712
49988
  * metadata field.
49713
49989
  */
49714
49990
  hasCommittedBundle(artifactID) {
49715
- return (0, import_node_fs3.existsSync)(getArtifactCommittedBundlePath(this.storagePath, artifactID));
49991
+ return (0, import_node_fs5.existsSync)(getArtifactCommittedBundlePath(this.storagePath, artifactID));
49716
49992
  }
49717
49993
  createDefaultScreen() {
49718
49994
  this.createScreen({ name: "Default" });
49719
49995
  }
49996
+ initializeDisplayState() {
49997
+ this.activeScreenID = this.pickPreferredScreenID();
49998
+ this.activeThemeName = null;
49999
+ this.persistDisplayState();
50000
+ }
49720
50001
  ensureDirectories() {
49721
- (0, import_node_fs3.mkdirSync)(this.screensDir, { recursive: true });
49722
- (0, import_node_fs3.mkdirSync)(this.artifactsDir, { recursive: true });
50002
+ (0, import_node_fs5.mkdirSync)(this.screensDir, { recursive: true });
50003
+ (0, import_node_fs5.mkdirSync)(this.artifactsDir, { recursive: true });
50004
+ (0, import_node_fs5.mkdirSync)(getThemesDir(this.storagePath), { recursive: true });
49723
50005
  }
49724
50006
  /**
49725
50007
  * Cascade the last-reference removal of `artifactID`: move the bundle to
@@ -49736,12 +50018,12 @@ var ServerStore = class extends EventTarget {
49736
50018
  }
49737
50019
  const affectedScreenIDs = this.collectReferencingScreenIDs(artifactID);
49738
50020
  if (!artifact.externalFilePath && !artifact.externalURL) {
49739
- (0, import_node_fs3.rmSync)(getArtifactPendingBundlePath(this.storagePath, artifactID), { recursive: true, force: true });
50021
+ (0, import_node_fs5.rmSync)(getArtifactPendingBundlePath(this.storagePath, artifactID), { recursive: true, force: true });
49740
50022
  }
49741
50023
  this.stopWatchingArtifactContent(artifactID);
49742
50024
  if (!artifact.externalFilePath && !artifact.externalURL && artifact.status === "pending" && !this.hasCommittedBundle(artifactID)) {
49743
50025
  this._artifacts.delete(artifactID);
49744
- (0, import_node_fs3.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifactID), { force: true });
50026
+ (0, import_node_fs5.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifactID), { force: true });
49745
50027
  this.stripArtifactFromScreens(artifactID, affectedScreenIDs);
49746
50028
  this.emitArtifactRemovedEvents(artifactID, affectedScreenIDs);
49747
50029
  return {
@@ -49754,7 +50036,7 @@ var ServerStore = class extends EventTarget {
49754
50036
  status: "committed"
49755
50037
  });
49756
50038
  this._artifacts.delete(artifactID);
49757
- (0, import_node_fs3.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifactID), { force: true });
50039
+ (0, import_node_fs5.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifactID), { force: true });
49758
50040
  this.stripArtifactFromScreens(artifactID, affectedScreenIDs);
49759
50041
  this.emitArtifactRemovedEvents(artifactID, affectedScreenIDs);
49760
50042
  return result;
@@ -49779,8 +50061,8 @@ var ServerStore = class extends EventTarget {
49779
50061
  }
49780
50062
  trashArtifact(artifact) {
49781
50063
  const metadataPath = getArtifactTrashMetadataPath(this.storagePath, artifact.id);
49782
- (0, import_node_fs3.mkdirSync)(import_node_path6.default.dirname(metadataPath), { recursive: true });
49783
- (0, import_node_fs3.writeFileSync)(metadataPath, JSON.stringify(artifact, null, JSON_INDENT_SPACES));
50064
+ (0, import_node_fs5.mkdirSync)(import_node_path9.default.dirname(metadataPath), { recursive: true });
50065
+ (0, import_node_fs5.writeFileSync)(metadataPath, JSON.stringify(artifact, null, JSON_INDENT_SPACES));
49784
50066
  if (artifact.externalURL) {
49785
50067
  return {
49786
50068
  outcome: "trashed",
@@ -49799,9 +50081,9 @@ var ServerStore = class extends EventTarget {
49799
50081
  }
49800
50082
  const committedBundlePath = getArtifactCommittedBundlePath(this.storagePath, artifact.id);
49801
50083
  const bundlePath = getArtifactTrashBundlePath(this.storagePath, artifact.id);
49802
- (0, import_node_fs3.rmSync)(bundlePath, { recursive: true, force: true });
49803
- if ((0, import_node_fs3.existsSync)(committedBundlePath)) {
49804
- (0, import_node_fs3.renameSync)(committedBundlePath, bundlePath);
50084
+ (0, import_node_fs5.rmSync)(bundlePath, { recursive: true, force: true });
50085
+ if ((0, import_node_fs5.existsSync)(committedBundlePath)) {
50086
+ (0, import_node_fs5.renameSync)(committedBundlePath, bundlePath);
49805
50087
  }
49806
50088
  return {
49807
50089
  outcome: "trashed",
@@ -49812,9 +50094,9 @@ var ServerStore = class extends EventTarget {
49812
50094
  }
49813
50095
  trashScreen(screen) {
49814
50096
  const metadataPath = getTrashedScreenMetadataPath(this.storagePath, screen.id);
49815
- (0, import_node_fs3.mkdirSync)(import_node_path6.default.dirname(metadataPath), { recursive: true });
49816
- (0, import_node_fs3.writeFileSync)(metadataPath, JSON.stringify(screen, null, JSON_INDENT_SPACES));
49817
- (0, import_node_fs3.rmSync)(import_node_path6.default.join(this.screensDir, `${screen.id}.json`), { force: true });
50097
+ (0, import_node_fs5.mkdirSync)(import_node_path9.default.dirname(metadataPath), { recursive: true });
50098
+ (0, import_node_fs5.writeFileSync)(metadataPath, JSON.stringify(screen, null, JSON_INDENT_SPACES));
50099
+ (0, import_node_fs5.rmSync)(import_node_path9.default.join(this.screensDir, `${screen.id}.json`), { force: true });
49818
50100
  this.screens.delete(screen.id);
49819
50101
  return metadataPath;
49820
50102
  }
@@ -49832,25 +50114,25 @@ var ServerStore = class extends EventTarget {
49832
50114
  }
49833
50115
  let entries;
49834
50116
  try {
49835
- entries = (0, import_node_fs3.readdirSync)(this.bundledViewsPath);
50117
+ entries = (0, import_node_fs5.readdirSync)(this.bundledViewsPath);
49836
50118
  } catch (error48) {
49837
50119
  console.warn(`Failed to read bundled views directory: ${this.bundledViewsPath}`, error48);
49838
50120
  return [];
49839
50121
  }
49840
50122
  const registry2 = [];
49841
50123
  for (const id of entries) {
49842
- const viewDir = import_node_path6.default.join(this.bundledViewsPath, id);
50124
+ const viewDir = import_node_path9.default.join(this.bundledViewsPath, id);
49843
50125
  try {
49844
- if (!(0, import_node_fs3.statSync)(viewDir).isDirectory()) {
50126
+ if (!(0, import_node_fs5.statSync)(viewDir).isDirectory()) {
49845
50127
  continue;
49846
50128
  }
49847
50129
  } catch {
49848
50130
  continue;
49849
50131
  }
49850
- const manifestPath = import_node_path6.default.join(viewDir, "manifest.json");
50132
+ const manifestPath = import_node_path9.default.join(viewDir, "manifest.json");
49851
50133
  let raw;
49852
50134
  try {
49853
- raw = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
50135
+ raw = (0, import_node_fs5.readFileSync)(manifestPath, "utf8");
49854
50136
  } catch {
49855
50137
  console.warn(`Skipping view "${id}": cannot read manifest at ${manifestPath}`);
49856
50138
  continue;
@@ -49872,41 +50154,57 @@ var ServerStore = class extends EventTarget {
49872
50154
  registry2.sort((a, b) => a.id.localeCompare(b.id));
49873
50155
  return registry2;
49874
50156
  }
49875
- loadViewerState() {
49876
- if (!(0, import_node_fs3.existsSync)(this.viewerStatePath)) {
49877
- return;
50157
+ loadDisplayState() {
50158
+ if (!(0, import_node_fs5.existsSync)(this.displayStatePath)) {
50159
+ return false;
49878
50160
  }
49879
50161
  let raw;
49880
50162
  try {
49881
- raw = (0, import_node_fs3.readFileSync)(this.viewerStatePath, "utf8");
50163
+ raw = (0, import_node_fs5.readFileSync)(this.displayStatePath, "utf8");
49882
50164
  } catch {
49883
- return;
50165
+ return false;
49884
50166
  }
49885
50167
  let parsed;
49886
50168
  try {
49887
50169
  parsed = JSON.parse(raw);
49888
50170
  } catch {
49889
- return;
50171
+ return false;
49890
50172
  }
49891
- if (parsed && typeof parsed === "object" && "activeScreenID" in parsed && (typeof parsed.activeScreenID === "string" || parsed.activeScreenID === null)) {
50173
+ if (parsed && typeof parsed === "object" && "activeScreenID" in parsed && (typeof parsed.activeScreenID === "string" || parsed.activeScreenID === null) && "activeThemeName" in parsed && (typeof parsed.activeThemeName === "string" || parsed.activeThemeName === null)) {
49892
50174
  this.activeScreenID = parsed.activeScreenID;
50175
+ this.activeThemeName = parsed.activeThemeName;
50176
+ return true;
49893
50177
  }
50178
+ return false;
49894
50179
  }
49895
- persistViewerState() {
49896
- (0, import_node_fs3.writeFileSync)(
49897
- this.viewerStatePath,
49898
- JSON.stringify({ activeScreenID: this.activeScreenID }, null, JSON_INDENT_SPACES)
50180
+ persistDisplayState() {
50181
+ (0, import_node_fs5.writeFileSync)(
50182
+ this.displayStatePath,
50183
+ JSON.stringify(this.getDisplayState(), null, JSON_INDENT_SPACES)
49899
50184
  );
49900
50185
  }
49901
- get viewerStatePath() {
49902
- return import_node_path6.default.join(this.storagePath, "viewer.json");
50186
+ get displayStatePath() {
50187
+ return import_node_path9.default.join(this.storagePath, "display.json");
50188
+ }
50189
+ resolveActiveScreenID(screenID) {
50190
+ if (screenID === null) {
50191
+ return null;
50192
+ }
50193
+ return this.screens.has(screenID) ? screenID : null;
50194
+ }
50195
+ pickPreferredScreenID() {
50196
+ const [firstScreen] = [...this.screens.values()].sort((a, b) => a.id.localeCompare(b.id));
50197
+ if (!firstScreen) {
50198
+ throw new Error("Cannot initialize display state without a screen");
50199
+ }
50200
+ return firstScreen.id;
49903
50201
  }
49904
50202
  loadOrCreateAuthToken() {
49905
- if ((0, import_node_fs3.existsSync)(this.tokenPath)) {
49906
- return (0, import_node_fs3.readFileSync)(this.tokenPath, "utf8").trim();
50203
+ if ((0, import_node_fs5.existsSync)(this.tokenPath)) {
50204
+ return (0, import_node_fs5.readFileSync)(this.tokenPath, "utf8").trim();
49907
50205
  }
49908
50206
  const token = createToken();
49909
- (0, import_node_fs3.writeFileSync)(this.tokenPath, `${token}
50207
+ (0, import_node_fs5.writeFileSync)(this.tokenPath, `${token}
49910
50208
  `);
49911
50209
  return token;
49912
50210
  }
@@ -49914,12 +50212,12 @@ var ServerStore = class extends EventTarget {
49914
50212
  if (!this.screens.has(screen.id)) {
49915
50213
  throw new Error(`Cannot persist screen ${screen.id}: not in live map`);
49916
50214
  }
49917
- (0, import_node_fs3.writeFileSync)(import_node_path6.default.join(this.screensDir, `${screen.id}.json`), JSON.stringify(screen, null, JSON_INDENT_SPACES));
50215
+ (0, import_node_fs5.writeFileSync)(import_node_path9.default.join(this.screensDir, `${screen.id}.json`), JSON.stringify(screen, null, JSON_INDENT_SPACES));
49918
50216
  }
49919
50217
  readArtifactFile(filePath, expectedID) {
49920
50218
  let raw;
49921
50219
  try {
49922
- raw = (0, import_node_fs3.readFileSync)(filePath, "utf8");
50220
+ raw = (0, import_node_fs5.readFileSync)(filePath, "utf8");
49923
50221
  } catch (error48) {
49924
50222
  throw this.invalidMetadataFile(filePath, "unreadable", error48);
49925
50223
  }
@@ -49941,7 +50239,7 @@ var ServerStore = class extends EventTarget {
49941
50239
  `id ${result.data.id} does not match filename ${expectedID}`
49942
50240
  );
49943
50241
  }
49944
- if (result.data.externalFilePath !== void 0 && !import_node_path6.default.isAbsolute(result.data.externalFilePath)) {
50242
+ if (result.data.externalFilePath !== void 0 && !import_node_path9.default.isAbsolute(result.data.externalFilePath)) {
49945
50243
  throw this.invalidMetadataFile(filePath, "externalFilePath must be absolute");
49946
50244
  }
49947
50245
  return result.data;
@@ -49958,16 +50256,16 @@ Review docs/storage.md for the current schema, and either update the file to mat
49958
50256
  return cause !== void 0 ? new Error(message, { cause }) : new Error(message);
49959
50257
  }
49960
50258
  writeArtifact(artifact) {
49961
- (0, import_node_fs3.writeFileSync)(getArtifactLiveMetadataPath(this.storagePath, artifact.id), JSON.stringify(artifact, null, JSON_INDENT_SPACES));
50259
+ (0, import_node_fs5.writeFileSync)(getArtifactLiveMetadataPath(this.storagePath, artifact.id), JSON.stringify(artifact, null, JSON_INDENT_SPACES));
49962
50260
  }
49963
50261
  scaffoldPendingBundle(artifactID, type) {
49964
50262
  const pendingPath = getArtifactPendingBundlePath(this.storagePath, artifactID);
49965
50263
  const publicDir = getArtifactPublicDirPath(this.storagePath, artifactID, "pending");
49966
- (0, import_node_fs3.mkdirSync)(publicDir, { recursive: true });
49967
- (0, import_node_fs3.writeFileSync)(import_node_path6.default.join(pendingPath, "artifact.md"), "");
49968
- (0, import_node_fs3.writeFileSync)(import_node_path6.default.join(pendingPath, "memory.md"), "");
49969
- (0, import_node_fs3.writeFileSync)(import_node_path6.default.join(pendingPath, "data.json"), "{}");
49970
- (0, import_node_fs3.writeFileSync)(getArtifactEntryFilePath(this.storagePath, artifactID, type, "pending"), "");
50264
+ (0, import_node_fs5.mkdirSync)(publicDir, { recursive: true });
50265
+ (0, import_node_fs5.writeFileSync)(import_node_path9.default.join(pendingPath, "artifact.md"), "");
50266
+ (0, import_node_fs5.writeFileSync)(import_node_path9.default.join(pendingPath, "memory.md"), "");
50267
+ (0, import_node_fs5.writeFileSync)(import_node_path9.default.join(pendingPath, "data.json"), "{}");
50268
+ (0, import_node_fs5.writeFileSync)(getArtifactEntryFilePath(this.storagePath, artifactID, type, "pending"), "");
49971
50269
  }
49972
50270
  validatePendingArtifact(artifact) {
49973
50271
  const issues = [];
@@ -49979,9 +50277,9 @@ Review docs/storage.md for the current schema, and either update the file to mat
49979
50277
  issues.push(`artifacts/${artifactID}.json must have status "pending" to commit pending content`);
49980
50278
  }
49981
50279
  const pendingPath = getArtifactPendingBundlePath(this.storagePath, artifactID);
49982
- this.validateArtifactDoc(import_node_path6.default.join(pendingPath, "artifact.md"), issues);
49983
- this.validateDataJson(import_node_path6.default.join(pendingPath, "data.json"), issues);
49984
- this.validateMemory(import_node_path6.default.join(pendingPath, "memory.md"), issues);
50280
+ this.validateArtifactDoc(import_node_path9.default.join(pendingPath, "artifact.md"), issues);
50281
+ this.validateDataJson(import_node_path9.default.join(pendingPath, "data.json"), issues);
50282
+ this.validateMemory(import_node_path9.default.join(pendingPath, "memory.md"), issues);
49985
50283
  this.validateEntryFile(getArtifactEntryFilePath(this.storagePath, artifactID, artifact.type, "pending"), issues);
49986
50284
  return issues;
49987
50285
  }
@@ -50031,14 +50329,14 @@ Review docs/storage.md for the current schema, and either update the file to mat
50031
50329
  }
50032
50330
  readTextFileForValidation(filePath, issues) {
50033
50331
  try {
50034
- return (0, import_node_fs3.readFileSync)(filePath, "utf8");
50332
+ return (0, import_node_fs5.readFileSync)(filePath, "utf8");
50035
50333
  } catch {
50036
50334
  issues.push(`${filePath} must exist`);
50037
50335
  return null;
50038
50336
  }
50039
50337
  }
50040
50338
  get screensDir() {
50041
- return import_node_path6.default.join(this.storagePath, "screens");
50339
+ return import_node_path9.default.join(this.storagePath, "screens");
50042
50340
  }
50043
50341
  normalizeExternalArtifactURL(rawURL) {
50044
50342
  if (!isExternalArtifactURL(rawURL)) {
@@ -50049,28 +50347,28 @@ Review docs/storage.md for the current schema, and either update the file to mat
50049
50347
  return rawURL;
50050
50348
  }
50051
50349
  normalizeExternalArtifactPath(sourcePath, type) {
50052
- if (!import_node_path6.default.isAbsolute(sourcePath)) {
50350
+ if (!import_node_path9.default.isAbsolute(sourcePath)) {
50053
50351
  throw new InvalidRequestError("externalFilePath must be absolute");
50054
50352
  }
50055
- if (import_node_path6.default.normalize(sourcePath) !== sourcePath) {
50353
+ if (import_node_path9.default.normalize(sourcePath) !== sourcePath) {
50056
50354
  throw new InvalidRequestError("externalFilePath must be normalized and must not contain traversal segments");
50057
50355
  }
50058
50356
  try {
50059
- (0, import_node_fs3.accessSync)(sourcePath, import_node_fs3.constants.R_OK);
50357
+ (0, import_node_fs5.accessSync)(sourcePath, import_node_fs5.constants.R_OK);
50060
50358
  } catch {
50061
50359
  throw new InvalidRequestError(`externalFilePath does not exist or is not readable: ${sourcePath}`);
50062
50360
  }
50063
50361
  const expectedExtension = type === "text/html" ? ".html" : ".md";
50064
- if (import_node_path6.default.extname(sourcePath).toLowerCase() !== expectedExtension) {
50362
+ if (import_node_path9.default.extname(sourcePath).toLowerCase() !== expectedExtension) {
50065
50363
  throw new InvalidRequestError(`externalFilePath extension does not match artifact type ${type}: ${sourcePath}`);
50066
50364
  }
50067
50365
  return sourcePath;
50068
50366
  }
50069
50367
  get artifactsDir() {
50070
- return import_node_path6.default.join(this.storagePath, "artifacts");
50368
+ return import_node_path9.default.join(this.storagePath, "artifacts");
50071
50369
  }
50072
50370
  get tokenPath() {
50073
- return import_node_path6.default.join(this.storagePath, "token");
50371
+ return import_node_path9.default.join(this.storagePath, "token");
50074
50372
  }
50075
50373
  };
50076
50374
  function findCardIDForArtifact(layout, artifactID) {
@@ -50122,24 +50420,24 @@ function createDirectiveError(message) {
50122
50420
  return new CLIDirectiveError(ensureHelpPointer(message));
50123
50421
  }
50124
50422
  function getDevPackageDir() {
50125
- return import_node_path7.default.resolve(import_node_path7.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)), "..");
50423
+ return import_node_path10.default.resolve(import_node_path10.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)), "..");
50126
50424
  }
50127
50425
  function resolveVercelSkillsInstallerBin() {
50128
50426
  if (typeof require === "function" && typeof require.resolve === "function") {
50129
50427
  return require.resolve("skills/bin/cli.mjs");
50130
50428
  }
50131
- const localRequire = (0, import_node_module.createRequire)(import_node_path7.default.join(getDevPackageDir(), "package.json"));
50429
+ const localRequire = (0, import_node_module.createRequire)(import_node_path10.default.join(getDevPackageDir(), "package.json"));
50132
50430
  return localRequire.resolve("skills/bin/cli.mjs");
50133
50431
  }
50134
50432
  function readCLIVersion() {
50135
- if ("0.1.96".length > 0) {
50136
- return "0.1.96";
50433
+ if ("0.1.97".length > 0) {
50434
+ return "0.1.97";
50137
50435
  }
50138
- const devPackageJsonPath = import_node_path7.default.join(getDevPackageDir(), "package.json");
50139
- if (!(0, import_node_fs4.existsSync)(devPackageJsonPath)) {
50436
+ const devPackageJsonPath = import_node_path10.default.join(getDevPackageDir(), "package.json");
50437
+ if (!(0, import_node_fs6.existsSync)(devPackageJsonPath)) {
50140
50438
  throw new Error("Could not resolve package.json for repo-local CLI version.");
50141
50439
  }
50142
- return JSON.parse((0, import_node_fs4.readFileSync)(devPackageJsonPath, "utf8")).version;
50440
+ return JSON.parse((0, import_node_fs6.readFileSync)(devPackageJsonPath, "utf8")).version;
50143
50441
  }
50144
50442
  var FRONTMATTER_DELIMITER = "---\n";
50145
50443
  var FRONTMATTER_DELIMITER_LENGTH = FRONTMATTER_DELIMITER.length;
@@ -50170,11 +50468,11 @@ function splitFrontmatter(text) {
50170
50468
  return { frontmatter, body };
50171
50469
  }
50172
50470
  function readBundledSkill(skillRoot) {
50173
- const skillMarkdownPath = import_node_path7.default.join(skillRoot, "SKILL.md");
50174
- if (!(0, import_node_fs4.existsSync)(skillMarkdownPath)) {
50471
+ const skillMarkdownPath = import_node_path10.default.join(skillRoot, "SKILL.md");
50472
+ if (!(0, import_node_fs6.existsSync)(skillMarkdownPath)) {
50175
50473
  throw new Error(`Bundled skill is missing SKILL.md at ${skillMarkdownPath}.`);
50176
50474
  }
50177
- const markdown = (0, import_node_fs4.readFileSync)(skillMarkdownPath, "utf8");
50475
+ const markdown = (0, import_node_fs6.readFileSync)(skillMarkdownPath, "utf8");
50178
50476
  const { frontmatter } = splitFrontmatter(markdown);
50179
50477
  const name = frontmatter.name?.trim();
50180
50478
  const description = frontmatter.description?.trim();
@@ -50184,11 +50482,11 @@ function readBundledSkill(skillRoot) {
50184
50482
  return { name, description, markdown, root: skillRoot };
50185
50483
  }
50186
50484
  function listBundledSkills(collectionRoot) {
50187
- return (0, import_node_fs4.readdirSync)(collectionRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readBundledSkill(import_node_path7.default.join(collectionRoot, entry.name))).sort((a, b) => a.name.localeCompare(b.name));
50485
+ return (0, import_node_fs6.readdirSync)(collectionRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readBundledSkill(import_node_path10.default.join(collectionRoot, entry.name))).sort((a, b) => a.name.localeCompare(b.name));
50188
50486
  }
50189
50487
  function resolveBundledSkill(collectionRoot, skillName) {
50190
- const skillRoot = import_node_path7.default.join(collectionRoot, skillName);
50191
- if (!(0, import_node_fs4.existsSync)(skillRoot)) {
50488
+ const skillRoot = import_node_path10.default.join(collectionRoot, skillName);
50489
+ if (!(0, import_node_fs6.existsSync)(skillRoot)) {
50192
50490
  throw createDirectiveError(`tv skills show could not find bundled skill \`${skillName}\`.`);
50193
50491
  }
50194
50492
  return readBundledSkill(skillRoot);
@@ -50245,55 +50543,55 @@ function resolveStaticDir() {
50245
50543
  if (!process.argv[1]) {
50246
50544
  return void 0;
50247
50545
  }
50248
- const entryDir = import_node_path7.default.dirname((0, import_node_fs4.realpathSync)(process.argv[1]));
50249
- const resolved = import_node_path7.default.resolve(entryDir, "./web");
50250
- return (0, import_node_fs4.existsSync)(import_node_path7.default.join(resolved, "index.html")) ? resolved : void 0;
50546
+ const entryDir = import_node_path10.default.dirname((0, import_node_fs6.realpathSync)(process.argv[1]));
50547
+ const resolved = import_node_path10.default.resolve(entryDir, "./web");
50548
+ return (0, import_node_fs6.existsSync)(import_node_path10.default.join(resolved, "index.html")) ? resolved : void 0;
50251
50549
  }
50252
50550
  function resolveBundledViewsPath() {
50253
50551
  if (process.argv[1]) {
50254
- const entryDir = import_node_path7.default.dirname((0, import_node_fs4.realpathSync)(process.argv[1]));
50255
- const resolved = import_node_path7.default.resolve(entryDir, "./views");
50256
- return (0, import_node_fs4.existsSync)(resolved) ? resolved : void 0;
50552
+ const entryDir = import_node_path10.default.dirname((0, import_node_fs6.realpathSync)(process.argv[1]));
50553
+ const resolved = import_node_path10.default.resolve(entryDir, "./views");
50554
+ return (0, import_node_fs6.existsSync)(resolved) ? resolved : void 0;
50257
50555
  }
50258
- const devViewsPath = import_node_path7.default.join(getDevPackageDir(), "dist/views");
50259
- return (0, import_node_fs4.existsSync)(devViewsPath) ? devViewsPath : void 0;
50556
+ const devViewsPath = import_node_path10.default.join(getDevPackageDir(), "dist/views");
50557
+ return (0, import_node_fs6.existsSync)(devViewsPath) ? devViewsPath : void 0;
50260
50558
  }
50261
50559
  function resolveCanonicalDir() {
50262
50560
  if (process.argv[1]) {
50263
- const entryDir = import_node_path7.default.dirname((0, import_node_fs4.realpathSync)(process.argv[1]));
50264
- const resolved = import_node_path7.default.resolve(entryDir, "./canonical/v1");
50265
- return (0, import_node_fs4.existsSync)(resolved) ? resolved : void 0;
50561
+ const entryDir = import_node_path10.default.dirname((0, import_node_fs6.realpathSync)(process.argv[1]));
50562
+ const resolved = import_node_path10.default.resolve(entryDir, "./canonical/v1");
50563
+ return (0, import_node_fs6.existsSync)(resolved) ? resolved : void 0;
50266
50564
  }
50267
- const devCanonicalPath = import_node_path7.default.resolve(
50565
+ const devCanonicalPath = import_node_path10.default.resolve(
50268
50566
  getDevPackageDir(),
50269
50567
  "../server/dist/canonical/v1"
50270
50568
  );
50271
- return (0, import_node_fs4.existsSync)(devCanonicalPath) ? devCanonicalPath : void 0;
50569
+ return (0, import_node_fs6.existsSync)(devCanonicalPath) ? devCanonicalPath : void 0;
50272
50570
  }
50273
50571
  function resolveBundledSkillsRoot() {
50274
- const builtPath = typeof process.argv[1] === "string" ? import_node_path7.default.resolve(import_node_path7.default.dirname((0, import_node_fs4.realpathSync)(process.argv[1])), "./skills") : void 0;
50275
- if (builtPath && (0, import_node_fs4.existsSync)(builtPath)) {
50572
+ const builtPath = typeof process.argv[1] === "string" ? import_node_path10.default.resolve(import_node_path10.default.dirname((0, import_node_fs6.realpathSync)(process.argv[1])), "./skills") : void 0;
50573
+ if (builtPath && (0, import_node_fs6.existsSync)(builtPath)) {
50276
50574
  return builtPath;
50277
50575
  }
50278
- const devPath = import_node_path7.default.resolve(getDevPackageDir(), "../skills/dist");
50279
- return (0, import_node_fs4.existsSync)(devPath) ? devPath : void 0;
50576
+ const devPath = import_node_path10.default.resolve(getDevPackageDir(), "../skills/dist");
50577
+ return (0, import_node_fs6.existsSync)(devPath) ? devPath : void 0;
50280
50578
  }
50281
50579
  function isInTempDirectory(inputPath) {
50282
50580
  try {
50283
- const realTmp = (0, import_node_fs4.realpathSync)(import_node_os4.default.tmpdir());
50284
- const realInput = (0, import_node_fs4.existsSync)(inputPath) ? (0, import_node_fs4.realpathSync)(inputPath) : import_node_path7.default.resolve(inputPath);
50285
- const relative = import_node_path7.default.relative(realTmp, realInput);
50286
- return relative !== "" && !relative.startsWith("..") && !import_node_path7.default.isAbsolute(relative);
50581
+ const realTmp = (0, import_node_fs6.realpathSync)(import_node_os4.default.tmpdir());
50582
+ const realInput = (0, import_node_fs6.existsSync)(inputPath) ? (0, import_node_fs6.realpathSync)(inputPath) : import_node_path10.default.resolve(inputPath);
50583
+ const relative = import_node_path10.default.relative(realTmp, realInput);
50584
+ return relative !== "" && !relative.startsWith("..") && !import_node_path10.default.isAbsolute(relative);
50287
50585
  } catch {
50288
50586
  return false;
50289
50587
  }
50290
50588
  }
50291
50589
  function validateExternalArtifactPath(inputPath) {
50292
- if (!import_node_path7.default.isAbsolute(inputPath)) {
50590
+ if (!import_node_path10.default.isAbsolute(inputPath)) {
50293
50591
  throw createDirectiveError("tv create-external-artifact requires --path to be an absolute path.");
50294
50592
  }
50295
50593
  try {
50296
- (0, import_node_fs4.accessSync)(inputPath, import_node_fs4.constants.R_OK);
50594
+ (0, import_node_fs6.accessSync)(inputPath, import_node_fs6.constants.R_OK);
50297
50595
  } catch {
50298
50596
  throw createDirectiveError(`tv create-external-artifact could not read --path ${inputPath}.`);
50299
50597
  }
@@ -50381,6 +50679,7 @@ function formatScreenRemovalResult(result) {
50381
50679
  function formatCommanderError(argv, error48) {
50382
50680
  const commandName = commandNameFromArgv(argv);
50383
50681
  const optionMatch = /option '([^']+)'/.exec(error48.message);
50682
+ const argumentMatch = /missing required argument '([^']+)'/.exec(error48.message);
50384
50683
  if (error48.code === "commander.unknownCommand") {
50385
50684
  return createDirectiveError(`Unknown tv command: ${commandName}.`);
50386
50685
  }
@@ -50395,17 +50694,21 @@ function formatCommanderError(argv, error48) {
50395
50694
  const optionText = optionMatch?.[1] ?? "the required option";
50396
50695
  return createDirectiveError(`tv ${commandName} requires ${optionText}.`);
50397
50696
  }
50697
+ if (error48.code === "commander.missingArgument") {
50698
+ const argumentText = argumentMatch ? `<${argumentMatch[1]}>` : "the required argument";
50699
+ return createDirectiveError(`tv ${commandName} requires ${argumentText}.`);
50700
+ }
50398
50701
  if (error48.code === "commander.invalidArgument") {
50399
50702
  return createDirectiveError(`tv ${commandName} received an invalid argument: ${error48.message.replace(/^error:\s*/, "")}`);
50400
50703
  }
50401
50704
  return null;
50402
50705
  }
50403
50706
  function readAuthToken(storagePath) {
50404
- const tokenPath = import_node_path7.default.join(storagePath, "token");
50405
- if (!(0, import_node_fs4.existsSync)(tokenPath)) {
50707
+ const tokenPath = import_node_path10.default.join(storagePath, "token");
50708
+ if (!(0, import_node_fs6.existsSync)(tokenPath)) {
50406
50709
  return void 0;
50407
50710
  }
50408
- const token = (0, import_node_fs4.readFileSync)(tokenPath, "utf8").trim();
50711
+ const token = (0, import_node_fs6.readFileSync)(tokenPath, "utf8").trim();
50409
50712
  return token.length > 0 ? token : void 0;
50410
50713
  }
50411
50714
  function readOrCreateAuthToken(storagePath) {
@@ -50532,7 +50835,7 @@ function createProgram(env, argv = []) {
50532
50835
  screenID: opts.screen
50533
50836
  });
50534
50837
  if (shouldFocus) {
50535
- await client.viewer.focus({ artifactID: result.artifact.id, screenID: opts.screen });
50838
+ await client.display.focus({ artifactID: result.artifact.id, screenID: opts.screen });
50536
50839
  }
50537
50840
  if (!("pendingPath" in result)) {
50538
50841
  throw new Error("Server did not return pendingPath for internal artifact creation");
@@ -50589,7 +50892,7 @@ If you wish to display temporary content to the user, use an internal artifact i
50589
50892
  screenID: opts.screen
50590
50893
  });
50591
50894
  if (shouldFocus) {
50592
- await client.viewer.focus({ artifactID: artifact.id, screenID: opts.screen });
50895
+ await client.display.focus({ artifactID: artifact.id, screenID: opts.screen });
50593
50896
  }
50594
50897
  writeLine(env.stdout, `External artifact ${artifact.id} created.`);
50595
50898
  writeLine(env.stdout, `Television will display content from ${externalPath} and watch it for changes.`);
@@ -50606,7 +50909,7 @@ If you wish to display temporary content to the user, use an internal artifact i
50606
50909
  screenID: opts.screen
50607
50910
  });
50608
50911
  if (shouldFocus) {
50609
- await client.viewer.focus({ artifactID: artifact.id, screenID: opts.screen });
50912
+ await client.display.focus({ artifactID: artifact.id, screenID: opts.screen });
50610
50913
  }
50611
50914
  writeLine(env.stdout, `URL artifact ${artifact.id} created.`);
50612
50915
  writeLine(env.stdout, `Television will display ${opts.url} in an embedded frame.`);
@@ -50648,7 +50951,7 @@ If you wish to display temporary content to the user, use an internal artifact i
50648
50951
  screenID: opts.screen
50649
50952
  });
50650
50953
  if (shouldFocus) {
50651
- await client.viewer.focus({ artifactID: opts.id, screenID: opts.screen });
50954
+ await client.display.focus({ artifactID: opts.id, screenID: opts.screen });
50652
50955
  }
50653
50956
  writeLine(
50654
50957
  env.stdout,
@@ -50684,7 +50987,7 @@ If you wish to display temporary content to the user, use an internal artifact i
50684
50987
  const client = createAuthenticatedClient(opts.server);
50685
50988
  const { screen } = await client.screens.create({ name: opts.name });
50686
50989
  if (shouldFocus) {
50687
- await client.viewer.setActive({ screenID: screen.id });
50990
+ await client.display.patch({ activeScreenID: screen.id });
50688
50991
  }
50689
50992
  writeLine(env.stdout, `Screen created: ${screen.id} (${screen.name})`);
50690
50993
  });
@@ -50708,31 +51011,40 @@ If you wish to display temporary content to the user, use an internal artifact i
50708
51011
  writeJSON(env.stdout, await client.screens.get({ screenID: opts.id }));
50709
51012
  });
50710
51013
  program2.command("focus-status").description(
50711
- "Print focus state as JSON: the active (persistently focused) screen ID and the count of currently connected GUI clients. Useful for verifying that an upcoming focus event will actually be visible to anyone \u2014 if connectedClients is 0, focus-screen and focus-artifact are no-ops from the user's perspective."
51014
+ "Print display state as JSON: the active (persistently focused) screen ID and the active theme name."
50712
51015
  ).option("--server <url>", "Server URL", DEFAULT_SERVER_URL).action(async (opts) => {
50713
51016
  const client = createAuthenticatedClient(opts.server);
50714
- writeJSON(env.stdout, await client.viewer.state());
51017
+ writeJSON(env.stdout, await client.display.get());
50715
51018
  });
50716
51019
  program2.command("focus-screen").description(
50717
51020
  "Set persistent screen focus. The change is broadcast to every connected GUI client and survives reconnects. This is the right command when the user wants to switch to a different screen and stay there. For just nudging attention to a specific artifact (which may live on the current screen), prefer `tv focus-artifact`."
50718
51021
  ).requiredOption("--id <id>", "Screen ID").option("--server <url>", "Server URL", DEFAULT_SERVER_URL).action(async (opts) => {
50719
51022
  const client = createAuthenticatedClient(opts.server);
50720
- await client.viewer.setActive({ screenID: opts.id });
51023
+ await client.display.patch({ activeScreenID: opts.id });
50721
51024
  writeLine(env.stdout, `Focused screen ${opts.id}.`);
50722
51025
  });
51026
+ program2.command("set-theme").description(
51027
+ 'Activate a display theme by name. The special theme name `default` clears the active theme and falls back to Television\'s bundled default look. A user-authored theme literally named `default` cannot be activated through this CLI convenience; call `PATCH /display { activeThemeName: "default" }` directly if you need that exact theme.'
51028
+ ).argument("<theme>", "Theme name to activate, or `default` to clear the active theme").option("--server <url>", "Server URL", DEFAULT_SERVER_URL).addHelpText("after", [
51029
+ "",
51030
+ "Examples:",
51031
+ " tv set-theme paperlike Activate the `paperlike` theme.",
51032
+ " tv set-theme default Clear the active theme and use the bundled defaults.",
51033
+ "",
51034
+ "Note:",
51035
+ " `default` is a CLI sentinel that clears the active theme.",
51036
+ ' To activate a user-authored theme literally named `default`, call `PATCH /display { activeThemeName: "default" }` directly.'
51037
+ ].join("\n")).action(async (theme, opts) => {
51038
+ const client = createAuthenticatedClient(opts.server);
51039
+ await client.display.patch({ activeThemeName: theme === "default" ? null : theme });
51040
+ });
50723
51041
  program2.command("focus-artifact").description(
50724
51042
  "Send a transient focus nudge for a specific artifact. Connected clients switch to the artifact's screen if they're not already on it, scroll the artifact's card into view, and play a brief highlight animation. This is NOT persisted as state \u2014 there is no concept of a 'focused artifact' that survives reconnects (the focused screen is persistent, but artifact focus is a one-shot event). Pass --screen <id> to pin which screen the nudge targets, otherwise the server picks one (preferring the active screen when the artifact is there)."
50725
51043
  ).requiredOption("--id <id>", "Artifact ID").option("--screen <id>", "Screen ID (optional; server picks a screen the artifact is on)").option("--server <url>", "Server URL", DEFAULT_SERVER_URL).action(async (opts) => {
50726
51044
  const client = createAuthenticatedClient(opts.server);
50727
- const { connectedClients } = await client.viewer.state();
50728
- if (connectedClients === 0) {
50729
- env.stderr.write(
50730
- "Warning: no Television clients are connected; the focus event will be broadcast to nobody.\n"
50731
- );
50732
- }
50733
51045
  const focusInput = { artifactID: opts.id };
50734
51046
  if (opts.screen !== void 0) focusInput.screenID = opts.screen;
50735
- const result = await client.viewer.focus(focusInput);
51047
+ const result = await client.display.focus(focusInput);
50736
51048
  writeLine(env.stdout, `Focused artifact ${result.artifactID} on screen ${result.screenID}.`);
50737
51049
  });
50738
51050
  const skillsCommand = program2.command("skills").description("Manage the bundled Television skills").addHelpText("after", [