@telepath-computer/television 0.1.8 → 0.1.9

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.
@@ -0,0 +1 @@
1
+ artifact-view{background:var(--color-bg-muted);border:1px solid var(--color-border);border-radius:10px;box-sizing:border-box;width:100%;height:100%;display:flex;flex-direction:column;overflow:hidden}.artifact-title-bar{display:flex;align-items:center;height:34px;padding:0 12px;flex-shrink:0;border-bottom:1px solid var(--color-border);-webkit-user-select:none;user-select:none}.artifact-title{flex:1;font-size:13px;font-weight:500;color:var(--color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.artifact-close-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:6px;background:transparent;color:var(--color-text-muted);cursor:pointer;flex-shrink:0}.artifact-close-btn:hover{background:var(--color-interactive);color:var(--color-text)}.artifact-content{flex:1;min-height:0;overflow:hidden;padding:0}.artifact-html-frame{pointer-events:none}layout-view{width:100%;flex:1;min-width:0;min-height:0;display:flex;align-items:flex-start;overflow:hidden;outline:none;position:relative;--layout-unit-x: 120px;--layout-unit-y: 120px;--layout-gap: 16px;--layout-drag-transition-ms: 0ms;--color-drag-target: color-mix( in srgb, var(--color-border, var(--neutral-300, #cbd5e1)) 55%, transparent );--shadow-drag-card: 0 24px 48px color-mix(in srgb, var(--neutral-900, #0f172a) 28%, transparent)}layout-view .track{display:flex;align-items:start;gap:var(--layout-gap);flex-shrink:0;width:max-content;will-change:transform;transition:transform .2s ease}layout-view .node{display:grid;grid-template-columns:repeat(4,var(--layout-unit-x));grid-template-rows:repeat(6,var(--layout-unit-y));gap:var(--layout-gap);align-self:start;width:calc(var(--layout-unit-x) * 4 + var(--layout-gap) * 3);will-change:transform}layout-view .card-slot{border-radius:12px;box-sizing:border-box;cursor:default;min-width:0;min-height:0;position:relative;z-index:1}layout-view .card-slot artifact-view{width:100%;height:100%}layout-view .card-slot[data-layout-draggable=true]{cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}layout-view .card-placeholder{border-radius:12px;background:var(--color-drag-target);position:relative;z-index:0}layout-view .card-placeholder[data-layout-placeholder-ghost=true]{background:transparent;border:0;opacity:0;pointer-events:none}layout-view .drag-preview{position:fixed;z-index:10;pointer-events:none;filter:drop-shadow(var(--shadow-drag-card))}layout-view .drag-preview>*{width:100%;height:100%}workspace-view{width:100%;flex:1;min-width:0;min-height:0;display:flex;flex-direction:column}workspace-view [data-testid=empty-workspace]{margin:0;padding:1rem}television-app{width:100%;height:100%;display:grid;grid-template-rows:auto 1fr;grid-template-areas:"header" "main"}television-app>header{grid-area:header;position:relative;z-index:10}television-app>main{grid-area:main;grid-row:1 / -1}body.electron television-app>header{-webkit-app-region:drag;padding-left:80px;border-bottom:1px solid var(--color-border)}header{display:flex;align-items:center;justify-content:space-between;height:34px;padding:0 12px;flex-shrink:0;-webkit-user-select:none;user-select:none}television-app>header>nav{display:flex;align-items:center;gap:8px;min-width:0}workspace-picker,workspace-picker [trigger],button[variant=toolbar],.settings-server-remove-button,.settings-submit-button,.settings-cancel-button,.settings-connect-button,.settings-field input{-webkit-app-region:no-drag}workspace-picker dropdown-menu::part(panel){min-width:18rem}workspace-picker [trigger],workspace-picker [trigger][data-dropdown-trigger]{min-width:0;padding:0;border:none;background:transparent;font-size:13px;font-weight:600}workspace-picker [trigger] .trigger-label{white-space:nowrap}dropdown-group{display:block;padding:4px 0}dropdown-label{display:block;padding:0 10px 4px;font-size:12px;color:color-mix(in srgb,currentColor 60%,transparent)}dropdown-item{display:block;padding:6px 10px;border-radius:4px;cursor:pointer}dropdown-item[selected]{background:color-mix(in srgb,var(--color-border) 70%,white)}dropdown-item[action]{color:color-mix(in srgb,currentColor 50%,transparent)}dropdown-item[danger]{color:#c74343}television-app>main{min-width:0;min-height:0;overflow:hidden;display:flex;flex-direction:column}television-app>main.auth-gate{display:flex;align-items:center;justify-content:center}.settings-panel{width:min(32rem,100%);display:flex;flex-direction:column;padding:20px;border:1px solid var(--color-border);border-radius:12px;background:var(--color-bg)}.workspace-modal,.auth-modal{width:min(24rem,100%);display:flex;flex-direction:column;padding:20px;border:1px solid var(--color-border);border-radius:12px;background:var(--color-bg)}.auth-form{display:flex;flex-direction:column;gap:12px}.auth-modal-title,.auth-modal-copy{margin:0}.auth-token-input-invalid{border-color:#c74343}.workspace-form{display:flex;flex-direction:column;gap:12px}.workspace-form-actions{display:flex;gap:8px}.workspace-modal-title,.workspace-delete-copy,.workspace-delete-warning,.workspace-delete-error{margin:0}.workspace-delete-warning,.workspace-delete-error{font-size:13px}.workspace-delete-error{color:#c74343}.settings-section-title{margin:0}.settings-section,.settings-server-list,.settings-add-server-form{display:flex;flex-direction:column;gap:12px}.settings-form-actions{display:flex;gap:8px}.settings-card{padding:12px;border:1px solid var(--color-border);border-radius:8px}.settings-server{display:flex;align-items:center;justify-content:space-between;gap:12px}.settings-server-details{min-width:0}.settings-server-name{font-weight:600}.settings-server-url{font-size:13px;word-break:break-word;color:color-mix(in srgb,currentColor 70%,transparent)}.settings-server-remove-button,.settings-submit-button,.settings-cancel-button,.settings-connect-button{padding:8px 12px;border-radius:8px}.settings-field{display:flex;flex-direction:column;gap:6px}.settings-field input{width:100%;padding:8px 10px;border:1px solid var(--color-border);border-radius:8px;background:var(--color-bg);color:inherit;font:inherit;box-sizing:border-box}.settings-submit-button,.settings-cancel-button,.settings-connect-button{align-self:flex-start}dropdown-menu{position:relative;display:inline-block;color:inherit}dropdown-group{display:grid;gap:var(--space-4)}dropdown-label{display:block;padding:var(--space-4) var(--space-8);color:var(--color-text-muted);font:inherit;font-size:var(--text-sm);font-weight:600;letter-spacing:.04em;text-transform:uppercase}dropdown-divider{display:block;height:1px;margin:var(--space-4) 0;background:var(--color-border)}[data-dropdown-trigger],dropdown-item{color:inherit;font:inherit;line-height:1.2}[data-dropdown-trigger],dropdown-menu::part(trigger){display:inline-flex;align-items:center;gap:var(--space-8);margin:0;padding:.375rem .5rem;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);color:var(--color-text);box-shadow:none;cursor:pointer}[data-dropdown-trigger]:hover,dropdown-menu::part(trigger):hover{background:var(--color-interactive-hover)}dropdown-menu[open] [data-dropdown-trigger],dropdown-menu[open]::part(trigger){background:var(--color-interactive)}[data-dropdown-trigger-label],dropdown-menu::part(trigger-label){min-width:0}[data-dropdown-trigger-icon],dropdown-menu::part(trigger-icon){display:inline-flex;align-items:center;justify-content:center;flex:none}[data-dropdown-trigger]:focus-visible,dropdown-menu::part(trigger):focus-visible,dropdown-item:focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}dropdown-menu::part(panel){position:absolute;top:calc(100% + var(--space-4));left:0;z-index:var(--layer-popover);min-width:12rem;padding:var(--space-4);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text);box-shadow:var(--shadow-popover)}dropdown-item{display:block;margin:0;padding:.375rem .5rem;border-radius:var(--radius-sm);cursor:pointer;-webkit-user-select:none;user-select:none}dropdown-item:hover{background:var(--color-interactive-hover)}dropdown-item[selected]{background:var(--color-interactive)}panel-view{display:block}panel-view::part(panel){display:flex;flex-direction:column;gap:var(--space-16);color:var(--color-text)}panel-view::part(header){display:flex;align-items:center;justify-content:space-between;gap:var(--space-12)}panel-view::part(title-group){display:flex;align-items:center;gap:var(--space-8);min-width:0}panel-view::part(title){margin:0;font:inherit;font-size:var(--text-lg);font-weight:600;color:var(--color-text)}panel-view::part(button){display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;margin:0;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);color:var(--color-text);box-shadow:none;cursor:pointer}panel-view::part(button):hover{background:var(--color-interactive-hover)}panel-view::part(button):focus-visible{outline:2px solid var(--color-focus-ring);outline-offset:2px}panel-view::part(icon){display:inline-flex;align-items:center;justify-content:center;flex:none}panel-view::part(back-icon){transform:rotate(90deg)}modal-overlay{color:inherit}modal-overlay::part(backdrop){position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--layer-modal);display:flex;align-items:center;justify-content:center;padding:var(--space-16);background:var(--color-overlay-backdrop)}*,*:before,*:after{box-sizing:border-box}:root{--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;--font-brand: var(--font-sans);--font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;--text-base: 14px;--text-scale: 1.125;--text-sm: round(calc(var(--text-base) / var(--text-scale)), 1px);--text-lg: round(calc(var(--text-base) * var(--text-scale)), 1px);--text-xl: round(calc(var(--text-lg) * var(--text-scale)), 1px);--leading-base: 1.5;--space-2: 2px;--space-4: 4px;--space-6: 6px;--space-8: 8px;--space-12: 12px;--space-16: 16px;--space-24: 24px;--space-32: 32px;--space-48: 48px;--space-64: 64px;--radius-sm: .375rem;--radius-md: .5rem;--layer-popover: 10;--layer-modal: 1000;--shadow-popover: 0 12px 32px rgb(0 0 0 / .14);--shadow-modal: 0 24px 64px rgb(0 0 0 / .2)}html,body{margin:0;padding:0;width:100%;height:100%}body{font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);color:var(--color-text);background:var(--color-bg)}a{text-underline-offset:.15em;text-decoration-thickness:.08em}button,input,textarea,select{font:inherit;color:inherit}a{color:var(--color-link)}h1,h2,h3,h4,h5,h6,p,ul,ol,blockquote,pre,hr{margin:0}h1,h2,h3,h4,h5,h6{font-weight:600;color:var(--color-text)}code{font-family:var(--font-mono);font-size:.9em;background:var(--color-bg-muted);padding:var(--space-2) var(--space-4);border-radius:var(--radius-sm)}pre{font-family:var(--font-mono);background:var(--color-bg-muted);padding:var(--space-12);border-radius:var(--radius-md);overflow-x:auto}pre code{background:none;padding:0}blockquote{padding-left:var(--space-16);border-left:3px solid var(--color-border);color:var(--color-text-muted)}hr{border:none;border-top:1px solid var(--color-border)}table{border-collapse:collapse}th,td{padding:var(--space-8) var(--space-12);border:1px solid var(--color-border);text-align:left}th{background:var(--color-bg-muted);font-weight:600}.prose{line-height:var(--leading-base)}.prose h1{font-size:var(--text-xl);margin-bottom:var(--space-8)}.prose h2{font-size:var(--text-lg);margin-top:var(--space-24);margin-bottom:var(--space-8)}.prose h3,.prose h4,.prose h5,.prose h6{font-size:var(--text-base);margin-top:var(--space-24);margin-bottom:var(--space-8)}.prose p{margin:var(--space-12) 0}.prose ul,.prose ol{padding-left:var(--space-24);margin:var(--space-12) 0}.prose blockquote,.prose pre{margin:var(--space-12) 0}.prose hr{margin:var(--space-24) 0}.prose table{width:100%;margin:var(--space-12) 0}.prose img{max-width:100%;height:auto}.prose>:first-child{margin-top:0}.prose>:last-child{margin-bottom:0}[data-ui-icon]{display:inline-block;vertical-align:middle}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-6);border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-bg);color:inherit;cursor:pointer}button:hover{background:var(--color-interactive)}button[variant=toolbar]{border:none;background:transparent;color:var(--color-text-muted)}button[size=sm]{min-height:26px;padding:var(--space-2) var(--space-6)}button[size=md]{min-height:32px;padding:var(--space-6) var(--space-12)}:root{--black: #000000;--white: #ffffff;--neutral-50: oklch(.985 .001 106.423);--neutral-100: oklch(.97 .001 106.424);--neutral-200: oklch(.923 .003 48.717);--neutral-300: oklch(.869 .005 56.366);--neutral-400: oklch(.709 .01 56.259);--neutral-500: oklch(.553 .013 58.071);--neutral-600: oklch(.444 .011 73.639);--neutral-700: oklch(.374 .01 67.558);--neutral-800: oklch(.268 .007 34.298);--neutral-900: oklch(.216 .006 56.043);--neutral-950: oklch(.147 .004 49.25)}:root{--color-bg: var(--neutral-50);--color-bg-muted: var(--neutral-100);--color-surface: var(--white);--color-surface-muted: var(--neutral-100);--color-interactive: var(--neutral-200);--color-interactive-hover: var(--neutral-300);--color-border: var(--neutral-300);--color-border-muted: var(--neutral-200);--color-text: var(--neutral-900);--color-text-muted: var(--neutral-500);--color-link: inherit;--color-focus-ring: var(--neutral-500);--color-overlay-backdrop: rgb(0 0 0 / .5);color-scheme:light}:root{--color-bg: var(--neutral-200);--color-border: var(--neutral-300)}
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Television</title>
7
- <script type="module" crossorigin src="./assets/index-C3ygXWMh.js"></script>
8
- <link rel="stylesheet" crossorigin href="./assets/index-nGpvLl78.css">
7
+ <script type="module" crossorigin src="./assets/index-BEMTFjJ-.js"></script>
8
+ <link rel="stylesheet" crossorigin href="./assets/index-CmOTsDN-.css">
9
9
  </head>
10
10
  <body></body>
11
11
  </html>
package/dist/cli.cjs CHANGED
@@ -77196,13 +77196,21 @@ var Server2 = class {
77196
77196
  });
77197
77197
  }
77198
77198
  start() {
77199
- return new Promise((resolve) => {
77200
- this.httpServer.listen(this.port, this.host, () => {
77199
+ return new Promise((resolve, reject) => {
77200
+ const handleError = (error2) => {
77201
+ this.httpServer.off("listening", handleListening);
77202
+ reject(error2);
77203
+ };
77204
+ const handleListening = () => {
77205
+ this.httpServer.off("error", handleError);
77201
77206
  const address = this.httpServer.address();
77202
77207
  const resolvedPort = typeof address === "object" && address ? address.port : this.port;
77203
77208
  this.baseURL = buildServerURL(this.host ?? DEFAULT_SERVER_HOST, resolvedPort);
77204
77209
  resolve(this.httpServer);
77205
- });
77210
+ };
77211
+ this.httpServer.once("error", handleError);
77212
+ this.httpServer.once("listening", handleListening);
77213
+ this.httpServer.listen(this.port, this.host);
77206
77214
  });
77207
77215
  }
77208
77216
  getBaseURL() {
@@ -77353,15 +77361,29 @@ function isClientMessage(value) {
77353
77361
  case "create-workspace":
77354
77362
  return typeof message.id === "string" && typeof message.name === "string";
77355
77363
  case "update-workspace":
77356
- return typeof message.workspaceID === "string" && typeof message.fields === "object" && message.fields !== null && (!("name" in message.fields) || typeof message.fields.name === "string") && (!("layout" in message.fields) || Array.isArray(message.fields.layout) && message.fields.layout.every(
77357
- (card) => typeof card === "object" && card !== null && "type" in card && card.type === "artifact" && "artifactID" in card && typeof card.artifactID === "string" && "cols" in card && typeof card.cols === "number" && "rows" in card && typeof card.rows === "number"
77358
- ));
77364
+ return typeof message.workspaceID === "string" && typeof message.fields === "object" && message.fields !== null && (!("name" in message.fields) || typeof message.fields.name === "string") && (!("layout" in message.fields) || Array.isArray(message.fields.layout) && message.fields.layout.every(isLayoutNode));
77359
77365
  case "delete-workspace":
77360
77366
  return typeof message.workspaceID === "string";
77361
77367
  default:
77362
77368
  return false;
77363
77369
  }
77364
77370
  }
77371
+ function isLayoutNode(value) {
77372
+ if (typeof value !== "object" || value === null || !("type" in value)) {
77373
+ return false;
77374
+ }
77375
+ const node = value;
77376
+ switch (node.type) {
77377
+ case "card":
77378
+ return typeof node.id === "string" && typeof node.artifactID === "string" && (typeof node.width === "number" || node.width === "auto") && (typeof node.height === "number" || node.height === "auto");
77379
+ case "row":
77380
+ return typeof node.id === "string" && (typeof node.height === "number" || node.height === "auto") && Array.isArray(node.children) && node.children.every(isLayoutNode);
77381
+ case "stack":
77382
+ return typeof node.id === "string" && Array.isArray(node.children) && node.children.every(isLayoutNode);
77383
+ default:
77384
+ return false;
77385
+ }
77386
+ }
77365
77387
 
77366
77388
  // node_modules/mitt/dist/mitt.mjs
77367
77389
  function mitt_default(n) {
@@ -77413,54 +77435,324 @@ var import_node_crypto2 = require("node:crypto");
77413
77435
  var import_node_fs3 = require("node:fs");
77414
77436
  var import_node_path4 = __toESM(require("node:path"), 1);
77415
77437
 
77438
+ // src/constants.ts
77439
+ var LAYOUT_FULL_WIDTH_UNITS = 4;
77440
+ var LAYOUT_FULL_HEIGHT_UNITS = 6;
77441
+ var MIN_LAYOUT_CARD_WIDTH_UNITS = 2;
77442
+ var MAX_LAYOUT_CARD_WIDTH_UNITS = LAYOUT_FULL_WIDTH_UNITS;
77443
+ var DEFAULT_LAYOUT_CARD_WIDTH = "auto";
77444
+ var DEFAULT_LAYOUT_CARD_HEIGHT = "auto";
77445
+ var DEFAULT_LAYOUT_UNIT = 120;
77446
+ var CARD_GAP = 16;
77447
+ var CARD_WIDTH = DEFAULT_LAYOUT_UNIT * LAYOUT_FULL_WIDTH_UNITS + CARD_GAP * (LAYOUT_FULL_WIDTH_UNITS - 1);
77448
+ var CARD_HEIGHT = DEFAULT_LAYOUT_UNIT * LAYOUT_FULL_HEIGHT_UNITS + CARD_GAP * (LAYOUT_FULL_HEIGHT_UNITS - 1);
77449
+
77416
77450
  // src/types.ts
77417
- var DEFAULT_LAYOUT_CARD_COLS = 2;
77418
- var DEFAULT_LAYOUT_CARD_ROWS = 6;
77419
- var LAYOUT_COLS_NARROW = 1;
77420
- var LAYOUT_ROW_SHORT = 2;
77421
- var LAYOUT_ROW_MID = 3;
77422
- var LAYOUT_ROW_TALL = 4;
77423
- var ALLOWED_COLS = [LAYOUT_COLS_NARROW, DEFAULT_LAYOUT_CARD_COLS];
77424
- var ALLOWED_ROWS = [
77425
- LAYOUT_ROW_SHORT,
77426
- LAYOUT_ROW_MID,
77427
- LAYOUT_ROW_TALL,
77428
- DEFAULT_LAYOUT_CARD_ROWS
77429
- ];
77430
- function clampLayoutCols(cols) {
77431
- const n = Math.round(cols);
77432
- return ALLOWED_COLS.includes(n) ? n : DEFAULT_LAYOUT_CARD_COLS;
77433
- }
77434
- function clampLayoutRows(rows) {
77435
- const n = Math.round(rows);
77436
- return ALLOWED_ROWS.includes(n) ? n : DEFAULT_LAYOUT_CARD_ROWS;
77437
- }
77438
- function createLayoutCardRecord(artifactID) {
77451
+ function createCardNode(artifactID, overrides = {}) {
77439
77452
  return {
77440
- type: "artifact",
77453
+ id: overrides.id ?? ulid3(),
77454
+ type: "card",
77441
77455
  artifactID,
77442
- cols: DEFAULT_LAYOUT_CARD_COLS,
77443
- rows: DEFAULT_LAYOUT_CARD_ROWS
77456
+ width: overrides.width ?? DEFAULT_LAYOUT_CARD_WIDTH,
77457
+ height: overrides.height ?? DEFAULT_LAYOUT_CARD_HEIGHT
77444
77458
  };
77445
77459
  }
77446
- function sanitizeLayoutCard(card) {
77460
+ function layoutContainsArtifactID(layout, artifactID) {
77461
+ return layout.some((node) => nodeContainsArtifactID(node, artifactID));
77462
+ }
77463
+ function nodeContainsArtifactID(node, artifactID) {
77464
+ switch (node.type) {
77465
+ case "card":
77466
+ return node.artifactID === artifactID;
77467
+ case "row":
77468
+ return node.children.some((child) => child.artifactID === artifactID);
77469
+ case "stack":
77470
+ return node.children.some((child) => nodeContainsArtifactID(child, artifactID));
77471
+ }
77472
+ }
77473
+ function getWorkspaceArtifactIDs(workspace) {
77474
+ return workspace.layout.flatMap(collectArtifactIDs);
77475
+ }
77476
+ function collectArtifactIDs(node) {
77477
+ switch (node.type) {
77478
+ case "card":
77479
+ return [node.artifactID];
77480
+ case "row":
77481
+ return node.children.map((child) => child.artifactID);
77482
+ case "stack":
77483
+ return node.children.flatMap(collectArtifactIDs);
77484
+ }
77485
+ }
77486
+
77487
+ // src/browser/ui/layout.ts
77488
+ var LEGACY_BAND_HEIGHT_ROWS = 6;
77489
+ var LEGACY_NARROW_COLS = 1;
77490
+ var LEGACY_FULL_COLS = 2;
77491
+ var LEGACY_ROW_SHORT = 2;
77492
+ var LEGACY_ROW_MID = 3;
77493
+ var LEGACY_ROW_TALL = 4;
77494
+ var LEGACY_ROW_FULL = 6;
77495
+ var LEGACY_ALLOWED_ROWS = [LEGACY_ROW_SHORT, LEGACY_ROW_MID, LEGACY_ROW_TALL, LEGACY_ROW_FULL];
77496
+ var LEGACY_LEFT_X = 0;
77497
+ var LEGACY_RIGHT_X = 1;
77498
+ function removeArtifactFromLayout(layout, artifactID) {
77499
+ return normalizeRootNodes(layout.flatMap((node) => removeArtifactFromNode(node, artifactID)));
77500
+ }
77501
+ function migrateLegacyLayout(cards) {
77502
+ const normalized = cards.map((card) => ({
77503
+ artifactID: card.artifactID,
77504
+ width: normalizeLegacyCols(card.width),
77505
+ height: normalizeLegacyRows(card.height)
77506
+ }));
77507
+ const bands = deriveLegacyBands(normalized);
77508
+ return normalizeRootNodes(bands.flatMap(buildNodesForLegacyBand));
77509
+ }
77510
+ function normalizeRootNodes(layout) {
77511
+ const normalized = [];
77512
+ for (const node of layout) {
77513
+ const entries = normalizeNode(node);
77514
+ for (const entry of entries) {
77515
+ if (entry.type === "row") {
77516
+ normalized.push(...entry.children.map(cloneCard));
77517
+ continue;
77518
+ }
77519
+ normalized.push(entry);
77520
+ }
77521
+ }
77522
+ return normalized;
77523
+ }
77524
+ function normalizeNode(node) {
77525
+ switch (node.type) {
77526
+ case "card":
77527
+ return [cloneCard(node)];
77528
+ case "row": {
77529
+ const children = node.children.map(cloneCard);
77530
+ if (children.length === 0) {
77531
+ return [];
77532
+ }
77533
+ if (children.length === 1) {
77534
+ return [children[0]];
77535
+ }
77536
+ return [{ ...node, children }];
77537
+ }
77538
+ case "stack": {
77539
+ const children = [];
77540
+ for (const child of node.children) {
77541
+ const normalizedChildren = normalizeNode(child);
77542
+ for (const normalizedChild of normalizedChildren) {
77543
+ if (normalizedChild.type === "stack") {
77544
+ children.push(...normalizedChild.children.map(cloneStackChild));
77545
+ continue;
77546
+ }
77547
+ if (normalizedChild.type === "row") {
77548
+ children.push({
77549
+ ...normalizedChild,
77550
+ children: normalizedChild.children.map(cloneCard)
77551
+ });
77552
+ continue;
77553
+ }
77554
+ children.push(cloneCard(normalizedChild));
77555
+ }
77556
+ }
77557
+ if (children.length === 0) {
77558
+ return [];
77559
+ }
77560
+ if (children.length === 1) {
77561
+ return [cloneStackChild(children[0])];
77562
+ }
77563
+ return [{ ...node, children }];
77564
+ }
77565
+ }
77566
+ }
77567
+ function removeArtifactFromNode(node, artifactID) {
77568
+ switch (node.type) {
77569
+ case "card":
77570
+ return node.artifactID === artifactID ? [] : [cloneCard(node)];
77571
+ case "row": {
77572
+ const children = node.children.filter((child) => child.artifactID !== artifactID).map(cloneCard);
77573
+ if (children.length === 0) {
77574
+ return [];
77575
+ }
77576
+ if (children.length === 1) {
77577
+ return [children[0]];
77578
+ }
77579
+ return [{ ...node, children }];
77580
+ }
77581
+ case "stack": {
77582
+ const children = node.children.flatMap((child) => {
77583
+ const removed = removeArtifactFromNode(child, artifactID);
77584
+ return removed.filter((entry) => entry.type !== "stack");
77585
+ });
77586
+ if (children.length === 0) {
77587
+ return [];
77588
+ }
77589
+ if (children.length === 1) {
77590
+ return [cloneStackChild(children[0])];
77591
+ }
77592
+ return [{ ...node, children }];
77593
+ }
77594
+ }
77595
+ }
77596
+ function normalizeLegacyCols(width) {
77597
+ return Math.round(width) === LEGACY_NARROW_COLS ? LEGACY_NARROW_COLS : LEGACY_FULL_COLS;
77598
+ }
77599
+ function normalizeLegacyRows(height) {
77600
+ const rounded = Math.round(height);
77601
+ return LEGACY_ALLOWED_ROWS.includes(rounded) ? rounded : LEGACY_BAND_HEIGHT_ROWS;
77602
+ }
77603
+ function deriveLegacyBands(cards) {
77604
+ const bands = [];
77605
+ let current = null;
77606
+ for (const card of cards) {
77607
+ if (!current) {
77608
+ const placements = packLegacyBand([card], card.width);
77609
+ if (!placements) {
77610
+ continue;
77611
+ }
77612
+ current = {
77613
+ width: card.width,
77614
+ placements
77615
+ };
77616
+ continue;
77617
+ }
77618
+ const nextCards = current.placements.map((placement) => ({
77619
+ artifactID: placement.artifactID,
77620
+ width: placement.width,
77621
+ height: placement.height
77622
+ })).concat(card);
77623
+ let nextWidth = current.width;
77624
+ let packed = packLegacyBand(nextCards, nextWidth);
77625
+ if (!packed && current.width < LEGACY_FULL_COLS && card.width > current.width) {
77626
+ nextWidth = LEGACY_FULL_COLS;
77627
+ packed = packLegacyBand(nextCards, nextWidth);
77628
+ }
77629
+ if (packed) {
77630
+ current = {
77631
+ width: nextWidth,
77632
+ placements: packed
77633
+ };
77634
+ continue;
77635
+ }
77636
+ bands.push(current);
77637
+ const newPlacements = packLegacyBand([card], card.width);
77638
+ if (!newPlacements) {
77639
+ current = null;
77640
+ continue;
77641
+ }
77642
+ current = {
77643
+ width: card.width,
77644
+ placements: newPlacements
77645
+ };
77646
+ }
77647
+ if (current) {
77648
+ bands.push(current);
77649
+ }
77650
+ return bands;
77651
+ }
77652
+ function packLegacyBand(cards, width) {
77653
+ const occupied = Array.from(
77654
+ { length: LEGACY_BAND_HEIGHT_ROWS },
77655
+ () => Array.from({ length: width }, () => false)
77656
+ );
77657
+ const placements = [];
77658
+ for (const card of cards) {
77659
+ const position = firstLegacyFit(occupied, width, card);
77660
+ if (!position) {
77661
+ return null;
77662
+ }
77663
+ const placement = {
77664
+ artifactID: card.artifactID,
77665
+ width: card.width,
77666
+ height: card.height,
77667
+ x: position.x,
77668
+ y: position.y
77669
+ };
77670
+ placeLegacyCard(occupied, placement);
77671
+ placements.push(placement);
77672
+ }
77673
+ return placements;
77674
+ }
77675
+ function firstLegacyFit(occupied, width, card) {
77676
+ if (card.width > width || card.height > LEGACY_BAND_HEIGHT_ROWS) {
77677
+ return null;
77678
+ }
77679
+ for (let y2 = 0; y2 <= LEGACY_BAND_HEIGHT_ROWS - card.height; y2 += 1) {
77680
+ for (let x2 = 0; x2 <= width - card.width; x2 += 1) {
77681
+ let fits = true;
77682
+ for (let yy = y2; yy < y2 + card.height && fits; yy += 1) {
77683
+ for (let xx = x2; xx < x2 + card.width; xx += 1) {
77684
+ if (occupied[yy]?.[xx]) {
77685
+ fits = false;
77686
+ break;
77687
+ }
77688
+ }
77689
+ }
77690
+ if (fits) {
77691
+ return { x: x2, y: y2 };
77692
+ }
77693
+ }
77694
+ }
77695
+ return null;
77696
+ }
77697
+ function placeLegacyCard(occupied, placement) {
77698
+ for (let yy = placement.y; yy < placement.y + placement.height; yy += 1) {
77699
+ for (let xx = placement.x; xx < placement.x + placement.width; xx += 1) {
77700
+ occupied[yy][xx] = true;
77701
+ }
77702
+ }
77703
+ }
77704
+ function buildNodesForLegacyBand(band) {
77705
+ const placements = [...band.placements].sort((a, b2) => a.y - b2.y || a.x - b2.x);
77706
+ const children = [];
77707
+ for (let index = 0; index < placements.length; index += 1) {
77708
+ const current = placements[index];
77709
+ const next = placements[index + 1];
77710
+ if (current.width === LEGACY_NARROW_COLS && next && next.width === LEGACY_NARROW_COLS && next.y === current.y && next.height === current.height && current.x === LEGACY_LEFT_X && next.x === LEGACY_RIGHT_X) {
77711
+ children.push({
77712
+ id: ulid3(),
77713
+ type: "row",
77714
+ height: current.height,
77715
+ children: [migrateLegacyCard(current), migrateLegacyCard(next)]
77716
+ });
77717
+ index += 1;
77718
+ continue;
77719
+ }
77720
+ children.push(migrateLegacyCard(current));
77721
+ }
77722
+ if (children.length === 0) {
77723
+ return [];
77724
+ }
77725
+ if (children.length === 1) {
77726
+ const child = children[0];
77727
+ return child.type === "row" ? child.children.map(cloneCard) : [cloneCard(child)];
77728
+ }
77729
+ return [
77730
+ {
77731
+ id: ulid3(),
77732
+ type: "stack",
77733
+ children
77734
+ }
77735
+ ];
77736
+ }
77737
+ function migrateLegacyCard(card) {
77447
77738
  return {
77448
- type: "artifact",
77739
+ id: ulid3(),
77740
+ type: "card",
77449
77741
  artifactID: card.artifactID,
77450
- cols: clampLayoutCols(card.cols ?? DEFAULT_LAYOUT_CARD_COLS),
77451
- rows: clampLayoutRows(card.rows ?? DEFAULT_LAYOUT_CARD_ROWS)
77742
+ width: card.width === LEGACY_NARROW_COLS ? MIN_LAYOUT_CARD_WIDTH_UNITS : MAX_LAYOUT_CARD_WIDTH_UNITS,
77743
+ height: card.height
77452
77744
  };
77453
77745
  }
77454
- function migrateWorkspaceData(workspace) {
77455
- const layout = Array.isArray(workspace.layout) ? workspace.layout.map((card) => sanitizeLayoutCard(card)) : [];
77746
+ function cloneCard(node) {
77456
77747
  return {
77457
- id: workspace.id,
77458
- name: workspace.name,
77459
- layout
77748
+ ...node
77460
77749
  };
77461
77750
  }
77462
- function getWorkspaceArtifactIDs(workspace) {
77463
- return workspace.layout.map((card) => card.artifactID);
77751
+ function cloneStackChild(node) {
77752
+ return node.type === "card" ? cloneCard(node) : {
77753
+ ...node,
77754
+ children: node.children.map(cloneCard)
77755
+ };
77464
77756
  }
77465
77757
 
77466
77758
  // src/server/server-store.ts
@@ -77480,26 +77772,51 @@ function validateHTMLFragment(content) {
77480
77772
  }
77481
77773
  }
77482
77774
  function migrateWorkspaceJson(raw) {
77483
- if ("layout" in raw && Array.isArray(raw.layout) && raw.layout.length > 0) {
77484
- return false;
77485
- }
77486
77775
  if ("artifacts" in raw && Array.isArray(raw.artifacts)) {
77487
77776
  raw.layout = raw.artifacts;
77488
77777
  delete raw.artifacts;
77489
77778
  return true;
77490
77779
  }
77491
77780
  if ("artifactIDs" in raw && Array.isArray(raw.artifactIDs)) {
77492
- raw.layout = raw.artifactIDs.map((id) => ({
77493
- type: "artifact",
77494
- artifactID: id,
77495
- cols: DEFAULT_COLS,
77496
- rows: DEFAULT_ROWS
77497
- }));
77781
+ raw.layout = raw.artifactIDs.map((id) => ({ artifactID: id, width: DEFAULT_COLS, height: DEFAULT_ROWS }));
77498
77782
  delete raw.artifactIDs;
77499
77783
  return true;
77500
77784
  }
77501
77785
  return false;
77502
77786
  }
77787
+ function deserializeWorkspaceData(workspace) {
77788
+ const rawLayout = workspace.layout;
77789
+ if (!Array.isArray(rawLayout)) {
77790
+ return {
77791
+ migrated: false,
77792
+ workspace: {
77793
+ id: workspace.id,
77794
+ name: workspace.name,
77795
+ layout: []
77796
+ }
77797
+ };
77798
+ }
77799
+ if (rawLayout.every(
77800
+ (node) => typeof node === "object" && node !== null && "type" in node && (node.type === "card" || node.type === "stack")
77801
+ )) {
77802
+ return {
77803
+ migrated: false,
77804
+ workspace: {
77805
+ id: workspace.id,
77806
+ name: workspace.name,
77807
+ layout: rawLayout
77808
+ }
77809
+ };
77810
+ }
77811
+ return {
77812
+ migrated: true,
77813
+ workspace: {
77814
+ id: workspace.id,
77815
+ name: workspace.name,
77816
+ layout: migrateLegacyLayout(rawLayout)
77817
+ }
77818
+ };
77819
+ }
77503
77820
  var ServerStore = class extends Emitter {
77504
77821
  dataDir;
77505
77822
  workspaces = /* @__PURE__ */ new Map();
@@ -77533,7 +77850,7 @@ var ServerStore = class extends Emitter {
77533
77850
  }
77534
77851
  findArtifactWorkspaces(artifactID) {
77535
77852
  return [...this.workspaces.values()].filter(
77536
- (workspace) => workspace.layout.some((card) => card.artifactID === artifactID)
77853
+ (workspace) => layoutContainsArtifactID(workspace.layout, artifactID)
77537
77854
  );
77538
77855
  }
77539
77856
  createArtifact(input) {
@@ -77551,7 +77868,7 @@ var ServerStore = class extends Emitter {
77551
77868
  content: input.content
77552
77869
  });
77553
77870
  this.artifacts.set(artifact.id, artifact);
77554
- workspace.layout.push(createLayoutCardRecord(artifact.id));
77871
+ workspace.layout = [...workspace.layout, createCardNode(artifact.id)];
77555
77872
  this.persistArtifact(artifact);
77556
77873
  this.persistWorkspace(workspace);
77557
77874
  this.emit("mutation", {
@@ -77584,11 +77901,11 @@ var ServerStore = class extends Emitter {
77584
77901
  throw new Error(`Artifact not found: ${input.artifactID}`);
77585
77902
  }
77586
77903
  const workspace = this.requireWorkspace(input.workspaceID);
77587
- if (!workspace.layout.some((card) => card.artifactID === input.artifactID)) {
77904
+ if (!layoutContainsArtifactID(workspace.layout, input.artifactID)) {
77588
77905
  throw new Error(`Artifact not linked to workspace: ${input.artifactID}`);
77589
77906
  }
77590
77907
  this.artifacts.delete(input.artifactID);
77591
- workspace.layout = workspace.layout.filter((card) => card.artifactID !== input.artifactID);
77908
+ workspace.layout = removeArtifactFromLayout(workspace.layout, input.artifactID);
77592
77909
  this.persistWorkspace(workspace);
77593
77910
  this.deleteArtifactFile(input.artifactID);
77594
77911
  this.emit("mutation", {
@@ -77619,7 +77936,7 @@ var ServerStore = class extends Emitter {
77619
77936
  nextFields.name = input.fields.name;
77620
77937
  }
77621
77938
  if (Array.isArray(input.fields.layout)) {
77622
- workspace.layout = input.fields.layout.map((card) => sanitizeLayoutCard(card));
77939
+ workspace.layout = input.fields.layout.map((node) => structuredClone(node));
77623
77940
  nextFields.layout = workspace.layout;
77624
77941
  }
77625
77942
  this.persistWorkspace(workspace);
@@ -77657,10 +77974,10 @@ var ServerStore = class extends Emitter {
77657
77974
  const filePath = import_node_path4.default.join(this.workspacesDir, file2);
77658
77975
  const raw = this.readJsonFile(filePath);
77659
77976
  if (!raw || typeof raw !== "object") continue;
77660
- const migrated = migrateWorkspaceJson(raw);
77661
- const workspace = migrateWorkspaceData(raw);
77977
+ const renamedLegacyFields = migrateWorkspaceJson(raw);
77978
+ const { workspace, migrated } = deserializeWorkspaceData(raw);
77662
77979
  this.workspaces.set(workspace.id, workspace);
77663
- if (migrated) {
77980
+ if (renamedLegacyFields || migrated) {
77664
77981
  this.persistWorkspace(workspace);
77665
77982
  }
77666
77983
  }