@cychien/ezdesign 0.9.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +209 -550
  2. package/dist/cli.mjs +2640 -655
  3. package/dist/constants.d.ts +8 -4
  4. package/dist/extension/g/0.11.0-75c3fbebc3/dialog.js +1 -0
  5. package/dist/extension/g/0.11.0-75c3fbebc3/media.js +1 -0
  6. package/dist/extension/g/0.11.0-75c3fbebc3/page.js +44 -0
  7. package/dist/extension/g/0.11.0-75c3fbebc3/worker.js +57 -0
  8. package/dist/extension/manifest.json +15 -12
  9. package/dist/home.css +4706 -0
  10. package/dist/home.js +440 -0
  11. package/dist/native-host.mjs +33804 -115
  12. package/dist/overlay.css +5 -1
  13. package/dist/overlay.js +73 -22
  14. package/dist/shell.css +297 -1
  15. package/dist/shell.js +1230 -358
  16. package/dist/video-digest.js +16388 -0
  17. package/dist/vite.mjs +5 -1
  18. package/package.json +4 -3
  19. package/skills/ezdesign/SKILL.md +7 -3
  20. package/skills/ezdesign/reference/start.md +2 -1
  21. package/dist/extension/g/0.9.2-d9aee83dae/dialog.js +0 -1
  22. package/dist/extension/g/0.9.2-d9aee83dae/media.js +0 -1
  23. package/dist/extension/g/0.9.2-d9aee83dae/page.js +0 -39
  24. package/dist/extension/g/0.9.2-d9aee83dae/worker.js +0 -57
  25. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/dialog.css +0 -0
  26. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/dialog.html +0 -0
  27. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/icon-128.png +0 -0
  28. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/icon-16.png +0 -0
  29. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/icon-32.png +0 -0
  30. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/icon-48.png +0 -0
  31. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/media.html +0 -0
  32. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/setup.css +0 -0
  33. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/setup.html +0 -0
  34. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/setup.js +0 -0
package/dist/overlay.css CHANGED
@@ -188,9 +188,11 @@
188
188
  color: var(--ez-ink);
189
189
  white-space: pre-wrap;
190
190
  word-break: break-word;
191
- outline: none;
192
191
  overscroll-behavior: contain;
193
192
  }
193
+ .ez-input:is(:focus, :focus-visible) {
194
+ outline: none;
195
+ }
194
196
  .ez-input[data-empty]::before {
195
197
  content: attr(data-placeholder);
196
198
  position: absolute;
@@ -254,6 +256,8 @@
254
256
  background: #fff;
255
257
  border: 1px solid var(--ez-paper-edge);
256
258
  cursor: auto;
259
+ transform-origin: top left;
260
+ transform: scale(var(--ez-ui-scale, 1));
257
261
  box-shadow: 0 12px 32px oklch(0% 0 0 / 0.12), 0 2px 8px oklch(0% 0 0 / 0.06);
258
262
  }
