pycodex-ws 0.4.2__tar.gz → 0.4.4__tar.gz

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 (42) hide show
  1. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/PKG-INFO +2 -2
  2. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_router/client.py +1 -0
  3. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_router/index.html +20 -6
  4. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_router/server.py +2 -0
  5. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pyproject.toml +2 -2
  6. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/app.py +35 -0
  7. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/content_views.mjs +7 -7
  8. pycodex_ws-0.4.4/workspace_server/editor.css +1 -0
  9. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/editor.mjs +195 -198
  10. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/content_views.mjs +1 -4
  11. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/editor.css +18 -0
  12. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/editor.mjs +32 -1
  13. pycodex_ws-0.4.4/workspace_server/frontend/touch_selection.mjs +183 -0
  14. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/workspace.html +88 -9
  15. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/workspaces.html +87 -20
  16. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/workspaces.py +16 -0
  17. pycodex_ws-0.4.2/workspace_server/editor.css +0 -1
  18. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/.gitignore +0 -0
  19. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/LICENSE +0 -0
  20. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/README.md +0 -0
  21. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_router/__init__.py +0 -0
  22. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_router/__main__.py +0 -0
  23. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_router/browser.py +0 -0
  24. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_router/protocol.py +0 -0
  25. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_ws/__init__.py +0 -0
  26. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_ws/feishu_card.py +0 -0
  27. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_ws/feishu_link.py +0 -0
  28. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/pycodex_ws/feishu_oauth.py +0 -0
  29. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/__init__.py +0 -0
  30. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/__main__.py +0 -0
  31. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/content_views.LICENSE.txt +0 -0
  32. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/content_views.css +0 -0
  33. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/.gitignore +0 -0
  34. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/build.mjs +0 -0
  35. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/composer.mjs +0 -0
  36. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/content_views.css +0 -0
  37. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/editor_context.mjs +0 -0
  38. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/package-lock.json +0 -0
  39. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/package.json +0 -0
  40. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/frontend/view_labels.mjs +0 -0
  41. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/owned_process.py +0 -0
  42. {pycodex_ws-0.4.2 → pycodex_ws-0.4.4}/workspace_server/workspace_views.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pycodex-ws
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Browser workspace and optional Feishu integration for python-codex
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.7
7
7
  Requires-Dist: pillow>=9.0.0
8
- Requires-Dist: python-codex==0.4.2
8
+ Requires-Dist: python-codex==0.4.4
9
9
  Requires-Dist: websockets<16,>=11
10
10
  Description-Content-Type: text/markdown
11
11
 
@@ -235,6 +235,7 @@ def attach_router(app, registry, target, port):
235
235
  "enable_board": workspace["enable_board"],
236
236
  "session_count": len(workspace["sessions"]),
237
237
  "running": any(session["running"] for session in workspace["sessions"]),
238
+ "unread": any(session["unread"] for session in workspace["sessions"]),
238
239
  }
239
240
  for workspace in registry.list_workspaces()
240
241
  ]
@@ -73,21 +73,31 @@
73
73
  .workspace-group-heading { display: flex; align-items: center; gap: 10px; padding: 12px 14px; }
74
74
  .workspace-group-name { flex: 1; min-width: 0; font-size: 12px; font-weight: 600; line-height: 1.6; overflow-wrap: anywhere; }
75
75
  .workspace-group-heading .count { flex: 0 0 auto; }
76
- .list-head, .workspace-row { display: grid; grid-template-columns: minmax(94px, .85fr) minmax(120px, 1.85fr) 60px 44px; gap: 12px; align-items: center; padding: 10px 14px; }
76
+ .list-head, .workspace-row { display: grid; grid-template-columns: minmax(94px, .85fr) minmax(120px, 1.85fr) 76px 44px; gap: 12px; align-items: center; padding: 10px 14px; }
77
77
  .list-head { position: sticky; top: 0; z-index: 1; background: var(--soft); color: var(--muted); font-size: 10px; font-weight: 550; letter-spacing: .3px; }
78
- .workspace-row { min-height: 62px; border-top: 1px solid var(--line); }
78
+ .workspace-row { position: relative; min-height: 62px; border-top: 1px solid var(--line); }
79
79
  .workspace-row > * { min-width: 0; }
80
80
  .workspace-row:hover { background: var(--soft); }
81
81
  .workspace-row:last-child { border-radius: 0 0 9px 9px; }
82
- .activity-badge { justify-self: start; display: inline-flex; align-items: center; gap: 5px; padding: 3px 6px; border-radius: 4px; color: var(--muted); background: var(--soft); font-size: 10px; font-weight: 500; }
82
+ .activity-badge { display: inline-flex; align-items: center; gap: 5px; padding: 3px 6px; border-radius: 4px; color: var(--muted); background: var(--soft); font-size: 10px; font-weight: 500; }
83
83
  .activity-badge[data-state="working"] { color: var(--green); background: var(--green-soft); }
84
- .activity-badge[data-state="working"]::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
84
+ .activity-badge[data-state="working"]::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; animation: spinner-pulse 1.2s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
85
+ @keyframes spinner-pulse {
86
+ 0%, 100% { opacity: .45; transform: scale(.82); }
87
+ 50% { opacity: 1; transform: scale(1.08); }
88
+ }
89
+ @media (prefers-reduced-motion: reduce) {
90
+ .activity-badge[data-state="working"]::before { animation: none; opacity: .8; transform: none; }
91
+ }
92
+ .workspace-status { display: inline-flex; align-items: center; gap: 6px; justify-self: start; }
93
+ .unread-dot { flex: 0 0 6px; width: 6px; height: 6px; border-radius: 50%; background: var(--danger); }
85
94
  .workspace-title { font-size: 12px; line-height: 1.6; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
86
95
  .workspace-path, .state-path { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; line-height: 1.8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
87
96
  .state-path { color: var(--muted); }
88
97
  .session-count { color: var(--muted); font-size: 11px; line-height: 1.7; }
89
98
  .open-link, .manage-link { display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px; min-height: 28px; color: var(--accent); font-size: 11px; font-weight: 550; white-space: nowrap; }
90
99
  .open-link svg, .manage-link svg { width: 13px; height: 13px; }
100
+ .open-link::after { content: ""; position: absolute; inset: 0; }
91
101
  .open-link:hover, .manage-link:hover { text-decoration: underline; text-underline-offset: 4px; }
92
102
  .device { min-width: 0; padding: 12px 14px; }
93
103
  .device + .device { border-top: 1px solid var(--line); }
@@ -117,7 +127,7 @@
117
127
  .list-head { display: none; }
118
128
  .workspace-row { grid-template-columns: minmax(0, 1fr) auto auto; gap: 6px 10px; padding: 10px 12px; }
119
129
  .workspace-paths { grid-column: 1 / -1; grid-row: 2; }
120
- .activity-badge { grid-column: 2; grid-row: 1; }
130
+ .workspace-status { grid-column: 2; grid-row: 1; }
121
131
  .open-link { grid-column: 3; grid-row: 1; min-height: 36px; }
122
132
  }
123
133
  </style>
@@ -191,7 +201,10 @@
191
201
  <p class="workspace-path"></p>
192
202
  <p class="state-path"></p>
193
203
  </div>
194
- <span class="activity-badge"></span>
204
+ <span class="workspace-status">
205
+ <span class="unread-dot" role="img" aria-label="Unread reply" title="Unread reply"></span>
206
+ <span class="activity-badge"></span>
207
+ </span>
195
208
  <a class="open-link">Open <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14m-6-6 6 6-6 6"/></svg></a>
196
209
  </article>
197
210
  </template>
@@ -346,6 +359,7 @@
346
359
  const title = card.querySelector(".workspace-title");
347
360
  title.textContent = workspace.id;
348
361
  title.title = workspace.id;
362
+ card.querySelector(".unread-dot").hidden = !workspace.unread;
349
363
  const path = card.querySelector(".workspace-path");
350
364
  path.textContent = workspace.work_dir;
351
365
  path.title = workspace.work_dir;
@@ -114,6 +114,7 @@ def validate_inventory(workspaces):
114
114
  "enable_board",
115
115
  "session_count",
116
116
  "running",
117
+ "unread",
117
118
  }
118
119
  seen = set()
119
120
  for workspace in workspaces:
@@ -136,6 +137,7 @@ def validate_inventory(workspaces):
136
137
  if (
137
138
  type(workspace["enable_board"]) is not bool
138
139
  or type(workspace["running"]) is not bool
140
+ or type(workspace["unread"]) is not bool
139
141
  or type(workspace["session_count"]) is not int
140
142
  or not 0 <= workspace["session_count"] <= 1000000
141
143
  ):
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pycodex-ws"
7
- version = "0.4.2"
7
+ version = "0.4.4"
8
8
  description = "Browser workspace and optional Feishu integration for python-codex"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"