259
263
  .ez-slash-head {
package/dist/overlay.js CHANGED
@@ -147,23 +147,53 @@
147
147
  // src/client/slash.ts
148
148
  var MAX_QUERY = 24;
149
149
  var BREAK = /[\s ]/;
150
+ var PATH_CHAR = /[./\\:@#%&=?~+*_-]/;
151
+ function opensTrigger(prev) {
152
+ return BREAK.test(prev) || /[\p{P}\p{S}]/u.test(prev) && !PATH_CHAR.test(prev);
153
+ }
150
154
  var menuCount = 0;
155
+ function menuPosition(at, box, view, scale) {
156
+ const margin = 8 * scale;
157
+ const gap = 6 * scale;
158
+ const left = Math.max(margin, Math.min(at.left, view.width - box.width - margin));
159
+ const below = at.bottom + gap;
160
+ const above = at.top - box.height - gap;
161
+ const crowded = below + box.height > view.height - margin || at.top > view.height * 0.6;
162
+ const top = crowded && above >= margin ? above : Math.min(below, view.height - box.height - margin);
163
+ return { left, top };
164
+ }
151
165
  function detectSlash(before, trigger = "/") {
152
166
  const start = before.lastIndexOf(trigger);
153
167
  if (start === -1) return null;
154
168
  const prev = start === 0 ? "" : before[start - 1];
155
- if (prev && !BREAK.test(prev)) return null;
169
+ if (prev && !opensTrigger(prev)) return null;
156
170
  const query = before.slice(start + 1);
157
171
  if (query.length > MAX_QUERY || BREAK.test(query)) return null;
158
172
  return { start, query };
159
173
  }
174
+ var WORD_BREAK = /[^\p{L}\p{N}]/u;
175
+ function rank(command, q) {
176
+ const names = [command.id, command.label].map((term) => term.toLowerCase());
177
+ if (names.some((name) => name.startsWith(q))) return 0;
178
+ if (names.some((name) => startsWord(name, q))) return 1;
179
+ if ([...names, ...command.keywords.map((k) => k.toLowerCase())].some((term) => term.includes(q))) return 2;
180
+ return null;
181
+ }
182
+ function startsWord(text, q) {
183
+ let at = text.indexOf(q);
184
+ while (at !== -1) {
185
+ if (at === 0 || WORD_BREAK.test(text.charAt(at - 1))) return true;
186
+ at = text.indexOf(q, at + 1);
187
+ }
188
+ return false;
189
+ }
160
190
  function filterCommands(commands, query) {
161
191
  const available = commands.filter((c) => c.enabled?.() ?? true);
162
192
  const q = query.trim().toLowerCase();
163
193
  if (!q) return available;
164
- return available.filter(
165
- (c) => [c.id, c.label, ...c.keywords].some((term) => term.toLowerCase().includes(q))
166
- );
194
+ const ranked = available.map((command) => ({ command, rank: rank(command, q) })).filter((r) => r.rank !== null).sort((a, b) => a.rank - b.rank).map((r) => r.command);
195
+ if (ranked.length > 0) return ranked;
196
+ return available.filter((c) => c.description && startsWord(c.description.toLowerCase(), q));
167
197
  }
168
198
  function attachSlashMenu(editable, {
169
199
  mk,
@@ -203,11 +233,8 @@
203
233
  const at = slashRect();
204
234
  if (!at) return;
205
235
  const box = menu.getBoundingClientRect();
206
- const left = Math.max(8, Math.min(at.left, view.innerWidth - box.width - 8));
207
- const below = at.bottom + 6;
208
- const above = at.top - box.height - 6;
209
- const crowded = below + box.height > view.innerHeight - 8 || at.top > view.innerHeight * 0.6;
210
- const top = crowded && above >= 8 ? above : Math.min(below, view.innerHeight - box.height - 8);
236
+ const scale = box.width / menu.offsetWidth || 1;
237
+ const { left, top } = menuPosition(at, box, { width: view.innerWidth, height: view.innerHeight }, scale);
211
238
  menu.style.left = `${left}px`;
212
239
  menu.style.top = `${top}px`;
213
240
  }
@@ -363,8 +390,19 @@
363
390
  return { close };
364
391
  }
365
392
 
393
+ // src/library/policy.ts
394
+ var VIDEO_CAP_BYTES = 2e8;
395
+
396
+ // src/attachment-policy.ts
397
+ var MAX_ATTACHMENT_BYTES = 8 * 1024 * 1024;
398
+ function isVideoMime(mime) {
399
+ return mime.startsWith("video/");
400
+ }
401
+ function attachmentByteLimit(mime) {
402
+ return isVideoMime(mime) ? VIDEO_CAP_BYTES : MAX_ATTACHMENT_BYTES;
403
+ }
404
+
366
405
  // src/client/attach.ts
367
- var MAX_BYTES = 8 * 1024 * 1024;
368
406
  function directAttachments(api) {
369
407
  return {
370
408
  async upload(file) {
@@ -536,8 +574,9 @@
536
574
  placeCaretAfter(after);
537
575
  }
538
576
  function add(file) {
539
- if (file.size > MAX_BYTES) {
540
- showError(`${file.name || "\u9019\u500B\u6A94\u6848"} \u8D85\u904E\u55AE\u6A94 8 MB \u4E0A\u9650`);
577
+ const limit = attachmentByteLimit(file.type);
578
+ if (file.size > limit) {
579
+ showError(`${file.name || "\u9019\u500B\u6A94\u6848"} \u8D85\u904E\u55AE\u6A94 ${Math.round(limit / 1e6)} MB \u4E0A\u9650`);
541
580
  return;
542
581
  }
543
582
  const chip = fileChip(file.name || "\u8CBC\u4E0A\u7684\u5716\u7247");
@@ -723,6 +762,16 @@
723
762
  refs: () => draftRefs(snapshot()),
724
763
  pending: () => [...uploading].filter((c) => editable.contains(c)).length,
725
764
  pendingNames: () => draftPendingNames(snapshot()),
765
+ paintNotes(noteFor) {
766
+ editable.querySelectorAll(`[${CHIP_ATTR}], [${PICK_ATTR}]`).forEach((chip) => {
767
+ const ids = chip.hasAttribute(PICK_ATTR) ? parsePick(chip.getAttribute(PICK_ATTR)).map((f) => f.id) : [chip.getAttribute(CHIP_ATTR) ?? ""].filter(Boolean);
768
+ const text = ids.length ? noteFor(ids) : null;
769
+ let note = chip.querySelector(".ez-chip-note");
770
+ if (!text) return note?.remove();
771
+ note ?? (note = chip.querySelector(".ez-chip-name").insertAdjacentElement("afterend", mk("span", "ez-chip-note")));
772
+ note.textContent = text;
773
+ });
774
+ },
726
775
  // Either menu, whichever is up - at most one can be, since a trigger only
727
776
  // counts at the start of the word being typed.
728
777
  closeSlash: () => slash.close() || skillMenu.close(),
@@ -868,6 +917,7 @@
868
917
  };
869
918
  }
870
919
  var RELAY_TIMEOUT_MS = 1e4;
920
+ var UPLOAD_BYTES_PER_MS = 2e4;
871
921
  function relayLink(config) {
872
922
  const { shellOrigin } = config;
873
923
  const pending = /* @__PURE__ */ new Map();
@@ -891,13 +941,13 @@
891
941
  for (const listener of listeners) listener(data.snapshot);
892
942
  }
893
943
  });
894
- const request = (op, transfer) => new Promise((resolve, reject) => {
944
+ const request = (op, timeoutMs = RELAY_TIMEOUT_MS) => new Promise((resolve, reject) => {
895
945
  const reqId = `r${++seq}-${Date.now().toString(36)}`;
896
946
  pending.set(reqId, { resolve, reject });
897
947
  window.setTimeout(() => {
898
948
  if (pending.delete(reqId)) reject(new Error("the review did not answer"));
899
- }, RELAY_TIMEOUT_MS);
900
- window.parent?.postMessage({ type: "ez:relay", reqId, op }, shellOrigin, transfer);
949
+ }, timeoutMs);
950
+ window.parent?.postMessage({ type: "ez:relay", reqId, op }, shellOrigin);
901
951
  });
902
952
  return {
903
953
  kind: "relay",
@@ -906,13 +956,12 @@
906
956
  await request({ op: "annotate", body });
907
957
  },
908
958
  attachments: {
959
+ // The file, not `arrayBuffer()`: a Blob is structured-cloned without reading
960
+ // it, so this frame never holds a 200 MB recording's bytes on its heap.
909
961
  upload: async (file) => {
910
- const bytes = await file.arrayBuffer();
911
962
  const name = file.name || "image.png";
912
- return await request(
913
- { op: "upload", name, mime: file.type || "application/octet-stream", bytes },
914
- [bytes]
915
- );
963
+ const op = { op: "upload", name, mime: file.type || "application/octet-stream", file };
964
+ return await request(op, RELAY_TIMEOUT_MS + file.size / UPLOAD_BYTES_PER_MS);
916
965
  },
917
966
  discard: (id) => {
918
967
  request({ op: "discard-upload", id }).catch(() => {
@@ -2441,14 +2490,16 @@
2441
2490
  }
2442
2491
  function onScrollAttempt(e) {
2443
2492
  const w = e.type === "wheel" ? e : null;
2444
- if (w && (w.metaKey || w.ctrlKey) && window.parent !== window) {
2493
+ const zoom = !!w && (w.metaKey || w.ctrlKey);
2494
+ const boardPan = link.kind === "relay" && (mode !== "off" || pick2 !== null) && !isOwnUi(e.target);
2495
+ if (w && (zoom || boardPan) && window.parent !== window) {
2445
2496
  e.preventDefault();
2446
2497
  const unit = w.deltaMode === 1 ? 16 : w.deltaMode === 2 ? window.innerHeight : 1;
2447
2498
  post({
2448
2499
  type: "ez:wheel",
2449
2500
  dx: w.deltaX * unit,
2450
2501
  dy: w.deltaY * unit,
2451
- ...link.kind === "relay" ? { zoom: true, at: { x: w.clientX, y: w.clientY } } : {}
2502
+ ...zoom && link.kind === "relay" ? { zoom: true, at: { x: w.clientX, y: w.clientY } } : {}
2452
2503
  });
2453
2504
  return;
2454
2505
  }
package/dist/shell.css CHANGED
@@ -771,6 +771,20 @@ body.ez-arranging .ez-frame {
771
771
  opacity: 0.4;
772
772
  cursor: default;
773
773
  }
774
+ .ez-send-waiting {
775
+ animation: ez-send-wait 1.4s ease-in-out infinite;
776
+ }
777
+ @keyframes ez-send-wait {
778
+ 50% {
779
+ opacity: 0.55;
780
+ }
781
+ }
782
+ @media (prefers-reduced-motion: reduce) {
783
+ .ez-send-waiting {
784
+ animation: none;
785
+ opacity: 0.7;
786
+ }
787
+ }
774
788
  .ez-notices {
775
789
  position: absolute;
776
790
  left: 0;
@@ -963,6 +977,219 @@ body.ez-arranging .ez-frame {
963
977
  .ez-update[hidden] {
964
978
  display: none;
965
979
  }
980
+ .ez-devserver-card {
981
+ width: min(520px, calc(100% - 48px));
982
+ margin: max(48px, 12vh) auto 48px;
983
+ font-size: 13.5px;
984
+ line-height: 1.55;
985
+ }
986
+ .ez-devserver-card[hidden] {
987
+ display: none;
988
+ }
989
+ .ez-devserver-title {
990
+ margin: 0;
991
+ font-size: 16px;
992
+ font-weight: 600;
993
+ line-height: 1.4;
994
+ }
995
+ .ez-devserver-text {
996
+ margin: 6px 0 0;
997
+ color: var(--muted-foreground);
998
+ }
999
+ .ez-devserver-hint {
1000
+ margin: 10px 0 0;
1001
+ font-size: 12.5px;
1002
+ color: var(--muted-foreground);
1003
+ }
1004
+ .ez-devserver-command {
1005
+ display: flex;
1006
+ flex-direction: column;
1007
+ gap: 4px;
1008
+ margin-top: 14px;
1009
+ padding: 10px 12px;
1010
+ border-radius: 8px;
1011
+ background: var(--muted);
1012
+ }
1013
+ .ez-devserver-line,
1014
+ .ez-devserver-tool,
1015
+ .ez-devserver-log,
1016
+ .ez-devserver-input {
1017
+ font-family:
1018
+ ui-monospace,
1019
+ SFMono-Regular,
1020
+ Menlo,
1021
+ monospace;
1022
+ }
1023
+ .ez-devserver-line {
1024
+ font-size: 13px;
1025
+ -webkit-text-stroke: 0.3px currentColor;
1026
+ color: var(--foreground);
1027
+ word-break: break-all;
1028
+ }
1029
+ .ez-devserver-source {
1030
+ font-size: 12px;
1031
+ color: var(--muted-foreground);
1032
+ }
1033
+ .ez-devserver-log {
1034
+ margin: 14px 0 0;
1035
+ max-height: 260px;
1036
+ overflow: auto;
1037
+ padding: 10px 12px;
1038
+ border-radius: 8px;
1039
+ background: var(--ink);
1040
+ color: #e8e6e1;
1041
+ font-size: 11.5px;
1042
+ line-height: 1.5;
1043
+ white-space: pre-wrap;
1044
+ word-break: break-all;
1045
+ }
1046
+ .ez-devserver-log:empty::before {
1047
+ content: "\7b49\5f85\8f38\51fa\2026";
1048
+ color: var(--ink-faint);
1049
+ }
1050
+ .ez-devserver-missing {
1051
+ display: flex;
1052
+ flex-direction: column;
1053
+ gap: 8px;
1054
+ margin: 14px 0 0;
1055
+ padding: 0;
1056
+ list-style: none;
1057
+ }
1058
+ .ez-devserver-missing li {
1059
+ display: flex;
1060
+ align-items: baseline;
1061
+ gap: 10px;
1062
+ }
1063
+ .ez-devserver-tool {
1064
+ flex-shrink: 0;
1065
+ padding: 1px 6px;
1066
+ border-radius: 5px;
1067
+ background: var(--muted);
1068
+ font-size: 12.5px;
1069
+ font-weight: 600;
1070
+ }
1071
+ .ez-devserver-inline {
1072
+ padding: 0 4px;
1073
+ border-radius: 4px;
1074
+ background: var(--muted);
1075
+ font-family:
1076
+ ui-monospace,
1077
+ SFMono-Regular,
1078
+ Menlo,
1079
+ monospace;
1080
+ font-size: 12px;
1081
+ }
1082
+ .ez-devserver-form {
1083
+ display: flex;
1084
+ flex-direction: column;
1085
+ gap: 12px;
1086
+ margin-top: 14px;
1087
+ }
1088
+ .ez-devserver-label {
1089
+ display: flex;
1090
+ flex-direction: column;
1091
+ gap: 5px;
1092
+ font-size: 12.5px;
1093
+ font-weight: 500;
1094
+ color: var(--muted-foreground);
1095
+ }
1096
+ .ez-devserver-input {
1097
+ height: 34px;
1098
+ padding: 0 10px;
1099
+ border: 1px solid var(--border);
1100
+ border-radius: 7px;
1101
+ background: var(--panel);
1102
+ color: var(--foreground);
1103
+ font-size: 13px;
1104
+ }
1105
+ .ez-devserver-input:focus-visible {
1106
+ outline: none;
1107
+ border-color: var(--ring);
1108
+ box-shadow: 0 0 0 3px var(--ring-halo);
1109
+ }
1110
+ .ez-devserver-actions {
1111
+ display: flex;
1112
+ flex-wrap: wrap;
1113
+ align-items: center;
1114
+ gap: 8px;
1115
+ margin-top: 18px;
1116
+ }
1117
+ .ez-devserver-form .ez-devserver-actions {
1118
+ margin-top: 4px;
1119
+ }
1120
+ .ez-devserver-go,
1121
+ .ez-devserver-secondary {
1122
+ display: inline-flex;
1123
+ align-items: center;
1124
+ gap: 6px;
1125
+ height: 32px;
1126
+ padding: 0 14px;
1127
+ border-radius: 7px;
1128
+ font-family: inherit;
1129
+ font-size: 13px;
1130
+ font-weight: 600;
1131
+ cursor: pointer;
1132
+ }
1133
+ .ez-devserver-go {
1134
+ border: 1px solid var(--primary);
1135
+ background: var(--primary);
1136
+ color: var(--primary-foreground);
1137
+ box-shadow: inset 0 1px 0 0 oklch(100% 0 0 / 0.14);
1138
+ }
1139
+ .ez-devserver-secondary {
1140
+ border: 1px solid var(--border);
1141
+ background: var(--panel);
1142
+ color: var(--foreground);
1143
+ }
1144
+ .ez-devserver-go:hover:not(:disabled) {
1145
+ opacity: 0.88;
1146
+ }
1147
+ .ez-devserver-secondary:hover:not(:disabled) {
1148
+ background: var(--muted);
1149
+ }
1150
+ .ez-devserver-go:disabled,
1151
+ .ez-devserver-secondary:disabled {
1152
+ opacity: 0.4;
1153
+ cursor: default;
1154
+ }
1155
+ .ez-devserver-reason {
1156
+ font-size: 12.5px;
1157
+ color: var(--foreground);
1158
+ }
1159
+ .ez-devserver-asking {
1160
+ display: flex;
1161
+ align-items: center;
1162
+ gap: 8px;
1163
+ margin: 10px 0 0;
1164
+ font-size: 12.5px;
1165
+ color: var(--muted-foreground);
1166
+ animation: ez-devserver-pulse 1.6s ease-in-out infinite;
1167
+ }
1168
+ .ez-devserver-asking::before {
1169
+ content: "";
1170
+ flex-shrink: 0;
1171
+ width: 12px;
1172
+ height: 12px;
1173
+ border: 1.5px solid currentColor;
1174
+ border-right-color: transparent;
1175
+ border-radius: 50%;
1176
+ animation: ez-devserver-spin 0.8s linear infinite;
1177
+ }
1178
+ @keyframes ez-devserver-spin {
1179
+ to {
1180
+ transform: rotate(1turn);
1181
+ }
1182
+ }
1183
+ @keyframes ez-devserver-pulse {
1184
+ 50% {
1185
+ opacity: 0.45;
1186
+ }
1187
+ }
1188
+ .ez-devserver-error {
1189
+ margin: 10px 0 0;
1190
+ font-size: 12.5px;
1191
+ color: oklch(45% 0.15 25);
1192
+ }
966
1193
  .ez-section {
967
1194
  padding: 14px;
968
1195
  padding-block: 8px;
@@ -1289,6 +1516,15 @@ body.ez-arranging .ez-frame {
1289
1516
  line-height: 1.55;
1290
1517
  word-break: break-word;
1291
1518
  }
1519
+ .ez-qi-video {
1520
+ margin-top: 4px;
1521
+ font-size: 12px;
1522
+ color: var(--ink-faint);
1523
+ font-variant-numeric: tabular-nums;
1524
+ }
1525
+ .ez-qi-video-failed {
1526
+ color: var(--code-ink);
1527
+ }
1292
1528
  .ez-queue-item[data-editing] .ez-attach {
1293
1529
  margin: 6px 0 0;
1294
1530
  }
@@ -1384,6 +1620,9 @@ body.ez-arranging .ez-frame {
1384
1620
  .ez-agent-pill {
1385
1621
  --brand-nudge: 1px;
1386
1622
  }
1623
+ .ez-agent-pill[data-offline] {
1624
+ color: var(--foreground);
1625
+ }
1387
1626
  .ez-agent-pill > svg,
1388
1627
  .ez-config-pill > svg {
1389
1628
  flex-shrink: 0;
@@ -1788,6 +2027,11 @@ body.ez-arranging .ez-frame {
1788
2027
  .ez-chip-pending {
1789
2028
  opacity: 0.55;
1790
2029
  }
2030
+ .ez-chip-note {
2031
+ margin-left: 4px;
2032
+ font-variant-numeric: tabular-nums;
2033
+ opacity: 0.8;
2034
+ }
1791
2035
  .ez-file-chips {
1792
2036
  display: flex;
1793
2037
  flex-wrap: wrap;
@@ -2796,7 +3040,6 @@ body.ez-arranging .ez-frame {
2796
3040
  .ez-tabs-bar {
2797
3041
  display: flex;
2798
3042
  align-items: center;
2799
- gap: 8px;
2800
3043
  height: 42px;
2801
3044
  flex-shrink: 0;
2802
3045
  padding: 0 10px 0 0;
@@ -2914,6 +3157,7 @@ body.ez-arranging .ez-frame {
2914
3157
  display: flex;
2915
3158
  align-items: center;
2916
3159
  flex-shrink: 0;
3160
+ margin-left: 8px;
2917
3161
  }
2918
3162
  .ez-tab-new,
2919
3163
  .ez-canvas-menu {
@@ -3072,6 +3316,10 @@ body.ez-arranging .ez-frame {
3072
3316
  .ez-board[hidden] {
3073
3317
  display: none;
3074
3318
  }
3319
+ html.ez-on-board,
3320
+ html.ez-on-board body {
3321
+ overscroll-behavior-x: none;
3322
+ }
3075
3323
  .ez-board.ez-space {
3076
3324
  cursor: grab;
3077
3325
  }
@@ -3097,6 +3345,17 @@ body.ez-arranging .ez-frame {
3097
3345
  .ez-board-frame[aria-selected=true] {
3098
3346
  outline-color: var(--arrange);
3099
3347
  }
3348
+ .ez-board-marquee {
3349
+ position: absolute;
3350
+ left: 0;
3351
+ top: 0;
3352
+ pointer-events: none;
3353
+ border: 1px solid var(--arrange);
3354
+ background: color-mix(in oklab, var(--arrange) 8%, transparent);
3355
+ }
3356
+ .ez-board-marquee[hidden] {
3357
+ display: none;
3358
+ }
3100
3359
  .ez-board-heads {
3101
3360
  position: absolute;
3102
3361
  left: 0;
@@ -3777,6 +4036,7 @@ body.ez-arranging .ez-frame {
3777
4036
  }
3778
4037
  .ez-library-detail-stage {
3779
4038
  position: relative;
4039
+ container-type: size;
3780
4040
  display: flex;
3781
4041
  align-items: center;
3782
4042
  justify-content: center;
@@ -4061,3 +4321,39 @@ body.ez-arranging .ez-frame {
4061
4321
  animation-name: ez-fade-in;
4062
4322
  }
4063
4323
  }
4324
+ [data-host=desktop] .ez-tabs-bar {
4325
+ padding-left: var(--traffic-light-room);
4326
+ -webkit-app-region: drag;
4327
+ }
4328
+ [data-host=desktop][data-sidebar=open] .ez-tab:first-child {
4329
+ margin-left: -1px;
4330
+ }
4331
+ [data-host=desktop] .ez-account {
4332
+ display: none;
4333
+ }
4334
+ [data-host=desktop] .ez-tabs-bar button,
4335
+ [data-host=desktop] .ez-tabs-bar [role=tab],
4336
+ [data-host=desktop] .ez-tabs-bar .ez-menu {
4337
+ -webkit-app-region: no-drag;
4338
+ }
4339
+ .ez-sidebar-toggle {
4340
+ display: none;
4341
+ align-items: center;
4342
+ justify-content: center;
4343
+ flex-shrink: 0;
4344
+ width: 28px;
4345
+ height: 28px;
4346
+ margin: 0 6px 0 -4px;
4347
+ border: none;
4348
+ border-radius: 7px;
4349
+ background: none;
4350
+ color: var(--muted-foreground);
4351
+ cursor: pointer;
4352
+ }
4353
+ .ez-sidebar-toggle:hover {
4354
+ background: var(--muted);
4355
+ color: var(--foreground);
4356
+ }
4357
+ [data-sidebar=closed] .ez-sidebar-toggle {
4358
+ display: inline-flex;
4359
+ }