11
11
  dependencies = [
12
- "python-codex==0.4.2",
12
+ "python-codex==0.4.4",
13
13
  "pillow>=9.0.0",
14
14
  "websockets>=11,<16",
15
15
  ]
@@ -198,6 +198,7 @@ class WebSessionView:
198
198
  self._turns_by_submission_id: "typing.Dict[str, typing.Dict[str, object]]" = {}
199
199
  self._turns_by_turn_id: "typing.Dict[str, typing.Dict[str, object]]" = {}
200
200
  self._title = ""
201
+ self._last_final_id = ""
201
202
  self._model = "pycodex"
202
203
  self._rollout_path = ""
203
204
  self._recorded_rollout_path = ""
@@ -276,6 +277,7 @@ class WebSessionView:
276
277
  self._turns_by_submission_id = {}
277
278
  self._turns_by_turn_id = {}
278
279
  self._events = []
280
+ self._last_final_id = ""
279
281
  for entry in history:
280
282
  if entry is None:
281
283
  self._add_compaction_marker()
@@ -318,6 +320,7 @@ class WebSessionView:
318
320
  with self._lock:
319
321
  return {
320
322
  "running": bool(self._spinner_status),
323
+ "last_final_id": self._last_final_id,
321
324
  "status": self._spinner_status,
322
325
  "status_kind": "spinner" if self._spinner_status else "idle",
323
326
  "spinner": self._spinner_status,
@@ -341,6 +344,7 @@ class WebSessionView:
341
344
  return {
342
345
  "model": self._model,
343
346
  "running": bool(self._spinner_status),
347
+ "last_final_id": self._last_final_id,
344
348
  "spinner": self._spinner_status,
345
349
  "title": self._title,
346
350
  "turn_count": len(self._turns),
@@ -462,6 +466,8 @@ class WebSessionView:
462
466
  response = event.visualize()
463
467
  if response:
464
468
  turn["response"] = response
469
+ turn["final_id"] = event.turn_id
470
+ self._last_final_id = event.turn_id
465
471
  elif turn.get("thinking"):
466
472
  turn["response"] = str(turn.get("thinking") or "")
467
473
  turn["thinking"] = ""
@@ -979,6 +985,13 @@ def create_multi_workspace_app(
979
985
  entry.manager, request.path_params["session_id"]
980
986
  )
981
987
 
988
+ async def workspace_read_session(request: Request) -> JSONResponse:
989
+ payload = await _json_object_body(request)
990
+ entry = _workspace_entry_or_404(registry, request.path_params["workspace_id"])
991
+ return _read_session_response(
992
+ entry.manager, request.path_params["session_id"], payload
993
+ )
994
+
982
995
  async def workspace_session(request: Request) -> JSONResponse:
983
996
  entry = _workspace_entry_or_404(registry, request.path_params["workspace_id"])
984
997
  return _session_response(entry.manager, request.query_params.get("session_id"))
@@ -1054,6 +1067,10 @@ def create_multi_workspace_app(
1054
1067
  "/w/{workspace_id}/api/sessions/{session_id}",
1055
1068
  workspace_delete_session, methods=["DELETE"],
1056
1069
  ),
1070
+ Route(
1071
+ "/w/{workspace_id}/api/sessions/{session_id}/read",
1072
+ workspace_read_session, methods=["POST"],
1073
+ ),
1057
1074
  Route("/w/{workspace_id}/api/session", workspace_session, methods=["GET"]),
1058
1075
  Route(
1059
1076
  "/w/{workspace_id}/api/session/message",
@@ -1348,6 +1365,10 @@ def _install_workspace_routes(
1348
1365
  async def delete_session(request: Request) -> JSONResponse:
1349
1366
  return await _delete_session_response(manager, request.path_params["session_id"])
1350
1367
 
1368
+ async def read_session(request: Request) -> JSONResponse:
1369
+ payload = await _json_object_body(request)
1370
+ return _read_session_response(manager, request.path_params["session_id"], payload)
1371
+
1351
1372
  async def session(request: Request) -> JSONResponse:
1352
1373
  return _session_response(manager, request.query_params.get("session_id"))
1353
1374
 
@@ -1385,6 +1406,7 @@ def _install_workspace_routes(
1385
1406
  Route("/api/sessions", sessions, methods=["GET"]),
1386
1407
  Route("/api/sessions", new_session, methods=["POST"]),
1387
1408
  Route("/api/sessions/{session_id}", delete_session, methods=["DELETE"]),
1409
+ Route("/api/sessions/{session_id}/read", read_session, methods=["POST"]),
1388
1410
  Route("/api/session", session, methods=["GET"]),
1389
1411
  Route("/api/session/message", message, methods=["POST"]),
1390
1412
  WebSocketRoute("/ws/session", websocket_session),
@@ -1534,6 +1556,18 @@ def _sessions_response(manager: "WorkspaceSessionManager") -> JSONResponse:
1534
1556
  return JSONResponse({"sessions": manager.list_sessions()})
1535
1557
 
1536
1558
 
1559
+ def _read_session_response(
1560
+ manager: "WorkspaceSessionManager", session_id: str, payload: "typing.Dict[str, object]"
1561
+ ) -> JSONResponse:
1562
+ try:
1563
+ manager.mark_read(session_id, payload.get("final_id"))
1564
+ except KeyError:
1565
+ raise HTTPException(status_code=404, detail="session not found")
1566
+ except ValueError as exc:
1567
+ raise HTTPException(status_code=400, detail=str(exc))
1568
+ return _sessions_response(manager)
1569
+
1570
+
1537
1571
  async def _new_session_response(manager: "WorkspaceSessionManager") -> JSONResponse:
1538
1572
  session_id = await manager.create_session()
1539
1573
  return JSONResponse(
@@ -1670,6 +1704,7 @@ def _public_turn(turn: "typing.Dict[str, object]") -> "typing.Dict[str, object]"
1670
1704
  {
1671
1705
  "submission_id": turn.get("submission_id", ""),
1672
1706
  "turn_id": turn.get("turn_id", ""),
1707
+ "final_id": turn.get("final_id", ""),
1673
1708
  "prompt": turn.get("prompt", ""),
1674
1709
  "response": turn.get("response", ""),
1675
1710
  "thinking": turn.get("thinking", ""),
@@ -1,11 +1,11 @@
1
1
  // Generated by workspace_server/frontend/build.mjs. See content_views.LICENSE.txt.
2
- function $(c){function t(n){let o=n.lastIndexOf("/");return o<0?".":n.slice(0,o)||"/"}let e=new Map,i=c.map(n=>{let a=(n.kind==="diff"?n.title.split(" <-> "):[n.title]).map(u=>{let h=/^(?:([0-9a-f]{40}|[0-9a-f]{64})|.+ \(([0-9a-f]{40}|[0-9a-f]{64})\)):(.+)$/i.exec(u),d=h?h[3]:u,p=d.slice(d.lastIndexOf("/")+1);return e.has(p)||e.set(p,new Set),e.get(p).add(d),{path:d,name:p,revision:h?(h[1]||h[2]).slice(0,7):null}});return{view:n,sources:a}}),s=new Map;for(let n of e.values()){if(n.size<2)continue;let o=Array.from(n),a=o.map(u=>t(u).split("/"));o.forEach((u,h)=>{let d=1;for(;d<a[h].length&&a.some((p,x)=>x!==h&&p.slice(-d).join("/")===a[h].slice(-d).join("/"));)d+=1;s.set(u,a[h].slice(-d).join("/")||"/")})}return new Map(i.map(({view:n,sources:o})=>{let a=o.every(p=>p.path===o[0].path),u=a?o[0].name:o.map(p=>p.name).join(" \u2194 "),h="";a?h=s.get(o[0].path)||"":o.some(p=>s.has(p.path))&&(h=o.map(p=>s.get(p.path)||t(p.path)).join(" \u2194 "));let d=o.some(p=>p.revision)?o.map(p=>p.revision||"working tree").join(" \u2194 "):n.kind==="diff"&&a?"diff":"";return[n.id,{name:u,description:[h,d].filter(Boolean).join(" \xB7 ")}]}))}var N=class c{constructor(t,e,i,s,n){this.element=t,this.changed=e,this.submit=i,this.editContext=s,this.error=n,this.composing=!1,this.selection={start:0,end:0},this.undo=[],this.redo=[],this.previous=this.read(),t.addEventListener("input",o=>{o.isComposing||this.record(),this.changed()}),t.addEventListener("compositionstart",()=>{this.composing=!0}),t.addEventListener("compositionend",()=>{this.composing=!1,this.record(),this.changed()}),document.addEventListener("selectionchange",()=>{this.captureSelection()&&this.changed()}),t.addEventListener("keydown",o=>this.keydown(o)),t.addEventListener("beforeinput",o=>{o.inputType==="historyUndo"||o.inputType==="historyRedo"?(o.preventDefault(),this.history(o.inputType==="historyUndo")):o.inputType.startsWith("format")&&o.preventDefault()}),t.addEventListener("click",o=>{let a=o.target.closest(".context-token");a&&this.editContext(JSON.parse(a.dataset.context))}),t.addEventListener("mousedown",o=>{o.target.closest(".context-token")&&o.preventDefault()}),t.addEventListener("copy",o=>this.copy(o,!1)),t.addEventListener("cut",o=>this.copy(o,!0)),t.addEventListener("paste",o=>{o.preventDefault(),this.paste(o.clipboardData)}),t.addEventListener("dragover",o=>o.preventDefault()),t.addEventListener("drop",o=>{o.preventDefault();let a=document.caretRangeFromPoint(o.clientX,o.clientY);if(a&&t.contains(a.startContainer)){let u=window.getSelection();u.removeAllRanges(),u.addRange(a),this.captureSelection()}this.paste(o.dataTransfer)})}static size(t){return t.reduce((e,i)=>e+(i.type==="context"?1:i.text.length),0)}static text(t){return t.map(e=>e.text).join("")}static slice(t,e,i){let s=[],n=0;for(let o of t){let a=c.size([o]);e<n+a&&i>n&&s.push(o.type==="context"?{...o}:{type:"text",text:o.text.slice(Math.max(0,e-n),i-n)}),n+=a}return s}parts(t=this.element){let e=[],i=n=>{if(!n)return;let o=e.at(-1);(o==null?void 0:o.type)==="text"?o.text+=n:e.push({type:"text",text:n})},s=n=>{var o;for(let a of n.childNodes)a.nodeType===Node.TEXT_NODE?i(a.data):(o=a.dataset)!=null&&o.context?e.push(JSON.parse(a.dataset.context)):a.nodeName==="BR"?!a.hasAttribute("data-tail")&&n.childNodes.length>1&&i(`
3
- `):(["DIV","P"].includes(a.nodeName)&&e.length&&!e.at(-1).text.endsWith(`
2
+ function U(c){function t(n){let o=n.lastIndexOf("/");return o<0?".":n.slice(0,o)||"/"}let e=new Map,i=c.map(n=>{let r=(n.kind==="diff"?n.title.split(" <-> "):[n.title]).map(d=>{let h=/^(?:([0-9a-f]{40}|[0-9a-f]{64})|.+ \(([0-9a-f]{40}|[0-9a-f]{64})\)):(.+)$/i.exec(d),u=h?h[3]:d,p=u.slice(u.lastIndexOf("/")+1);return e.has(p)||e.set(p,new Set),e.get(p).add(u),{path:u,name:p,revision:h?(h[1]||h[2]).slice(0,7):null}});return{view:n,sources:r}}),s=new Map;for(let n of e.values()){if(n.size<2)continue;let o=Array.from(n),r=o.map(d=>t(d).split("/"));o.forEach((d,h)=>{let u=1;for(;u<r[h].length&&r.some((p,k)=>k!==h&&p.slice(-u).join("/")===r[h].slice(-u).join("/"));)u+=1;s.set(d,r[h].slice(-u).join("/")||"/")})}return new Map(i.map(({view:n,sources:o})=>{let r=o.every(p=>p.path===o[0].path),d=r?o[0].name:o.map(p=>p.name).join(" \u2194 "),h="";r?h=s.get(o[0].path)||"":o.some(p=>s.has(p.path))&&(h=o.map(p=>s.get(p.path)||t(p.path)).join(" \u2194 "));let u=o.some(p=>p.revision)?o.map(p=>p.revision||"working tree").join(" \u2194 "):n.kind==="diff"&&r?"diff":"";return[n.id,{name:d,description:[h,u].filter(Boolean).join(" \xB7 ")}]}))}var M=class c{constructor(t,e,i,s,n){this.element=t,this.changed=e,this.submit=i,this.editContext=s,this.error=n,this.composing=!1,this.selection={start:0,end:0},this.undo=[],this.redo=[],this.previous=this.read(),t.addEventListener("input",o=>{o.isComposing||this.record(),this.changed()}),t.addEventListener("compositionstart",()=>{this.composing=!0}),t.addEventListener("compositionend",()=>{this.composing=!1,this.record(),this.changed()}),document.addEventListener("selectionchange",()=>{this.captureSelection()&&this.changed()}),t.addEventListener("keydown",o=>this.keydown(o)),t.addEventListener("beforeinput",o=>{o.inputType==="historyUndo"||o.inputType==="historyRedo"?(o.preventDefault(),this.history(o.inputType==="historyUndo")):o.inputType.startsWith("format")&&o.preventDefault()}),t.addEventListener("click",o=>{let r=o.target.closest(".context-token");r&&this.editContext(JSON.parse(r.dataset.context))}),t.addEventListener("mousedown",o=>{o.target.closest(".context-token")&&o.preventDefault()}),t.addEventListener("copy",o=>this.copy(o,!1)),t.addEventListener("cut",o=>this.copy(o,!0)),t.addEventListener("paste",o=>{o.preventDefault(),this.paste(o.clipboardData)}),t.addEventListener("dragover",o=>o.preventDefault()),t.addEventListener("drop",o=>{o.preventDefault();let r=document.caretRangeFromPoint(o.clientX,o.clientY);if(r&&t.contains(r.startContainer)){let d=window.getSelection();d.removeAllRanges(),d.addRange(r),this.captureSelection()}this.paste(o.dataTransfer)})}static size(t){return t.reduce((e,i)=>e+(i.type==="context"?1:i.text.length),0)}static text(t){return t.map(e=>e.text).join("")}static slice(t,e,i){let s=[],n=0;for(let o of t){let r=c.size([o]);e<n+r&&i>n&&s.push(o.type==="context"?{...o}:{type:"text",text:o.text.slice(Math.max(0,e-n),i-n)}),n+=r}return s}parts(t=this.element){let e=[],i=n=>{if(!n)return;let o=e.at(-1);(o==null?void 0:o.type)==="text"?o.text+=n:e.push({type:"text",text:n})},s=n=>{var o;for(let r of n.childNodes)r.nodeType===Node.TEXT_NODE?i(r.data):(o=r.dataset)!=null&&o.context?e.push(JSON.parse(r.dataset.context)):r.nodeName==="BR"?!r.hasAttribute("data-tail")&&n.childNodes.length>1&&i(`
3
+ `):(["DIV","P"].includes(r.nodeName)&&e.length&&!e.at(-1).text.endsWith(`
4
4
  `)&&i(`
5
- `),s(a))};return s(t),e}captureSelection(){if(document.activeElement!==this.element)return!1;let t=window.getSelection();if(!t.rangeCount)return!1;let e=t.getRangeAt(0);if(!this.element.contains(e.startContainer)||!this.element.contains(e.endContainer))return!1;let i=document.createRange();i.selectNodeContents(this.element),i.setEnd(e.startContainer,e.startOffset);let s=c.size(this.parts(i.cloneContents()));return this.selection={start:s,end:s+c.size(this.parts(e.cloneContents()))},!0}read(){return this.captureSelection(),{parts:this.parts(),selection:{...this.selection}}}set(t){this.undo=[],this.redo=[],this.render(t),document.activeElement===this.element&&this.focus(),this.previous=this.read()}render(t){let e=t.parts||[];this.element.replaceChildren();for(let s of e)if(s.type==="text")this.element.append(document.createTextNode(s.text));else{let n=document.createElement("span");n.className="context-token",n.contentEditable="false",n.dataset.context=JSON.stringify(s),n.textContent=s.label,n.title=s.label+`
5
+ `),s(r))};return s(t),e}captureSelection(){if(document.activeElement!==this.element)return!1;let t=window.getSelection();if(!t.rangeCount)return!1;let e=t.getRangeAt(0);if(!this.element.contains(e.startContainer)||!this.element.contains(e.endContainer))return!1;let i=document.createRange();i.selectNodeContents(this.element),i.setEnd(e.startContainer,e.startOffset);let s=c.size(this.parts(i.cloneContents()));return this.selection={start:s,end:s+c.size(this.parts(e.cloneContents()))},!0}read(){return this.captureSelection(),{parts:this.parts(),selection:{...this.selection}}}set(t){this.undo=[],this.redo=[],this.render(t),document.activeElement===this.element&&this.focus(),this.previous=this.read()}render(t){let e=t.parts||[];this.element.replaceChildren();for(let s of e)if(s.type==="text")this.element.append(document.createTextNode(s.text));else{let n=document.createElement("span");n.className="context-token",n.contentEditable="false",n.dataset.context=JSON.stringify(s),n.textContent=s.label,n.title=s.label+`
6
6
  Click to edit context; Backspace / Delete to remove`,this.element.append(n,document.createTextNode(""))}if(!e.length||e.at(-1).type==="text"&&e.at(-1).text.endsWith(`
7
- `)){let s=document.createElement("br");s.dataset.tail="",this.element.append(s)}let i=c.size(e);this.selection=t.selection||{start:i,end:i},this.selection={start:Math.min(this.selection.start,i),end:Math.min(this.selection.end,i)},this.element.classList.toggle("empty",!e.length)}focus(){this.element.focus({preventScroll:!0});let t=s=>{let n=o=>{var a;for(let u=0;u<o.childNodes.length;u++){let h=o.childNodes[u];if(h.nodeType===Node.TEXT_NODE){if(s<=h.length)return[h,s];s-=h.length}else if((a=h.dataset)!=null&&a.context){if(s===0)return[o,u];if(s-=1,s===0)return[o,u+1]}else if(h.nodeName==="BR"){if(s===0)return[o,u];h.hasAttribute("data-tail")||(s-=1)}else{let d=n(h);if(d)return d}}};return n(this.element)||[this.element,this.element.childNodes.length]},e=document.createRange();e.setStart(...t(this.selection.start)),e.setEnd(...t(this.selection.end));let i=window.getSelection();i.removeAllRanges(),i.addRange(e)}record(){let t=this.read();JSON.stringify(t.parts)!==JSON.stringify(this.previous.parts)&&(this.undo.push(this.previous),this.undo.length>40&&this.undo.shift(),this.redo=[],this.previous=t),this.element.classList.toggle("empty",!t.parts.length)}replace(t,e,i){let s=this.read(),n=[...c.slice(s.parts,0,t),...i,...c.slice(s.parts,e,c.size(s.parts))],o=t+c.size(i);this.render({parts:n,selection:{start:o,end:o}}),this.focus(),this.record(),this.changed()}insert(t){this.captureSelection(),this.replace(this.selection.start,this.selection.end,t.map(e=>({...e,type:"context",id:crypto.getRandomValues(new Uint32Array(4)).join("-")})))}updateContext(t,e){for(let i of this.element.querySelectorAll(".context-token")){let s=JSON.parse(i.dataset.context);s.id===t&&(i.dataset.context=JSON.stringify({...s,text:e}))}this.record(),this.changed()}removeContext(t){let e=0;for(let i of this.parts()){if(i.type==="context"&&i.id===t){this.replace(e,e+1,[]);return}e+=c.size([i])}}history(t){let e=t?this.undo:this.redo;e.length&&((t?this.redo:this.undo).push(this.read()),this.render(e.pop()),this.focus(),this.previous=this.read(),this.changed())}keydown(t){var i;if(t.isComposing||this.composing||t.keyCode===229)return;let e=t.ctrlKey||t.metaKey;if(e&&(t.key.toLowerCase()==="z"||t.key.toLowerCase()==="y"))t.preventDefault(),this.history(t.key.toLowerCase()==="z"&&!t.shiftKey);else if(t.key==="Enter")t.preventDefault(),t.shiftKey?(this.captureSelection(),this.replace(this.selection.start,this.selection.end,[{type:"text",text:`
8
- `}])):this.submit();else if(["Backspace","Delete"].includes(t.key)&&!e&&!t.altKey){this.captureSelection();let{start:s,end:n}=this.selection,o=t.key==="Backspace"?s-1:s;s===n&&((i=c.slice(this.parts(),o,o+1)[0])==null?void 0:i.type)==="context"&&(t.preventDefault(),this.replace(o,o+1,[]))}}copy(t,e){if(!this.captureSelection()||this.selection.start===this.selection.end)return;let i=c.slice(this.parts(),this.selection.start,this.selection.end);t.clipboardData.setData("text/plain",c.text(i)),t.clipboardData.setData("application/x-pycodex-parts",JSON.stringify(i)),t.preventDefault(),e&&this.replace(this.selection.start,this.selection.end,[])}paste(t){try{let e=t.getData("application/x-pycodex-parts"),i=e?JSON.parse(e):[{type:"text",text:t.getData("text/plain")}];if(!Array.isArray(i)||i.some(s=>!s||typeof s.text!="string"||!["text","context"].includes(s.type)||s.type==="context"&&typeof s.label!="string"))throw new Error("Invalid context format in clipboard.");this.captureSelection(),this.replace(this.selection.start,this.selection.end,i.map(s=>s.type==="context"?{...s,id:crypto.getRandomValues(new Uint32Array(4)).join("-")}:s))}catch(e){this.error(e.message)}}};function Y(c,t=!0){if(typeof c!="string"||!c||c!==c.trim()||/[\x00-\x1f\x7f]/.test(c))return!1;if(/^(?:diff:|git:|workspace:\/\/canvas\/?$)/i.test(c))return!0;if(!t||/^(?:https?|ftps?|mailto|tel|sms|javascript|vbscript|data|blob|about|file):/i.test(c))return!1;let e=c.split("#");if(e.length>2||e.length===2&&!/^L-?\d+$/i.test(e[1]))return!1;let i=e[0].replace(/:-?\d+$/,"");return!!i&&!i.startsWith("//")&&!i.includes("?")&&(!/^[a-z][a-z\d+.-]*:/i.test(i)||/^[a-z]:(?:[/\\]|%2f|%5c)/i.test(i))}var O=class{constructor({request:t,onChange:e,onSelect:i,selected:s=null,enableBoard:n=!0}){this.request=t,this.onChange=e,this.onSelect=i,this.enableBoard=n,this.views=[],this.activeId=n?"board":null,this.snapshot=null,this.selection=null,this.displayMode=null,this.loading=!1,this.error="",this.closing=new Set,this.cache=new Map,this.initialSelection=s,this.initialized=!1,this.mutation=0,this.pollRequest=null,this.contentRequest=null,this.openRequest=null,this.timer=null}start(){this.timer===null&&(this.poll(),this.timer=setInterval(()=>this.poll(),2e3))}stop(){clearInterval(this.timer),this.timer=null,this.cancelOpen(),this.pollRequest&&this.pollRequest.abort(),this.contentRequest&&this.contentRequest.abort(),this.pollRequest=null,this.contentRequest=null,this.loading=!1}async poll(){if(this.pollRequest||this.openRequest)return;let t=new AbortController,e=this.mutation;this.pollRequest=t;try{let i=await this.request("api/views",{signal:t.signal});if(this.pollRequest!==t||e!==this.mutation)return;this.updateViews(i.views);let s=new Set(this.views.map(o=>o.id));this.error="";let n=this.activeId;this.initialized||(n=this.initialSelection||this.defaultId(),this.initialized=!0),(n==="board"&&!this.enableBoard||n!==null&&n!=="board"&&!s.has(n))&&(n=this.defaultId()),n!==this.activeId||n!==null&&n!=="board"&&!this.snapshot&&!this.loading?this.select(n):(n!==null&&n!=="board"&&!this.cache.has(n)&&!this.contentRequest&&this.load(n),this.onChange(this))}catch(i){this.pollRequest===t&&e===this.mutation&&i.name!=="AbortError"&&(this.error="Unable to load tabs: "+i.message,this.onChange(this))}finally{this.pollRequest===t&&(this.pollRequest=null)}}defaultId(){return this.enableBoard?"board":this.views.length?this.views[0].id:null}updateViews(t){this.views=t;let e=new Map(t.map(i=>[i.id,i.revision]));for(let[i,s]of this.cache)(!e.has(i)||s.revision<e.get(i))&&this.cache.delete(i)}cancelOpen(){this.openRequest&&this.openRequest.abort(),this.openRequest=null}async open(t,e){if(this.cancelOpen(),/^workspace:\/\/canvas\/?$/i.test(t)){if(!this.enableBoard)throw new Error("Canvas is disabled for this workspace.");await this.select("board");return}let i=new AbortController;this.openRequest=i,this.mutation+=1,this.error="",this.onChange(this);try{let s=await this.request("api/views/open",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({href:t,src_path:e}),signal:i.signal});if(this.openRequest!==i)return;this.mutation+=1,this.updateViews(s.views);let n=s.view;return this.cache.set(n.id,n),this.initialized=!0,this.openRequest=null,this.select(n.id,n.selection?{...n.selection,revision:n.reveal_revision,contentRevision:n.revision}:null,n.selection?"source":"preview")}catch(s){if(this.openRequest!==i||s.name==="AbortError")return;throw this.error="Unable to open view: "+s.message,s}finally{this.openRequest===i&&(this.openRequest=null,this.onChange(this))}}async select(t,e=null,i=null){if(t==="board"&&!this.enableBoard||t!==null&&t!=="board"&&!this.views.some(n=>n.id===t))return;this.cancelOpen();let s=t===this.activeId?this.snapshot:null;this.contentRequest&&this.contentRequest.abort(),this.contentRequest=null,this.activeId=t,this.selection=e,this.displayMode=i,this.initialized||(this.initialSelection=t),this.snapshot=this.cache.get(t)||s,this.loading=t!==null&&t!=="board"&&!this.snapshot,this.error="",this.onSelect(t),this.onChange(this),!(t===null||t==="board"||this.cache.has(t))&&await this.load(t)}async load(t){let e=new AbortController;this.contentRequest=e;try{let i=await this.request("api/views/"+encodeURIComponent(t),{signal:e.signal});if(this.contentRequest!==e||this.activeId!==t)return;let s=this.views.find(n=>n.id===t);if(!s||i.revision<s.revision)return;this.cache.set(t,i),this.snapshot=i,this.error=""}catch(i){if(this.contentRequest!==e||this.activeId!==t||i.name==="AbortError")return;this.error="Unable to load view: "+i.message}finally{this.contentRequest===e&&(this.contentRequest=null,this.loading=!1,this.onChange(this))}}async close(t){if(!(t===null||t==="board"||this.closing.has(t))){this.cancelOpen(),this.closing.add(t),this.mutation+=1,this.onChange(this);try{await this.request("api/views/"+encodeURIComponent(t),{method:"DELETE"}),this.mutation+=1;let e=this.views.findIndex(i=>i.id===t);this.views=this.views.filter(i=>i.id!==t),this.cache.delete(t),this.activeId===t&&await this.select(e>0?this.views[e-1].id:this.defaultId())}catch(e){this.error="Unable to close tab: "+e.message}finally{this.closing.delete(t),this.onChange(this)}}}};function w(c,t,e,i){let s=c.createElement(t);return s.className=e,i!==void 0&&(s.textContent=i),s}var P=class{constructor(t,e=[0,0]){this.document=t.ownerDocument,this.window=this.document.defaultView,this.position=e,this.generation=0,this.frame=w(this.document,"iframe","snapshot-html"),this.frame.title="HTML preview",this.receive=i=>{if(i.source!==this.frame.contentWindow)return;let s=i.data;!s||s.generation!==this.generation||(s.type==="workspace-preview-ready"?this.frame.contentWindow.postMessage({type:"workspace-preview-restore",generation:this.generation,position:this.position},"*"):s.type==="workspace-preview-scroll"&&Array.isArray(s.position)&&s.position.length===2&&s.position.every(Number.isFinite)&&(this.position=s.position))},this.window.addEventListener("message",this.receive),t.appendChild(this.frame)}update(t){if(t===this.source)return;let e=new this.window.DOMParser().parseFromString(t,"text/html"),i=this.generation+1,s=e.createElement("script");s.textContent=`(() => {
7
+ `)){let s=document.createElement("br");s.dataset.tail="",this.element.append(s)}let i=c.size(e);this.selection=t.selection||{start:i,end:i},this.selection={start:Math.min(this.selection.start,i),end:Math.min(this.selection.end,i)},this.element.classList.toggle("empty",!e.length)}focus(){this.element.focus({preventScroll:!0});let t=s=>{let n=o=>{var r;for(let d=0;d<o.childNodes.length;d++){let h=o.childNodes[d];if(h.nodeType===Node.TEXT_NODE){if(s<=h.length)return[h,s];s-=h.length}else if((r=h.dataset)!=null&&r.context){if(s===0)return[o,d];if(s-=1,s===0)return[o,d+1]}else if(h.nodeName==="BR"){if(s===0)return[o,d];h.hasAttribute("data-tail")||(s-=1)}else{let u=n(h);if(u)return u}}};return n(this.element)||[this.element,this.element.childNodes.length]},e=document.createRange();e.setStart(...t(this.selection.start)),e.setEnd(...t(this.selection.end));let i=window.getSelection();i.removeAllRanges(),i.addRange(e)}record(){let t=this.read();JSON.stringify(t.parts)!==JSON.stringify(this.previous.parts)&&(this.undo.push(this.previous),this.undo.length>40&&this.undo.shift(),this.redo=[],this.previous=t),this.element.classList.toggle("empty",!t.parts.length)}replace(t,e,i){let s=this.read(),n=[...c.slice(s.parts,0,t),...i,...c.slice(s.parts,e,c.size(s.parts))],o=t+c.size(i);this.render({parts:n,selection:{start:o,end:o}}),this.focus(),this.record(),this.changed()}insert(t){this.captureSelection(),this.replace(this.selection.start,this.selection.end,t.map(e=>({...e,type:"context",id:crypto.getRandomValues(new Uint32Array(4)).join("-")})))}updateContext(t,e){for(let i of this.element.querySelectorAll(".context-token")){let s=JSON.parse(i.dataset.context);s.id===t&&(i.dataset.context=JSON.stringify({...s,text:e}))}this.record(),this.changed()}removeContext(t){let e=0;for(let i of this.parts()){if(i.type==="context"&&i.id===t){this.replace(e,e+1,[]);return}e+=c.size([i])}}history(t){let e=t?this.undo:this.redo;e.length&&((t?this.redo:this.undo).push(this.read()),this.render(e.pop()),this.focus(),this.previous=this.read(),this.changed())}keydown(t){var i;if(t.isComposing||this.composing||t.keyCode===229)return;let e=t.ctrlKey||t.metaKey;if(e&&(t.key.toLowerCase()==="z"||t.key.toLowerCase()==="y"))t.preventDefault(),this.history(t.key.toLowerCase()==="z"&&!t.shiftKey);else if(t.key==="Enter")t.preventDefault(),t.shiftKey?(this.captureSelection(),this.replace(this.selection.start,this.selection.end,[{type:"text",text:`
8
+ `}])):this.submit();else if(["Backspace","Delete"].includes(t.key)&&!e&&!t.altKey){this.captureSelection();let{start:s,end:n}=this.selection,o=t.key==="Backspace"?s-1:s;s===n&&((i=c.slice(this.parts(),o,o+1)[0])==null?void 0:i.type)==="context"&&(t.preventDefault(),this.replace(o,o+1,[]))}}copy(t,e){if(!this.captureSelection()||this.selection.start===this.selection.end)return;let i=c.slice(this.parts(),this.selection.start,this.selection.end);t.clipboardData.setData("text/plain",c.text(i)),t.clipboardData.setData("application/x-pycodex-parts",JSON.stringify(i)),t.preventDefault(),e&&this.replace(this.selection.start,this.selection.end,[])}paste(t){try{let e=t.getData("application/x-pycodex-parts"),i=e?JSON.parse(e):[{type:"text",text:t.getData("text/plain")}];if(!Array.isArray(i)||i.some(s=>!s||typeof s.text!="string"||!["text","context"].includes(s.type)||s.type==="context"&&typeof s.label!="string"))throw new Error("Invalid context format in clipboard.");this.captureSelection(),this.replace(this.selection.start,this.selection.end,i.map(s=>s.type==="context"?{...s,id:crypto.getRandomValues(new Uint32Array(4)).join("-")}:s))}catch(e){this.error(e.message)}}};function X(c,t=!0){if(typeof c!="string"||!c||c!==c.trim()||/[\x00-\x1f\x7f]/.test(c))return!1;if(/^(?:diff:|git:|workspace:\/\/canvas\/?$)/i.test(c))return!0;if(!t||/^(?:https?|ftps?|mailto|tel|sms|javascript|vbscript|data|blob|about|file):/i.test(c))return!1;let e=c.split("#");if(e.length>2||e.length===2&&!/^L-?\d+$/i.test(e[1]))return!1;let i=e[0].replace(/:-?\d+$/,"");return!!i&&!i.startsWith("//")&&!i.includes("?")&&(!/^[a-z][a-z\d+.-]*:/i.test(i)||/^[a-z]:(?:[/\\]|%2f|%5c)/i.test(i))}var N=class{constructor({request:t,onChange:e,onSelect:i,selected:s=null,enableBoard:n=!0}){this.request=t,this.onChange=e,this.onSelect=i,this.enableBoard=n,this.views=[],this.activeId=n?"board":null,this.snapshot=null,this.selection=null,this.displayMode=null,this.loading=!1,this.error="",this.closing=new Set,this.cache=new Map,this.initialSelection=s,this.initialized=!1,this.mutation=0,this.pollRequest=null,this.contentRequest=null,this.openRequest=null,this.timer=null}start(){this.timer===null&&(this.poll(),this.timer=setInterval(()=>this.poll(),2e3))}stop(){clearInterval(this.timer),this.timer=null,this.cancelOpen(),this.pollRequest&&this.pollRequest.abort(),this.contentRequest&&this.contentRequest.abort(),this.pollRequest=null,this.contentRequest=null,this.loading=!1}async poll(){if(this.pollRequest||this.openRequest)return;let t=new AbortController,e=this.mutation;this.pollRequest=t;try{let i=await this.request("api/views",{signal:t.signal});if(this.pollRequest!==t||e!==this.mutation)return;this.updateViews(i.views);let s=new Set(this.views.map(o=>o.id));this.error="";let n=this.activeId;this.initialized||(n=this.initialSelection||this.defaultId(),this.initialized=!0),(n==="board"&&!this.enableBoard||n!==null&&n!=="board"&&!s.has(n))&&(n=this.defaultId()),n!==this.activeId||n!==null&&n!=="board"&&!this.snapshot&&!this.loading?this.select(n):(n!==null&&n!=="board"&&!this.cache.has(n)&&!this.contentRequest&&this.load(n),this.onChange(this))}catch(i){this.pollRequest===t&&e===this.mutation&&i.name!=="AbortError"&&(this.error="Unable to load tabs: "+i.message,this.onChange(this))}finally{this.pollRequest===t&&(this.pollRequest=null)}}defaultId(){return this.enableBoard?"board":this.views.length?this.views[0].id:null}updateViews(t){this.views=t;let e=new Map(t.map(i=>[i.id,i.revision]));for(let[i,s]of this.cache)(!e.has(i)||s.revision<e.get(i))&&this.cache.delete(i)}cancelOpen(){this.openRequest&&this.openRequest.abort(),this.openRequest=null}async open(t,e){if(this.cancelOpen(),/^workspace:\/\/canvas\/?$/i.test(t)){if(!this.enableBoard)throw new Error("Canvas is disabled for this workspace.");await this.select("board");return}let i=new AbortController;this.openRequest=i,this.mutation+=1,this.error="",this.onChange(this);try{let s=await this.request("api/views/open",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({href:t,src_path:e}),signal:i.signal});if(this.openRequest!==i)return;this.mutation+=1,this.updateViews(s.views);let n=s.view;return this.cache.set(n.id,n),this.initialized=!0,this.openRequest=null,this.select(n.id,n.selection?{...n.selection,revision:n.reveal_revision,contentRevision:n.revision}:null,n.selection?"source":"preview")}catch(s){if(this.openRequest!==i||s.name==="AbortError")return;throw this.error="Unable to open view: "+s.message,s}finally{this.openRequest===i&&(this.openRequest=null,this.onChange(this))}}async select(t,e=null,i=null){if(t==="board"&&!this.enableBoard||t!==null&&t!=="board"&&!this.views.some(n=>n.id===t))return;this.cancelOpen();let s=t===this.activeId?this.snapshot:null;this.contentRequest&&this.contentRequest.abort(),this.contentRequest=null,this.activeId=t,this.selection=e,this.displayMode=i,this.initialized||(this.initialSelection=t),this.snapshot=this.cache.get(t)||s,this.loading=t!==null&&t!=="board"&&!this.snapshot,this.error="",this.onSelect(t),this.onChange(this),!(t===null||t==="board"||this.cache.has(t))&&await this.load(t)}async load(t){let e=new AbortController;this.contentRequest=e;try{let i=await this.request("api/views/"+encodeURIComponent(t),{signal:e.signal});if(this.contentRequest!==e||this.activeId!==t)return;let s=this.views.find(n=>n.id===t);if(!s||i.revision<s.revision)return;this.cache.set(t,i),this.snapshot=i,this.error=""}catch(i){if(this.contentRequest!==e||this.activeId!==t||i.name==="AbortError")return;this.error="Unable to load view: "+i.message}finally{this.contentRequest===e&&(this.contentRequest=null,this.loading=!1,this.onChange(this))}}async close(t){if(!(t===null||t==="board"||this.closing.has(t))){this.cancelOpen(),this.closing.add(t),this.mutation+=1,this.onChange(this);try{await this.request("api/views/"+encodeURIComponent(t),{method:"DELETE"}),this.mutation+=1;let e=this.views.findIndex(i=>i.id===t);this.views=this.views.filter(i=>i.id!==t),this.cache.delete(t),this.activeId===t&&await this.select(e>0?this.views[e-1].id:this.defaultId())}catch(e){this.error="Unable to close tab: "+e.message}finally{this.closing.delete(t),this.onChange(this)}}}};function y(c,t,e,i){let s=c.createElement(t);return s.className=e,i!==void 0&&(s.textContent=i),s}var O=class{constructor(t,e=[0,0]){this.document=t.ownerDocument,this.window=this.document.defaultView,this.position=e,this.generation=0,this.frame=y(this.document,"iframe","snapshot-html"),this.frame.title="HTML preview",this.receive=i=>{if(i.source!==this.frame.contentWindow)return;let s=i.data;!s||s.generation!==this.generation||(s.type==="workspace-preview-ready"?this.frame.contentWindow.postMessage({type:"workspace-preview-restore",generation:this.generation,position:this.position},"*"):s.type==="workspace-preview-scroll"&&Array.isArray(s.position)&&s.position.length===2&&s.position.every(Number.isFinite)&&(this.position=s.position))},this.window.addEventListener("message",this.receive),t.appendChild(this.frame)}update(t){if(t===this.source)return;let e=new this.window.DOMParser().parseFromString(t,"text/html"),i=this.generation+1,s=e.createElement("script");s.textContent=`(() => {
9
9
  const generation = ${i};
10
10
  let restored = false;
11
11
  function report() {
@@ -26,6 +26,6 @@ Click to edit context; Backspace / Delete to remove`,this.element.append(n,docum
26
26
  type: "workspace-preview-ready", generation,
27
27
  }, "*"));
28
28
  })();`,e.head.prepend(s);let n=e.doctype?new this.window.XMLSerializer().serializeToString(e.doctype)+`
29
- `:"";this.frame.srcdoc=n+e.documentElement.outerHTML,this.generation=i,this.source=t}capture(){return this.position}destroy(){this.window.removeEventListener("message",this.receive)}},T=class{constructor({content:t,renderMarkdown:e,loadEditor:i=null,onInsertContext:s=null,createHtmlPreview:n=(o,a)=>new P(o,a)}){this.content=t,this.document=t.ownerDocument,this.renderMarkdown=e,this.onInsertContext=s,this.loadEditor=i||(()=>this.loadEditorAssets()),this.editorAssets=null,this.createHtmlPreview=n,this.activeId=null,this.editor=null,this.htmlPreview=null,this.generation=0,this.remaining=new Set,this.editorStates=new Map,this.previews=new Map,this.scrollPositions=new Map,this.sourceViews=new Set,this.pendingSelection=null,this.selectionRevision=0}loadEditorAssets(){if(!this.editorAssets){let t=this.document.createElement("link");t.rel="stylesheet",t.href=new URL("./editor.css",import.meta.url).href;let e=new Promise((i,s)=>{t.addEventListener("load",i,{once:!0}),t.addEventListener("error",()=>s(new Error("Editor stylesheet request failed.")),{once:!0})});this.document.head.appendChild(t),this.editorAssets=Promise.all([import("./editor.mjs"),e]).then(([i])=>i).catch(i=>{throw t.remove(),this.editorAssets=null,i})}return this.editorAssets}retain(t){this.remaining=new Set(t);for(let e of[this.editorStates,this.previews,this.scrollPositions,this.sourceViews])for(let i of e.keys())this.remaining.has(i)||e.delete(i)}clear(t=""){this.generation+=1,this.editor?(this.remaining.has(this.activeId)&&this.editorStates.set(this.activeId,this.editor.capture()),this.editor.destroy(),this.editor=null):this.htmlPreview?(this.remaining.has(this.activeId)&&this.scrollPositions.set(this.activeId,this.htmlPreview.capture()),this.htmlPreview.destroy(),this.htmlPreview=null):this.remaining.has(this.activeId)&&this.previews.has(this.activeId)&&this.scrollPositions.set(this.activeId,[this.content.scrollLeft,this.content.scrollTop]),this.activeId=null,this.content.classList.remove("code-view"),this.content.classList.remove("html-view"),this.content.textContent="",t&&this.content.appendChild(w(this.document,"div","content-placeholder",t))}async show(t,e,i=null,s=null){i&&e.revision<i.contentRevision&&(i=null),this.pendingSelection=i,s==="preview"?this.sourceViews.delete(t):(i||s==="source")&&this.sourceViews.add(t);let n=e.documents[0],o=e.kind==="file"&&!this.sourceViews.has(t),a=o&&/\.(md|markdown)$/i.test(n.label)&&this.renderMarkdown,u=o&&/\.html?$/i.test(n.label);if(this.activeId===t&&this.htmlPreview&&u)return this.htmlPreview.update(n.text);if(this.activeId===t&&this.editor&&!a&&!u){let m=this.generation;try{await this.editor.update(e),m===this.generation&&await this.applySelection(t,i)}catch(v){if(m===this.generation)throw v}return}this.clear(),this.activeId=t;let h=this.generation;if(e.kind==="diff"||!n.image_url&&!a&&!u){this.content.classList.add("code-view"),this.content.appendChild(w(this.document,"div","content-placeholder","Loading code viewer\u2026"));try{let m=await this.loadEditor();if(h!==this.generation)return;this.content.textContent="";let v=w(this.document,"div","workspace-editor");this.content.appendChild(v);let k=this.onInsertContext&&(y=>{h===this.generation&&this.activeId===t&&this.onInsertContext(y)}),E=await m.createSnapshotEditor(v,e,this.editorStates.get(t),k);if(h!==this.generation){E.destroy();return}this.editor=E}catch(m){if(h!==this.generation)return;this.content.textContent="",this.content.appendChild(w(this.document,"div","content-placeholder","Unable to load code viewer: "+m.message));return}await this.applySelection(t,i);return}if(u)return this.content.classList.add("html-view"),this.htmlPreview=this.createHtmlPreview(this.content,this.scrollPositions.get(t)),this.htmlPreview.update(n.text);if(!this.previews.has(t)){let m=w(this.document,"div","snapshot"),v=w(this.document,"div","snapshot-heading");if(v.appendChild(w(this.document,"span","snapshot-label",n.label)),v.appendChild(w(this.document,"span","snapshot-stamp","Read only")),m.appendChild(v),n.image_url){let k=w(this.document,"img","snapshot-image");k.alt=n.label,m.appendChild(k)}else m.appendChild(w(this.document,"div","snapshot-markdown markdown"));this.previews.set(t,m)}this.content.appendChild(this.previews.get(t));let d=this.content.querySelector(".snapshot-image");if(d&&d.src!==n.image_url&&(d.src=n.image_url),a){let m=this.content.querySelector(".snapshot-markdown");n.source&&m.setAttribute("data-src-path",n.source.path),this.renderMarkdown(m,n.text)}let[p,x]=this.scrollPositions.get(t)||[0,0];this.content.scrollLeft=p,this.content.scrollTop=x}async applySelection(t,e){if(!e||e.revision<=this.selectionRevision)return;let i=this.editor,s=()=>this.activeId===t&&this.editor===i&&this.pendingSelection===e&&e.revision>this.selectionRevision;s()&&await i.revealLine(e.document,e.line,s)&&(this.selectionRevision=e.revision)}};function G({tabs:c,frame:t,content:e,status:i,request:s,storage:n,storageKey:o,renderMarkdown:a,onInsertContext:u,enableBoard:h=!0}){let d=c.ownerDocument,p=new T({content:e,renderMarkdown:a,onInsertContext:u}),x,m=null,v=!1,k=null,E=null,y;function z(r){let S=d.getElementById("content-tab-"+r);S&&(S.focus({preventScroll:!0}),S.scrollIntoView({block:"nearest",inline:"nearest"}))}function q(r){let S=r.snapshot?r.snapshot.documents.map(l=>l.error):[];i.textContent=[r.error,...S].filter(Boolean).join("; ")||(r.openRequest?"Opening view\u2026":""),i.title=i.textContent,i.hidden=!i.textContent;let L=new Map(Array.from(c.children,l=>[l.dataset.viewId,l])),W=(h?[{id:"board",title:"Canvas",kind:"board"}]:[]).concat(r.views),B=$(r.views);B.set("board",{name:"Canvas",description:""}),W.forEach((l,R)=>{let f=L.get(l.id);if(!f){f=w(d,"div","tab content-tab"),f.dataset.viewId=l.id,f.id="content-tab-"+l.id,f.setAttribute("role","tab"),f.setAttribute("aria-controls",l.id==="board"?t.id:e.id);let M=w(d,"span","tab-label content-tab-label");if(M.appendChild(w(d,"span","content-tab-name")),M.appendChild(w(d,"span","content-tab-description")),f.appendChild(M),f.addEventListener("click",()=>y.select(l.id)),f.addEventListener("keydown",g=>{if(g.target!==f)return;let b=(h?["board"]:[]).concat(y.views.map(H=>H.id)),J=b.indexOf(l.id),C=null;g.key==="ArrowRight"&&(C=b[(J+1)%b.length]),g.key==="ArrowLeft"&&(C=b[(J+b.length-1)%b.length]),g.key==="Home"&&(C=b[0]),g.key==="End"&&(C=b[b.length-1]),(g.key==="Enter"||g.key===" ")&&(C=l.id),g.key==="Delete"&&l.id!=="board"&&(g.preventDefault(),y.close(l.id)),C!==null&&(g.preventDefault(),y.select(C),d.getElementById("content-tab-"+C).focus())}),l.id!=="board"){let g=w(d,"button","tab-close content-tab-close");g.type="button",g.title="Close tab",g.setAttribute("aria-label","Close "+l.title),g.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg>',g.addEventListener("click",b=>{b.stopPropagation(),y.close(l.id)}),f.appendChild(g)}}L.delete(l.id);let A=B.get(l.id);f.querySelector(".content-tab-name").textContent=A.name;let U=f.querySelector(".content-tab-description");U.textContent=A.description,U.hidden=!A.description,f.className="tab content-tab"+(l.id===r.activeId?" active":""),f.tabIndex=l.id===r.activeId?0:-1,f.title=l.id==="board"?c.dataset.boardTitle:l.title+(l.live?`
29
+ `:"";this.frame.srcdoc=n+e.documentElement.outerHTML,this.generation=i,this.source=t}capture(){return this.position}destroy(){this.window.removeEventListener("message",this.receive)}},P=class{constructor({content:t,renderMarkdown:e,loadEditor:i=null,onInsertContext:s=null,createHtmlPreview:n=(o,r)=>new O(o,r)}){this.content=t,this.document=t.ownerDocument,this.renderMarkdown=e,this.onInsertContext=s,this.loadEditor=i||(()=>this.loadEditorAssets()),this.editorAssets=null,this.createHtmlPreview=n,this.activeId=null,this.editor=null,this.htmlPreview=null,this.generation=0,this.remaining=new Set,this.editorStates=new Map,this.previews=new Map,this.scrollPositions=new Map,this.sourceViews=new Set,this.pendingSelection=null,this.selectionRevision=0}loadEditorAssets(){if(!this.editorAssets){let t=this.document.createElement("link");t.rel="stylesheet",t.href=new URL("./editor.css",import.meta.url).href;let e=new Promise((i,s)=>{t.addEventListener("load",i,{once:!0}),t.addEventListener("error",()=>s(new Error("Editor stylesheet request failed.")),{once:!0})});this.document.head.appendChild(t),this.editorAssets=Promise.all([import("./editor.mjs"),e]).then(([i])=>i).catch(i=>{throw t.remove(),this.editorAssets=null,i})}return this.editorAssets}retain(t){this.remaining=new Set(t);for(let e of[this.editorStates,this.previews,this.scrollPositions,this.sourceViews])for(let i of e.keys())this.remaining.has(i)||e.delete(i)}clear(t=""){this.generation+=1,this.editor?(this.remaining.has(this.activeId)&&this.editorStates.set(this.activeId,this.editor.capture()),this.editor.destroy(),this.editor=null):this.htmlPreview?(this.remaining.has(this.activeId)&&this.scrollPositions.set(this.activeId,this.htmlPreview.capture()),this.htmlPreview.destroy(),this.htmlPreview=null):this.remaining.has(this.activeId)&&this.previews.has(this.activeId)&&this.scrollPositions.set(this.activeId,[this.content.scrollLeft,this.content.scrollTop]),this.activeId=null,this.content.classList.remove("code-view"),this.content.classList.remove("html-view"),this.content.textContent="",t&&this.content.appendChild(y(this.document,"div","content-placeholder",t))}async show(t,e,i=null,s=null){i&&e.revision<i.contentRevision&&(i=null),this.pendingSelection=i,s==="preview"?this.sourceViews.delete(t):(i||s==="source")&&this.sourceViews.add(t);let n=e.documents[0],o=e.kind==="file"&&!this.sourceViews.has(t),r=o&&/\.(md|markdown)$/i.test(n.label)&&this.renderMarkdown,d=o&&/\.html?$/i.test(n.label);if(this.activeId===t&&this.htmlPreview&&d)return this.htmlPreview.update(n.text);if(this.activeId===t&&this.editor&&!r&&!d){let m=this.generation;try{await this.editor.update(e),m===this.generation&&await this.applySelection(t,i)}catch(v){if(m===this.generation)throw v}return}this.clear(),this.activeId=t;let h=this.generation;if(e.kind==="diff"||!n.image_url&&!r&&!d){this.content.classList.add("code-view"),this.content.appendChild(y(this.document,"div","content-placeholder","Loading code viewer\u2026"));try{let m=await this.loadEditor();if(h!==this.generation)return;this.content.textContent="";let v=y(this.document,"div","workspace-editor");this.content.appendChild(v);let g=this.onInsertContext&&(E=>{h===this.generation&&this.activeId===t&&this.onInsertContext(E)}),S=await m.createSnapshotEditor(v,e,this.editorStates.get(t),g);if(h!==this.generation){S.destroy();return}this.editor=S}catch(m){if(h!==this.generation)return;this.content.textContent="",this.content.appendChild(y(this.document,"div","content-placeholder","Unable to load code viewer: "+m.message));return}await this.applySelection(t,i);return}if(d)return this.content.classList.add("html-view"),this.htmlPreview=this.createHtmlPreview(this.content,this.scrollPositions.get(t)),this.htmlPreview.update(n.text);if(!this.previews.has(t)){let m=y(this.document,"div","snapshot"),v=y(this.document,"div","snapshot-heading");if(v.appendChild(y(this.document,"span","snapshot-label",n.label)),v.appendChild(y(this.document,"span","snapshot-stamp","Read only")),m.appendChild(v),n.image_url){let g=y(this.document,"img","snapshot-image");g.alt=n.label,m.appendChild(g)}else m.appendChild(y(this.document,"div","snapshot-markdown markdown"));this.previews.set(t,m)}this.content.appendChild(this.previews.get(t));let u=this.content.querySelector(".snapshot-image");if(u&&u.src!==n.image_url&&(u.src=n.image_url),r){let m=this.content.querySelector(".snapshot-markdown");n.source&&m.setAttribute("data-src-path",n.source.path),this.renderMarkdown(m,n.text)}let[p,k]=this.scrollPositions.get(t)||[0,0];this.content.scrollLeft=p,this.content.scrollTop=k}async applySelection(t,e){if(!e||e.revision<=this.selectionRevision)return;let i=this.editor,s=()=>this.activeId===t&&this.editor===i&&this.pendingSelection===e&&e.revision>this.selectionRevision;s()&&await i.revealLine(e.document,e.line,s)&&(this.selectionRevision=e.revision)}};function F({tabs:c,frame:t,content:e,status:i,request:s,renderMarkdown:n,onInsertContext:o,enableBoard:r=!0}){let d=c.ownerDocument,h=new P({content:e,renderMarkdown:n,onInsertContext:o}),u,p=null,k=!1,m=null,v=null,g;function S(a){let C=d.getElementById("content-tab-"+a);C&&(C.focus({preventScroll:!0}),C.scrollIntoView({block:"nearest",inline:"nearest"}))}function E(a){let C=a.snapshot?a.snapshot.documents.map(l=>l.error):[];i.textContent=[a.error,...C].filter(Boolean).join("; ")||(a.openRequest?"Opening view\u2026":""),i.title=i.textContent,i.hidden=!i.textContent;let q=new Map(Array.from(c.children,l=>[l.dataset.viewId,l])),J=(r?[{id:"board",title:"Canvas",kind:"board"}]:[]).concat(a.views),T=U(a.views);T.set("board",{name:"Canvas",description:""}),J.forEach((l,R)=>{let f=q.get(l.id);if(!f){f=y(d,"div","tab content-tab"),f.dataset.viewId=l.id,f.id="content-tab-"+l.id,f.setAttribute("role","tab"),f.setAttribute("aria-controls",l.id==="board"?t.id:e.id);let D=y(d,"span","tab-label content-tab-label");if(D.appendChild(y(d,"span","content-tab-name")),D.appendChild(y(d,"span","content-tab-description")),f.appendChild(D),f.addEventListener("click",()=>g.select(l.id)),f.addEventListener("keydown",w=>{if(w.target!==f)return;let b=(r?["board"]:[]).concat(g.views.map($=>$.id)),j=b.indexOf(l.id),x=null;w.key==="ArrowRight"&&(x=b[(j+1)%b.length]),w.key==="ArrowLeft"&&(x=b[(j+b.length-1)%b.length]),w.key==="Home"&&(x=b[0]),w.key==="End"&&(x=b[b.length-1]),(w.key==="Enter"||w.key===" ")&&(x=l.id),w.key==="Delete"&&l.id!=="board"&&(w.preventDefault(),g.close(l.id)),x!==null&&(w.preventDefault(),g.select(x),d.getElementById("content-tab-"+x).focus())}),l.id!=="board"){let w=y(d,"button","tab-close content-tab-close");w.type="button",w.title="Close tab",w.setAttribute("aria-label","Close "+l.title),w.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg>',w.addEventListener("click",b=>{b.stopPropagation(),g.close(l.id)}),f.appendChild(w)}}q.delete(l.id);let L=T.get(l.id);f.querySelector(".content-tab-name").textContent=L.name;let V=f.querySelector(".content-tab-description");V.textContent=L.description,V.hidden=!L.description,f.className="tab content-tab"+(l.id===a.activeId?" active":""),f.tabIndex=l.id===a.activeId?0:-1,f.title=l.id==="board"?c.dataset.boardTitle:l.title+(l.live?`
30
30
  Read only \xB7 follows file changes \xB7 `:`
31
- Read-only snapshot \xB7 `)+new Date(l.created_at).toLocaleString(),f.setAttribute("aria-selected",String(l.id===r.activeId)),f.setAttribute("aria-label",l.title);let D=f.querySelector(".content-tab-close");D&&(D.disabled=r.closing.has(l.id),D.setAttribute("aria-label","Close "+l.title)),c.children[R]!==f&&c.insertBefore(f,c.children[R]||null)}),L.forEach(l=>l.remove()),p.retain(r.views.map(l=>l.id));let I=r.activeId==="board",V=x!==r.activeId,j=V||m!==r.snapshot||v!==r.loading||k!==r.selection||E!==r.displayMode;if(I&&j&&p.clear(),t.hidden=!I,e.hidden=I,!!j&&(x=r.activeId,m=r.snapshot,v=r.loading,k=r.selection,E=r.displayMode,r.activeId?e.setAttribute("aria-labelledby","content-tab-"+r.activeId):e.removeAttribute("aria-labelledby"),e.setAttribute("aria-busy",String(r.loading)),!I)){if(r.snapshot){let l=r.activeId,R=r.snapshot;p.show(l,R,r.selection,r.displayMode).catch(f=>{x!==l||m!==R||(m=null,r.error="Unable to update view: "+f.message,i.textContent=i.title=r.error,i.hidden=!1)})}else p.clear(r.loading?"Loading view\u2026":r.activeId?"View unavailable.":"Open a file link to view it here.");V&&z(r.activeId)}}return y=new O({request:s,enableBoard:h,onChange:q,onSelect:r=>{r?n.setItem(o,r):n.removeItem(o),e.closest(".workspace").dataset.mobilePane="board",z(r)},selected:n.getItem(o)}),q(y),{start(){y.start(),q(y)},stop(){y.stop(),p.clear(),x=null},poll(){return y.poll()},open(r,S){return y.open(r,S)}}}export{N as Composer,O as ContentTabs,T as SnapshotDisplay,Y as isWorkspaceLink,G as mountContentViews};
31
+ Read-only snapshot \xB7 `)+new Date(l.created_at).toLocaleString(),f.setAttribute("aria-selected",String(l.id===a.activeId)),f.setAttribute("aria-label",l.title);let A=f.querySelector(".content-tab-close");A&&(A.disabled=a.closing.has(l.id),A.setAttribute("aria-label","Close "+l.title)),c.children[R]!==f&&c.insertBefore(f,c.children[R]||null)}),q.forEach(l=>l.remove()),h.retain(a.views.map(l=>l.id));let I=a.activeId==="board",z=u!==a.activeId,B=z||p!==a.snapshot||k!==a.loading||m!==a.selection||v!==a.displayMode;if(I&&B&&h.clear(),t.hidden=!I,e.hidden=I,!!B&&(u=a.activeId,p=a.snapshot,k=a.loading,m=a.selection,v=a.displayMode,a.activeId?e.setAttribute("aria-labelledby","content-tab-"+a.activeId):e.removeAttribute("aria-labelledby"),e.setAttribute("aria-busy",String(a.loading)),!I)){if(a.snapshot){let l=a.activeId,R=a.snapshot;h.show(l,R,a.selection,a.displayMode).catch(f=>{u!==l||p!==R||(p=null,a.error="Unable to update view: "+f.message,i.textContent=i.title=a.error,i.hidden=!1)})}else h.clear(a.loading?"Loading view\u2026":a.activeId?"View unavailable.":"Open a file link to view it here.");z&&S(a.activeId)}}return g=new N({request:s,enableBoard:r,onChange:E,onSelect:a=>{e.closest(".workspace").dataset.mobilePane="board",S(a)}}),E(g),{start(){g.start(),E(g)},stop(){g.stop(),h.clear(),u=null},poll(){return g.poll()},open(a,C){return g.open(a,C)}}}export{M as Composer,N as ContentTabs,P as SnapshotDisplay,X as isWorkspaceLink,F as mountContentViews};