pycodex-ws 0.5.0__tar.gz → 0.5.1__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.
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/PKG-INFO +2 -2
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_ws/feishu_card.py +2 -2
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_ws/feishu_link.py +1 -1
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pyproject.toml +2 -2
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/app.py +8 -0
- pycodex_ws-0.5.1/workspace_server/content_views.css +1 -0
- pycodex_ws-0.5.1/workspace_server/content_views.mjs +32 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/composer.mjs +28 -2
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/content_views.css +27 -3
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/content_views.mjs +38 -2
- pycodex_ws-0.5.1/workspace_server/frontend/prompt_history.mjs +85 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/workspace.html +26 -5
- pycodex_ws-0.5.0/workspace_server/content_views.css +0 -1
- pycodex_ws-0.5.0/workspace_server/content_views.mjs +0 -31
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/.gitignore +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/LICENSE +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/README.md +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_router/__init__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_router/__main__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_router/browser.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_router/client.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_router/index.html +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_router/protocol.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_router/server.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_ws/__init__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/pycodex_ws/feishu_oauth.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/__init__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/__main__.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/agent_access.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/content_views.LICENSE.txt +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/customization.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/editor.css +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/editor.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/.gitignore +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/build.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/editor.css +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/editor.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/editor_context.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/package-lock.json +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/package.json +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/touch_selection.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/frontend/view_labels.mjs +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/owned_process.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/runtime_info.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/workspace_views.py +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/workspaces.html +0 -0
- {pycodex_ws-0.5.0 → pycodex_ws-0.5.1}/workspace_server/workspaces.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pycodex-ws
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
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.5.
|
|
8
|
+
Requires-Dist: python-codex==0.5.1
|
|
9
9
|
Requires-Dist: websockets<16,>=11
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
|
|
@@ -357,9 +357,9 @@ class PycodexCard:
|
|
|
357
357
|
or getattr(action_payload, "input_value", None)
|
|
358
358
|
or value.get("input_value")
|
|
359
359
|
or ""
|
|
360
|
-
)
|
|
360
|
+
)
|
|
361
361
|
if not prompt and form_values:
|
|
362
|
-
prompt = str(next(iter(form_values.values())))
|
|
362
|
+
prompt = str(next(iter(form_values.values())))
|
|
363
363
|
action = str(
|
|
364
364
|
value.get("action") or getattr(action_payload, "name", None) or "send"
|
|
365
365
|
).strip()
|
|
@@ -105,7 +105,7 @@ class PycodexRuntimeLink:
|
|
|
105
105
|
"content": "Unsupported action: {0}".format(action.get("action")),
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
prompt = str(action.get("prompt") or "")
|
|
108
|
+
prompt = str(action.get("prompt") or "")
|
|
109
109
|
future = asyncio.run_coroutine_threadsafe(
|
|
110
110
|
self.runtime.submit_input(
|
|
111
111
|
prompt, sender=str(action.get("sender") or "feishu")
|
|
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pycodex-ws"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.1"
|
|
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.5.
|
|
12
|
+
"python-codex==0.5.1",
|
|
13
13
|
"pillow>=9.0.0",
|
|
14
14
|
"websockets>=11,<16",
|
|
15
15
|
]
|
|
@@ -397,6 +397,9 @@ class WebSessionView:
|
|
|
397
397
|
active["submission_id"],
|
|
398
398
|
)
|
|
399
399
|
)
|
|
400
|
+
self._turns_by_turn_id[active["turn_id"]]["model_errors"] = list(
|
|
401
|
+
active.get("model_errors", [])
|
|
402
|
+
)
|
|
400
403
|
elif event.reason == "title":
|
|
401
404
|
self.set_session_title(state["title"])
|
|
402
405
|
else:
|
|
@@ -464,6 +467,10 @@ class WebSessionView:
|
|
|
464
467
|
turn["_thinking_active"] = True
|
|
465
468
|
return
|
|
466
469
|
|
|
470
|
+
if isinstance(event, StreamErrorEvent):
|
|
471
|
+
turn.setdefault("model_errors", []).append(event.detail())
|
|
472
|
+
return
|
|
473
|
+
|
|
467
474
|
if isinstance(event, ToolStartedEvent):
|
|
468
475
|
turn["status"] = "tool"
|
|
469
476
|
turn["tool_name"] = event.call.name
|
|
@@ -1815,6 +1822,7 @@ def _public_turn(turn: "typing.Dict[str, object]") -> "typing.Dict[str, object]"
|
|
|
1815
1822
|
"queue": turn.get("queue", ""),
|
|
1816
1823
|
"sender": turn.get("sender", ""),
|
|
1817
1824
|
"kind": turn.get("kind", "assistant"),
|
|
1825
|
+
"model_errors": turn.get("model_errors", []),
|
|
1818
1826
|
}
|
|
1819
1827
|
),
|
|
1820
1828
|
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.content-tabs{flex:1}.tab.content-tab{flex:0 0 auto}.tab.content-tab:first-child{min-width:0;padding-right:12px}.content-tab:focus-visible{outline:2px solid var(--accent);outline-offset:-1px}.content-tab-label{display:flex;align-items:baseline;gap:6px}.content-tab-name{flex:0 0 auto;max-width:100%;overflow:hidden;text-overflow:ellipsis}.content-tab-description{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:11px;font-weight:400;opacity:.75}.content-status{max-width:50%;font-size:11px;color:var(--danger);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.content-mode-toggle{flex:0 0 auto;padding:4px 8px;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--accent);font-size:11px;white-space:nowrap;cursor:pointer}.content-mode-toggle:hover{background:var(--accent-soft)}.content-mode-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.content-area{position:relative;min-width:0;min-height:0;overflow:hidden}.content-area>[hidden]{display:none}.content-view{width:100%;height:100%;overflow:auto;background:var(--board-chrome);color:var(--ink)}.content-view.code-view,.content-view.html-view{overflow:hidden}.snapshot-html{display:block;width:100%;height:100%;border:0;background:#fff}.snapshot{min-height:100%}.snapshot-heading{display:flex;align-items:center;gap:8px;min-height:42px;padding:6px 16px;border-bottom:1px solid var(--line);font-size:12px}.markdown-heading{position:sticky;top:0;z-index:1;background:var(--board-chrome)}.snapshot-label{min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow-wrap:anywhere}.snapshot-stamp{flex:0 0 auto;margin-left:auto;color:var(--muted);font-size:11px;white-space:nowrap}.snapshot-markdown{max-width:900px;padding:24px 32px;margin:0 auto}.snapshot-image{display:block;max-width:calc(100% - 40px);height:auto;margin:20px auto}.content-placeholder{padding:40px 24px;text-align:center;color:var(--muted)}@media(max-width:760px){.snapshot-heading{flex-wrap:wrap;gap:4px}.snapshot-stamp{margin-left:0}.markdown-heading{padding-right:48px}.markdown-heading .snapshot-label{flex:1 0 100%}.markdown-heading .snapshot-stamp{margin-left:auto}.snapshot-markdown{padding:20px}}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Generated by workspace_server/frontend/build.mjs. See content_views.LICENSE.txt.
|
|
2
|
+
function j(l){function t(n){let r=n.lastIndexOf("/");return r<0?".":n.slice(0,r)||"/"}let e=new Map,i=l.map(n=>{let o=(n.kind==="diff"?n.title.split(" <-> "):[n.title]).map(c=>{let h=/^(?:([0-9a-f]{40}|[0-9a-f]{64})|.+ \(([0-9a-f]{40}|[0-9a-f]{64})\)):(.+)$/i.exec(c),u=h?h[3]:c,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:o}}),s=new Map;for(let n of e.values()){if(n.size<2)continue;let r=Array.from(n),o=r.map(c=>t(c).split("/"));r.forEach((c,h)=>{let u=1;for(;u<o[h].length&&o.some((p,S)=>S!==h&&p.slice(-u).join("/")===o[h].slice(-u).join("/"));)u+=1;s.set(c,o[h].slice(-u).join("/")||"/")})}return new Map(i.map(({view:n,sources:r})=>{let o=r.every(p=>p.path===r[0].path),c=o?r[0].name:r.map(p=>p.name).join(" \u2194 "),h="";o?h=s.get(r[0].path)||"":r.some(p=>s.has(p.path))&&(h=r.map(p=>s.get(p.path)||t(p.path)).join(" \u2194 "));let u=r.some(p=>p.revision)?r.map(p=>p.revision||"working tree").join(" \u2194 "):n.kind==="diff"&&o?"diff":"";return[n.id,{name:c,description:[h,u].filter(Boolean).join(" \xB7 ")}]}))}var E=class l{constructor(t,e,i,s,n,r=null){this.element=t,this.changed=e,this.submit=i,this.editContext=s,this.error=n,this.recallInput=r,this.recalledParts=null,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 c=o.target.closest(".context-token");c&&this.editContext(JSON.parse(c.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 c=document.caretRangeFromPoint(o.clientX,o.clientY);if(c&&t.contains(c.startContainer)){let h=window.getSelection();h.removeAllRanges(),h.addRange(c),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 r of t){let o=l.size([r]);e<n+o&&i>n&&s.push(r.type==="context"?{...r}:{type:"text",text:r.text.slice(Math.max(0,e-n),i-n)}),n+=o}return s}parts(t=this.element){let e=[],i=n=>{if(!n)return;let r=e.at(-1);(r==null?void 0:r.type)==="text"?r.text+=n:e.push({type:"text",text:n})},s=n=>{var r;for(let o of n.childNodes)o.nodeType===Node.TEXT_NODE?i(o.data):(r=o.dataset)!=null&&r.context?e.push(JSON.parse(o.dataset.context)):o.nodeName==="BR"?!o.hasAttribute("data-tail")&&n.childNodes.length>1&&i(`
|
|
3
|
+
`):(["DIV","P"].includes(o.nodeName)&&e.length&&!e.at(-1).text.endsWith(`
|
|
4
|
+
`)&&i(`
|
|
5
|
+
`),s(o))};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=l.size(this.parts(i.cloneContents()));return this.selection={start:s,end:s+l.size(this.parts(e.cloneContents()))},!0}read(){return this.captureSelection(),{parts:this.parts(),selection:{...this.selection}}}set(t){this.recalledParts=null,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
|
+
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=l.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=r=>{var o;for(let c=0;c<r.childNodes.length;c++){let h=r.childNodes[c];if(h.nodeType===Node.TEXT_NODE){if(s<=h.length)return[h,s];s-=h.length}else if((o=h.dataset)!=null&&o.context){if(s===0)return[r,c];if(s-=1,s===0)return[r,c+1]}else if(h.nodeName==="BR"){if(s===0)return[r,c];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=[...l.slice(s.parts,0,t),...i,...l.slice(s.parts,e,l.size(s.parts))],r=t+l.size(i);this.render({parts:n,selection:{start:r,end:r}}),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+=l.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(this.recallInput&&["ArrowUp","ArrowDown"].includes(t.key)&&!e&&!t.altKey&&!t.shiftKey){let s=this.read();if(s.selection.start===s.selection.end){let n=s.selection.start,r=l.text(s.parts),o=t.key==="ArrowUp",c=l.text(l.slice(s.parts,0,n)),h=l.text(l.slice(s.parts,n,l.size(s.parts)));if(this.recalledParts===JSON.stringify(s.parts)||!r||!(o?c:h).includes(`
|
|
8
|
+
`)){let p=this.recallInput(o,s);p&&(t.preventDefault(),this.render(p),this.recalledParts=JSON.stringify(p.parts),this.focus(),this.record(),this.changed())}}}else 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:`
|
|
9
|
+
`}])):this.submit();else if(["Backspace","Delete"].includes(t.key)&&!e&&!t.altKey){this.captureSelection();let{start:s,end:n}=this.selection,r=t.key==="Backspace"?s-1:s;s===n&&((i=l.slice(this.parts(),r,r+1)[0])==null?void 0:i.type)==="context"&&(t.preventDefault(),this.replace(r,r+1,[]))}}copy(t,e){if(!this.captureSelection()||this.selection.start===this.selection.end)return;let i=l.slice(this.parts(),this.selection.start,this.selection.end);t.clipboardData.setData("text/plain",l.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 W(l){let t=l.trim().match(/^<(clock_tick|exec_command_completed)>\s*([\s\S]*?)\s*<\/\1>$/);if(!t)return!1;try{let e=JSON.parse(t[2]);return e!==null&&typeof e=="object"&&!Array.isArray(e)}catch(e){return!1}}var D=class{constructor(){this.entries=[],this.seen=new Set,this.pending=[],this.index=null,this.draft=null}remember(t){let e=t.parts.map(i=>({...i}));E.text(e).trim()&&(this.append({parts:e}),this.pending.push(E.text(e).trim()),this.pending.length>100&&this.pending.shift(),this.reset())}append(t){this.entries.push(t),this.entries.length>100&&(this.entries.shift(),this.index!==null&&(this.index=Math.max(0,this.index-1)))}update(t){for(let e of t||[]){let i=e.submission_id||e.turn_id;if(!i||this.seen.has(i))continue;this.seen.add(i);let s=e.prompt;if(!s||!s.trim())continue;let n=this.pending.indexOf(s.trim());if(n!==-1){this.pending.splice(n,1);continue}e.kind==="compaction"||W(s)||!["web","cli","feishu","resume","user"].includes(e.sender)||this.append({parts:[{type:"text",text:s}]})}}reset(){this.index=null,this.draft=null}recall(t,e){if(this.index!==null&&JSON.stringify(e.parts)!==JSON.stringify(this.entries[this.index].parts)&&this.reset(),t){if(!this.entries.length||this.index===0)return null;this.index===null&&(this.draft=e,this.index=this.entries.length),this.index-=1}else{if(this.index===null)return null;if(this.index+=1,this.index>=this.entries.length){let i=this.draft;return this.reset(),i}}return{parts:this.entries[this.index].parts.map(i=>({...i}))}}};function Q(l,t=!0){if(typeof l!="string"||!l||l!==l.trim()||/[\x00-\x1f\x7f]/.test(l))return!1;if(/^(?:diff:|git:|workspace:\/\/canvas\/?$)/i.test(l))return!0;if(!t||/^(?:https?|ftps?|mailto|tel|sms|javascript|vbscript|data|blob|about|file):/i.test(l))return!1;let e=l.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(r=>r.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,i){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 s=new AbortController;this.openRequest=s,this.mutation+=1,this.error="",this.onChange(this);try{let n=await this.request("api/views/open",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({href:t,src_path:e,session_id:i}),signal:s.signal});if(this.openRequest!==s)return;this.mutation+=1,this.updateViews(n.views);let r=n.view;return this.cache.set(r.id,r),this.initialized=!0,this.openRequest=null,this.select(r.id,r.selection?{...r.selection,revision:r.reveal_revision,contentRevision:r.revision}:null,r.selection?"source":"preview")}catch(n){if(this.openRequest!==s||n.name==="AbortError")return;throw this.error="Unable to open view: "+n.message,n}finally{this.openRequest===s&&(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)}setDisplayMode(t){!this.snapshot||this.activeId===null||this.activeId==="board"||!["source","preview"].includes(t)||(this.cancelOpen(),this.selection=null,this.displayMode=t,this.onChange(this))}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 g(l,t,e,i){let s=l.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=g(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=`(() => {
|
|
10
|
+
const generation = ${i};
|
|
11
|
+
let restored = false;
|
|
12
|
+
function report() {
|
|
13
|
+
if (restored) parent.postMessage({
|
|
14
|
+
type: "workspace-preview-scroll", generation, position: [scrollX, scrollY],
|
|
15
|
+
}, "*");
|
|
16
|
+
}
|
|
17
|
+
addEventListener("message", event => {
|
|
18
|
+
const message = event.data;
|
|
19
|
+
if (event.source !== parent || !message ||
|
|
20
|
+
message.type !== "workspace-preview-restore" || message.generation !== generation) return;
|
|
21
|
+
scrollTo(message.position[0], message.position[1]);
|
|
22
|
+
restored = true;
|
|
23
|
+
report();
|
|
24
|
+
});
|
|
25
|
+
addEventListener("scroll", report, {passive: true});
|
|
26
|
+
addEventListener("load", () => parent.postMessage({
|
|
27
|
+
type: "workspace-preview-ready", generation,
|
|
28
|
+
}, "*"));
|
|
29
|
+
})();`,e.head.prepend(s);let n=e.doctype?new this.window.XMLSerializer().serializeToString(e.doctype)+`
|
|
30
|
+
`:"";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,onChangeMode:n=null,createHtmlPreview:r=(o,c)=>new P(o,c)}){this.content=t,this.document=t.ownerDocument,this.renderMarkdown=e,this.onInsertContext=s,this.onChangeMode=n,this.loadEditor=i||(()=>this.loadEditorAssets()),this.editorAssets=null,this.createHtmlPreview=r,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.insertBefore(t,this.document.head.querySelector("[data-pycodex-custom-style]")),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(g(this.document,"div","content-placeholder",t))}addModeToggle(t,e,i=null){if(!this.onChangeMode)return;let s=g(this.document,"button","content-mode-toggle",e?"Show preview":"Show source");s.type="button",s.title=e?"Show rendered Markdown":"Show Markdown source",s.setAttribute("aria-label",s.textContent),s.addEventListener("click",()=>this.onChangeMode(e?"preview":"source")),t.insertBefore(s,i)}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],r=e.kind==="file"&&!this.sourceViews.has(t),o=r&&/\.(md|markdown)$/i.test(n.label)&&this.renderMarkdown,c=r&&/\.html?$/i.test(n.label);if(this.activeId===t&&this.htmlPreview&&c)return this.htmlPreview.update(n.text);if(this.activeId===t&&this.editor&&!o&&!c){let w=this.generation;try{await this.editor.update(e),w===this.generation&&await this.applySelection(t,i)}catch(v){if(w===this.generation)throw v}return}this.clear(),this.activeId=t;let h=this.generation;if(e.kind==="diff"||!n.image_url&&!o&&!c){this.content.classList.add("code-view"),this.content.appendChild(g(this.document,"div","content-placeholder","Loading code viewer\u2026"));try{let w=await this.loadEditor();if(h!==this.generation)return;this.content.textContent="";let v=g(this.document,"div","workspace-editor");this.content.appendChild(v);let m=this.onInsertContext&&(x=>{h===this.generation&&this.activeId===t&&this.onInsertContext(x)}),I=await w.createSnapshotEditor(v,e,this.editorStates.get(t),m);if(h!==this.generation){I.destroy();return}if(this.editor=I,this.onChangeMode&&this.renderMarkdown&&e.kind==="file"&&/\.(md|markdown)$/i.test(n.label)){let x=v.querySelector(".editor-toolbar"),a=x.querySelector(".editor-actions");x.insertBefore(g(this.document,"span","snapshot-stamp","Read only"),a),this.addModeToggle(x,!0,a)}}catch(w){if(h!==this.generation)return;this.content.textContent="",this.content.appendChild(g(this.document,"div","content-placeholder","Unable to load code viewer: "+w.message));return}await this.applySelection(t,i);return}if(c)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 w=g(this.document,"div","snapshot"),v=g(this.document,"div","snapshot-heading");if(v.appendChild(g(this.document,"span","snapshot-label",n.label)),v.appendChild(g(this.document,"span","snapshot-stamp","Read only")),o&&this.onChangeMode&&(v.classList.add("markdown-heading"),this.addModeToggle(v,!1)),w.appendChild(v),n.image_url){let m=g(this.document,"img","snapshot-image");m.alt=n.label,w.appendChild(m)}else w.appendChild(g(this.document,"div","snapshot-markdown markdown"));this.previews.set(t,w)}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),o){let w=this.content.querySelector(".snapshot-markdown");n.source&&w.setAttribute("data-src-path",n.source.path),this.renderMarkdown(w,n.text)}let[p,S]=this.scrollPositions.get(t)||[0,0];this.content.scrollLeft=p,this.content.scrollTop=S}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 Z({tabs:l,frame:t,content:e,status:i,request:s,renderMarkdown:n,onInsertContext:r,enableBoard:o=!0}){let c=l.ownerDocument,h=new T({content:e,renderMarkdown:n,onInsertContext:r,onChangeMode:a=>m.setDisplayMode(a)}),u,p=null,S=!1,w=null,v=null,m;function I(a){let k=c.getElementById("content-tab-"+a);k&&(k.focus({preventScroll:!0}),k.scrollIntoView({block:"nearest",inline:"nearest"}))}function x(a){let k=a.snapshot?a.snapshot.documents.map(d=>d.error):[];i.textContent=[a.error,...k].filter(Boolean).join("; ")||(a.openRequest?"Opening view\u2026":""),i.title=i.textContent,i.hidden=!i.textContent;let R=new Map(Array.from(l.children,d=>[d.dataset.viewId,d])),$=(o?[{id:"board",title:"Canvas",kind:"board"}]:[]).concat(a.views),z=j(a.views);z.set("board",{name:"Canvas",description:""}),$.forEach((d,q)=>{let f=R.get(d.id);if(!f){f=g(c,"div","tab content-tab"),f.dataset.viewId=d.id,f.id="content-tab-"+d.id,f.setAttribute("role","tab"),f.setAttribute("aria-controls",d.id==="board"?t.id:e.id);let N=g(c,"span","tab-label content-tab-label");if(N.appendChild(g(c,"span","content-tab-name")),N.appendChild(g(c,"span","content-tab-description")),f.appendChild(N),f.addEventListener("click",()=>m.select(d.id)),f.addEventListener("keydown",y=>{if(y.target!==f)return;let b=(o?["board"]:[]).concat(m.views.map(K=>K.id)),V=b.indexOf(d.id),C=null;y.key==="ArrowRight"&&(C=b[(V+1)%b.length]),y.key==="ArrowLeft"&&(C=b[(V+b.length-1)%b.length]),y.key==="Home"&&(C=b[0]),y.key==="End"&&(C=b[b.length-1]),(y.key==="Enter"||y.key===" ")&&(C=d.id),y.key==="Delete"&&d.id!=="board"&&(y.preventDefault(),m.close(d.id)),C!==null&&(y.preventDefault(),m.select(C),c.getElementById("content-tab-"+C).focus())}),d.id!=="board"){let y=g(c,"button","tab-close content-tab-close");y.type="button",y.title="Close tab",y.setAttribute("aria-label","Close "+d.title),y.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>',y.addEventListener("click",b=>{b.stopPropagation(),m.close(d.id)}),f.appendChild(y)}}R.delete(d.id);let A=z.get(d.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"+(d.id===a.activeId?" active":""),f.tabIndex=d.id===a.activeId?0:-1,f.title=d.id==="board"?l.dataset.boardTitle:d.title+(d.live?`
|
|
31
|
+
Read only \xB7 follows file changes \xB7 `:`
|
|
32
|
+
Read-only snapshot \xB7 `)+new Date(d.created_at).toLocaleString(),f.setAttribute("aria-selected",String(d.id===a.activeId)),f.setAttribute("aria-label",d.title);let M=f.querySelector(".content-tab-close");M&&(M.disabled=a.closing.has(d.id),M.setAttribute("aria-label","Close "+d.title)),l.children[q]!==f&&l.insertBefore(f,l.children[q]||null)}),R.forEach(d=>d.remove()),h.retain(a.views.map(d=>d.id));let L=a.activeId==="board",B=u!==a.activeId,J=B||p!==a.snapshot||S!==a.loading||w!==a.selection||v!==a.displayMode;if(L&&J&&h.clear(),t.hidden=!L,e.hidden=L,!!J&&(u=a.activeId,p=a.snapshot,S=a.loading,w=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)),!L)){if(a.snapshot){let d=a.activeId,q=a.snapshot;h.show(d,q,a.selection,a.displayMode).catch(f=>{u!==d||p!==q||(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.");B&&I(a.activeId)}}return m=new O({request:s,enableBoard:o,onChange:x,onSelect:a=>{e.closest(".workspace").dataset.mobilePane="board",I(a)}}),x(m),{start(){m.start(),x(m)},stop(){m.stop(),h.clear(),u=null},poll(){return m.poll()},open(a,k,R){return m.open(a,k,R)}}}export{E as Composer,O as ContentTabs,D as PromptHistory,T as SnapshotDisplay,Q as isWorkspaceLink,Z as mountContentViews};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/** A plain-text editor whose context tokens occupy one cursor position each. */
|
|
2
2
|
export class Composer {
|
|
3
|
-
constructor(element, changed, submit, editContext, error) {
|
|
3
|
+
constructor(element, changed, submit, editContext, error, recallInput = null) {
|
|
4
4
|
this.element = element;
|
|
5
5
|
this.changed = changed;
|
|
6
6
|
this.submit = submit;
|
|
7
7
|
this.editContext = editContext;
|
|
8
8
|
this.error = error;
|
|
9
|
+
this.recallInput = recallInput;
|
|
10
|
+
this.recalledParts = null;
|
|
9
11
|
this.composing = false;
|
|
10
12
|
this.selection = { start: 0, end: 0 };
|
|
11
13
|
this.undo = [];
|
|
@@ -124,6 +126,7 @@ export class Composer {
|
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
set(value) {
|
|
129
|
+
this.recalledParts = null;
|
|
127
130
|
this.undo = [];
|
|
128
131
|
this.redo = [];
|
|
129
132
|
this.render(value);
|
|
@@ -256,7 +259,30 @@ export class Composer {
|
|
|
256
259
|
keydown(event) {
|
|
257
260
|
if (event.isComposing || this.composing || event.keyCode === 229) return;
|
|
258
261
|
const modifier = event.ctrlKey || event.metaKey;
|
|
259
|
-
if (
|
|
262
|
+
if (this.recallInput && ["ArrowUp", "ArrowDown"].includes(event.key) &&
|
|
263
|
+
!modifier && !event.altKey && !event.shiftKey) {
|
|
264
|
+
const current = this.read();
|
|
265
|
+
if (current.selection.start === current.selection.end) {
|
|
266
|
+
const offset = current.selection.start;
|
|
267
|
+
const text = Composer.text(current.parts);
|
|
268
|
+
// Keep vertical cursor movement inside multiline drafts.
|
|
269
|
+
const back = event.key === "ArrowUp";
|
|
270
|
+
const prefix = Composer.text(Composer.slice(current.parts, 0, offset));
|
|
271
|
+
const suffix = Composer.text(Composer.slice(current.parts, offset, Composer.size(current.parts)));
|
|
272
|
+
const browsing = this.recalledParts === JSON.stringify(current.parts);
|
|
273
|
+
if (browsing || !text || !(back ? prefix : suffix).includes("\n")) {
|
|
274
|
+
const recalled = this.recallInput(back, current);
|
|
275
|
+
if (recalled) {
|
|
276
|
+
event.preventDefault();
|
|
277
|
+
this.render(recalled);
|
|
278
|
+
this.recalledParts = JSON.stringify(recalled.parts);
|
|
279
|
+
this.focus();
|
|
280
|
+
this.record();
|
|
281
|
+
this.changed();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
} else if (modifier && (event.key.toLowerCase() === "z" || event.key.toLowerCase() === "y")) {
|
|
260
286
|
event.preventDefault();
|
|
261
287
|
this.history(event.key.toLowerCase() === "z" && !event.shiftKey);
|
|
262
288
|
} else if (event.key === "Enter") {
|
|
@@ -41,6 +41,19 @@
|
|
|
41
41
|
overflow: hidden;
|
|
42
42
|
text-overflow: ellipsis;
|
|
43
43
|
}
|
|
44
|
+
.content-mode-toggle {
|
|
45
|
+
flex: 0 0 auto;
|
|
46
|
+
padding: 4px 8px;
|
|
47
|
+
border: 0;
|
|
48
|
+
border-radius: var(--radius-sm);
|
|
49
|
+
background: transparent;
|
|
50
|
+
color: var(--accent);
|
|
51
|
+
font-size: 11px;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
}
|
|
55
|
+
.content-mode-toggle:hover { background: var(--accent-soft); }
|
|
56
|
+
.content-mode-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
44
57
|
.content-area { position: relative; min-width: 0; min-height: 0; overflow: hidden; }
|
|
45
58
|
.content-area > [hidden] { display: none; }
|
|
46
59
|
.content-view {
|
|
@@ -56,13 +69,21 @@
|
|
|
56
69
|
.snapshot { min-height: 100%; }
|
|
57
70
|
.snapshot-heading {
|
|
58
71
|
display: flex;
|
|
59
|
-
align-items:
|
|
60
|
-
gap:
|
|
61
|
-
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 8px;
|
|
74
|
+
min-height: 42px;
|
|
75
|
+
padding: 6px 16px;
|
|
62
76
|
border-bottom: 1px solid var(--line);
|
|
63
77
|
font-size: 12px;
|
|
64
78
|
}
|
|
79
|
+
.markdown-heading {
|
|
80
|
+
position: sticky;
|
|
81
|
+
top: 0;
|
|
82
|
+
z-index: 1;
|
|
83
|
+
background: var(--board-chrome);
|
|
84
|
+
}
|
|
65
85
|
.snapshot-label {
|
|
86
|
+
min-width: 0;
|
|
66
87
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
67
88
|
overflow-wrap: anywhere;
|
|
68
89
|
}
|
|
@@ -79,5 +100,8 @@
|
|
|
79
100
|
@media (max-width: 760px) {
|
|
80
101
|
.snapshot-heading { flex-wrap: wrap; gap: 4px; }
|
|
81
102
|
.snapshot-stamp { margin-left: 0; }
|
|
103
|
+
.markdown-heading { padding-right: 48px; }
|
|
104
|
+
.markdown-heading .snapshot-label { flex: 1 0 100%; }
|
|
105
|
+
.markdown-heading .snapshot-stamp { margin-left: auto; }
|
|
82
106
|
.snapshot-markdown { padding: 20px; }
|
|
83
107
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Workspace content tabs, live updates and editor lifecycle.
|
|
2
2
|
import {tabLabels} from "./view_labels.mjs";
|
|
3
3
|
export {Composer} from "./composer.mjs";
|
|
4
|
+
export {PromptHistory} from "./prompt_history.mjs";
|
|
4
5
|
|
|
5
6
|
export function isWorkspaceLink(href, filePaths = true) {
|
|
6
7
|
if (typeof href !== "string" || !href || href !== href.trim() || /[\x00-\x1f\x7f]/.test(href)) {
|
|
@@ -170,6 +171,15 @@ export class ContentTabs {
|
|
|
170
171
|
await this.load(id);
|
|
171
172
|
}
|
|
172
173
|
|
|
174
|
+
setDisplayMode(displayMode) {
|
|
175
|
+
if (!this.snapshot || this.activeId === null || this.activeId === "board" ||
|
|
176
|
+
!["source", "preview"].includes(displayMode)) return;
|
|
177
|
+
this.cancelOpen();
|
|
178
|
+
this.selection = null;
|
|
179
|
+
this.displayMode = displayMode;
|
|
180
|
+
this.onChange(this);
|
|
181
|
+
}
|
|
182
|
+
|
|
173
183
|
async load(id) {
|
|
174
184
|
const request = new AbortController();
|
|
175
185
|
this.contentRequest = request;
|
|
@@ -296,12 +306,13 @@ class HtmlPreview {
|
|
|
296
306
|
}
|
|
297
307
|
|
|
298
308
|
export class SnapshotDisplay {
|
|
299
|
-
constructor({content, renderMarkdown, loadEditor = null, onInsertContext = null,
|
|
309
|
+
constructor({content, renderMarkdown, loadEditor = null, onInsertContext = null, onChangeMode = null,
|
|
300
310
|
createHtmlPreview = (parent, position) => new HtmlPreview(parent, position)}) {
|
|
301
311
|
this.content = content;
|
|
302
312
|
this.document = content.ownerDocument;
|
|
303
313
|
this.renderMarkdown = renderMarkdown;
|
|
304
314
|
this.onInsertContext = onInsertContext;
|
|
315
|
+
this.onChangeMode = onChangeMode;
|
|
305
316
|
this.loadEditor = loadEditor || (() => this.loadEditorAssets());
|
|
306
317
|
this.editorAssets = null;
|
|
307
318
|
this.createHtmlPreview = createHtmlPreview;
|
|
@@ -377,6 +388,17 @@ export class SnapshotDisplay {
|
|
|
377
388
|
}
|
|
378
389
|
}
|
|
379
390
|
|
|
391
|
+
addModeToggle(parent, sourceMode, before = null) {
|
|
392
|
+
if (!this.onChangeMode) return;
|
|
393
|
+
const button = element(this.document, "button", "content-mode-toggle",
|
|
394
|
+
sourceMode ? "Show preview" : "Show source");
|
|
395
|
+
button.type = "button";
|
|
396
|
+
button.title = sourceMode ? "Show rendered Markdown" : "Show Markdown source";
|
|
397
|
+
button.setAttribute("aria-label", button.textContent);
|
|
398
|
+
button.addEventListener("click", () => this.onChangeMode(sourceMode ? "preview" : "source"));
|
|
399
|
+
parent.insertBefore(button, before);
|
|
400
|
+
}
|
|
401
|
+
|
|
380
402
|
async show(id, snapshot, selection = null, displayMode = null) {
|
|
381
403
|
if (selection && snapshot.revision < selection.contentRevision) selection = null;
|
|
382
404
|
this.pendingSelection = selection;
|
|
@@ -422,6 +444,13 @@ export class SnapshotDisplay {
|
|
|
422
444
|
return;
|
|
423
445
|
}
|
|
424
446
|
this.editor = editor;
|
|
447
|
+
if (this.onChangeMode && this.renderMarkdown && snapshot.kind === "file" &&
|
|
448
|
+
/\.(md|markdown)$/i.test(source.label)) {
|
|
449
|
+
const toolbar = parent.querySelector(".editor-toolbar");
|
|
450
|
+
const actions = toolbar.querySelector(".editor-actions");
|
|
451
|
+
toolbar.insertBefore(element(this.document, "span", "snapshot-stamp", "Read only"), actions);
|
|
452
|
+
this.addModeToggle(toolbar, true, actions);
|
|
453
|
+
}
|
|
425
454
|
} catch (error) {
|
|
426
455
|
if (generation !== this.generation) return;
|
|
427
456
|
this.content.textContent = "";
|
|
@@ -444,6 +473,10 @@ export class SnapshotDisplay {
|
|
|
444
473
|
const heading = element(this.document, "div", "snapshot-heading");
|
|
445
474
|
heading.appendChild(element(this.document, "span", "snapshot-label", source.label));
|
|
446
475
|
heading.appendChild(element(this.document, "span", "snapshot-stamp", "Read only"));
|
|
476
|
+
if (markdown && this.onChangeMode) {
|
|
477
|
+
heading.classList.add("markdown-heading");
|
|
478
|
+
this.addModeToggle(heading, false);
|
|
479
|
+
}
|
|
447
480
|
root.appendChild(heading);
|
|
448
481
|
if (source.image_url) {
|
|
449
482
|
const image = element(this.document, "img", "snapshot-image");
|
|
@@ -484,7 +517,10 @@ export function mountContentViews({
|
|
|
484
517
|
enableBoard = true,
|
|
485
518
|
}) {
|
|
486
519
|
const document = tabs.ownerDocument;
|
|
487
|
-
const display = new SnapshotDisplay({
|
|
520
|
+
const display = new SnapshotDisplay({
|
|
521
|
+
content, renderMarkdown, onInsertContext,
|
|
522
|
+
onChangeMode: mode => controller.setDisplayMode(mode),
|
|
523
|
+
});
|
|
488
524
|
let displayedId;
|
|
489
525
|
let displayedSnapshot = null;
|
|
490
526
|
let displayedLoading = false;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {Composer} from "./composer.mjs";
|
|
2
|
+
|
|
3
|
+
function automaticPrompt(text) {
|
|
4
|
+
const match = text.trim().match(/^<(clock_tick|exec_command_completed)>\s*([\s\S]*?)\s*<\/\1>$/);
|
|
5
|
+
if (!match) return false;
|
|
6
|
+
try {
|
|
7
|
+
const payload = JSON.parse(match[2]);
|
|
8
|
+
return payload !== null && typeof payload === "object" && !Array.isArray(payload);
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Human input history belongs to one conversation and never submits on recall. */
|
|
15
|
+
export class PromptHistory {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.entries = [];
|
|
18
|
+
this.seen = new Set();
|
|
19
|
+
this.pending = [];
|
|
20
|
+
this.index = null;
|
|
21
|
+
this.draft = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
remember(value) {
|
|
25
|
+
const parts = value.parts.map(part => ({...part}));
|
|
26
|
+
if (!Composer.text(parts).trim()) return;
|
|
27
|
+
this.append({parts});
|
|
28
|
+
this.pending.push(Composer.text(parts).trim());
|
|
29
|
+
if (this.pending.length > 100) this.pending.shift();
|
|
30
|
+
this.reset();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
append(value) {
|
|
34
|
+
this.entries.push(value);
|
|
35
|
+
if (this.entries.length > 100) {
|
|
36
|
+
this.entries.shift();
|
|
37
|
+
if (this.index !== null) this.index = Math.max(0, this.index - 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
update(turns) {
|
|
42
|
+
for (const turn of turns || []) {
|
|
43
|
+
const id = turn.submission_id || turn.turn_id;
|
|
44
|
+
if (!id || this.seen.has(id)) continue;
|
|
45
|
+
this.seen.add(id);
|
|
46
|
+
const text = turn.prompt;
|
|
47
|
+
if (!text || !text.trim()) continue;
|
|
48
|
+
const pending = this.pending.indexOf(text.trim());
|
|
49
|
+
if (pending !== -1) {
|
|
50
|
+
this.pending.splice(pending, 1);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (turn.kind === "compaction" || automaticPrompt(text) ||
|
|
54
|
+
!["web", "cli", "feishu", "resume", "user"].includes(turn.sender)) continue;
|
|
55
|
+
this.append({parts: [{type: "text", text}]});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
reset() {
|
|
60
|
+
this.index = null;
|
|
61
|
+
this.draft = null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
recall(back, current) {
|
|
65
|
+
if (this.index !== null &&
|
|
66
|
+
JSON.stringify(current.parts) !== JSON.stringify(this.entries[this.index].parts)) this.reset();
|
|
67
|
+
if (back) {
|
|
68
|
+
if (!this.entries.length || this.index === 0) return null;
|
|
69
|
+
if (this.index === null) {
|
|
70
|
+
this.draft = current;
|
|
71
|
+
this.index = this.entries.length;
|
|
72
|
+
}
|
|
73
|
+
this.index -= 1;
|
|
74
|
+
} else {
|
|
75
|
+
if (this.index === null) return null;
|
|
76
|
+
this.index += 1;
|
|
77
|
+
if (this.index >= this.entries.length) {
|
|
78
|
+
const draft = this.draft;
|
|
79
|
+
this.reset();
|
|
80
|
+
return draft;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return {parts: this.entries[this.index].parts.map(part => ({...part}))};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -686,6 +686,15 @@
|
|
|
686
686
|
color: var(--muted);
|
|
687
687
|
font-size: 12px;
|
|
688
688
|
}
|
|
689
|
+
.entry.model-error {
|
|
690
|
+
border-left: 2px solid #c98100;
|
|
691
|
+
padding: 8px 10px;
|
|
692
|
+
background: color-mix(in srgb, #c98100 8%, transparent);
|
|
693
|
+
color: #7a4600;
|
|
694
|
+
font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
695
|
+
white-space: pre-wrap;
|
|
696
|
+
overflow-wrap: anywhere;
|
|
697
|
+
}
|
|
689
698
|
.entry.compaction::before,
|
|
690
699
|
.entry.compaction::after {
|
|
691
700
|
content: "";
|
|
@@ -1331,7 +1340,7 @@
|
|
|
1331
1340
|
</div>
|
|
1332
1341
|
<div id="prompt" class="prompt-input empty" contenteditable="true" role="textbox"
|
|
1333
1342
|
aria-multiline="true" aria-label="Message" spellcheck="false"
|
|
1334
|
-
title="Enter to send · Shift + Enter for a new line" data-placeholder="Message…"></div>
|
|
1343
|
+
title="Enter to send · Shift + Enter for a new line · ↑ / ↓ for input history" data-placeholder="Message…"></div>
|
|
1335
1344
|
</div>
|
|
1336
1345
|
<div id="contextBlockDetail" class="context-block-detail" hidden>
|
|
1337
1346
|
<div class="context-block-heading">
|
|
@@ -1414,7 +1423,7 @@
|
|
|
1414
1423
|
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js"></script>
|
|
1415
1424
|
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/contrib/auto-render.min.js"></script>
|
|
1416
1425
|
<script type="module">
|
|
1417
|
-
import {Composer, isWorkspaceLink, mountContentViews} from "./assets/content_views.mjs";
|
|
1426
|
+
import {Composer, PromptHistory, isWorkspaceLink, mountContentViews} from "./assets/content_views.mjs";
|
|
1418
1427
|
|
|
1419
1428
|
const log = document.getElementById("log");
|
|
1420
1429
|
const spinner = document.getElementById("spinner");
|
|
@@ -1467,7 +1476,9 @@
|
|
|
1467
1476
|
const composer = new Composer(prompt, () => {
|
|
1468
1477
|
stateForSession(activeSessionId).draft = composer.read();
|
|
1469
1478
|
resizePrompt();
|
|
1470
|
-
}, () => form.requestSubmit(), editContextBlock, showToast
|
|
1479
|
+
}, () => form.requestSubmit(), editContextBlock, showToast,
|
|
1480
|
+
(back, current) => stateForSession(activeSessionId).promptHistory.recall(back, current));
|
|
1481
|
+
prompt.addEventListener("input", () => stateForSession(activeSessionId).promptHistory.reset());
|
|
1471
1482
|
composer.set({parts: []});
|
|
1472
1483
|
const markdownRenderer = window.markdownit
|
|
1473
1484
|
? window.markdownit({html: false, linkify: true, breaks: false})
|
|
@@ -2114,6 +2125,7 @@
|
|
|
2114
2125
|
lastRenderedSignature: "",
|
|
2115
2126
|
scrollTop: null,
|
|
2116
2127
|
draft: {parts: []},
|
|
2128
|
+
promptHistory: new PromptHistory(),
|
|
2117
2129
|
notifyWhenDone: false,
|
|
2118
2130
|
wasRunning: false,
|
|
2119
2131
|
inputRequest: null,
|
|
@@ -2368,6 +2380,7 @@
|
|
|
2368
2380
|
item.status || "",
|
|
2369
2381
|
item.error || "",
|
|
2370
2382
|
item.kind || "",
|
|
2383
|
+
item.model_errors || [],
|
|
2371
2384
|
]),
|
|
2372
2385
|
snapshot.input_request || null,
|
|
2373
2386
|
]);
|
|
@@ -2468,6 +2481,7 @@
|
|
|
2468
2481
|
updateSessionDirectory(snapshot);
|
|
2469
2482
|
const state = stateForSession(activeSessionId);
|
|
2470
2483
|
state.inputRequest = snapshot.input_request || null;
|
|
2484
|
+
state.promptHistory.update(snapshot.turns);
|
|
2471
2485
|
renderQueueHint(snapshot.queued_inputs);
|
|
2472
2486
|
requestAnimationFrame(markVisibleFinalRead);
|
|
2473
2487
|
const signature = messageSignature(snapshot);
|
|
@@ -2594,6 +2608,11 @@
|
|
|
2594
2608
|
response.dataset.sessionId = activeSessionId;
|
|
2595
2609
|
}
|
|
2596
2610
|
if (turn.thinking) renderEntry(`${key}:thinking`, turn.thinking, "thinking");
|
|
2611
|
+
if (Array.isArray(turn.model_errors)) {
|
|
2612
|
+
turn.model_errors.forEach((message, index) => {
|
|
2613
|
+
renderEntry(`${key}:model-error:${index}`, message, "model-error");
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2597
2616
|
if (turn.error) {
|
|
2598
2617
|
const error = renderEntry(`${key}:error`, turn.error, "error");
|
|
2599
2618
|
error.dataset.finalId = isControl ? "" : (turn.final_id || "");
|
|
@@ -2866,8 +2885,10 @@
|
|
|
2866
2885
|
|
|
2867
2886
|
form.addEventListener("submit", (event) => {
|
|
2868
2887
|
event.preventDefault();
|
|
2869
|
-
const
|
|
2870
|
-
|
|
2888
|
+
const draft = composer.read();
|
|
2889
|
+
const text = Composer.text(draft.parts);
|
|
2890
|
+
if (!text.trim() && !stateForSession(activeSessionId).inputRequest) return;
|
|
2891
|
+
stateForSession(activeSessionId).promptHistory.remember(draft);
|
|
2871
2892
|
closeContextBlock();
|
|
2872
2893
|
composer.set({parts: []});
|
|
2873
2894
|
resizePrompt();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.content-tabs{flex:1}.tab.content-tab{flex:0 0 auto}.tab.content-tab:first-child{min-width:0;padding-right:12px}.content-tab:focus-visible{outline:2px solid var(--accent);outline-offset:-1px}.content-tab-label{display:flex;align-items:baseline;gap:6px}.content-tab-name{flex:0 0 auto;max-width:100%;overflow:hidden;text-overflow:ellipsis}.content-tab-description{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:11px;font-weight:400;opacity:.75}.content-status{max-width:50%;font-size:11px;color:var(--danger);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.content-area{position:relative;min-width:0;min-height:0;overflow:hidden}.content-area>[hidden]{display:none}.content-view{width:100%;height:100%;overflow:auto;background:var(--board-chrome);color:var(--ink)}.content-view.code-view,.content-view.html-view{overflow:hidden}.snapshot-html{display:block;width:100%;height:100%;border:0;background:#fff}.snapshot{min-height:100%}.snapshot-heading{display:flex;align-items:baseline;gap:20px;padding:10px 16px;border-bottom:1px solid var(--line);font-size:12px}.snapshot-label{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow-wrap:anywhere}.snapshot-stamp{flex:0 0 auto;margin-left:auto;color:var(--muted);font-size:11px;white-space:nowrap}.snapshot-markdown{max-width:900px;padding:24px 32px;margin:0 auto}.snapshot-image{display:block;max-width:calc(100% - 40px);height:auto;margin:20px auto}.content-placeholder{padding:40px 24px;text-align:center;color:var(--muted)}@media(max-width:760px){.snapshot-heading{flex-wrap:wrap;gap:4px}.snapshot-stamp{margin-left:0}.snapshot-markdown{padding:20px}}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// Generated by workspace_server/frontend/build.mjs. See content_views.LICENSE.txt.
|
|
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
|
-
`)&&i(`
|
|
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
|
-
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 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,i){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 s=new AbortController;this.openRequest=s,this.mutation+=1,this.error="",this.onChange(this);try{let n=await this.request("api/views/open",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({href:t,src_path:e,session_id:i}),signal:s.signal});if(this.openRequest!==s)return;this.mutation+=1,this.updateViews(n.views);let o=n.view;return this.cache.set(o.id,o),this.initialized=!0,this.openRequest=null,this.select(o.id,o.selection?{...o.selection,revision:o.reveal_revision,contentRevision:o.revision}:null,o.selection?"source":"preview")}catch(n){if(this.openRequest!==s||n.name==="AbortError")return;throw this.error="Unable to open view: "+n.message,n}finally{this.openRequest===s&&(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
|
-
const generation = ${i};
|
|
10
|
-
let restored = false;
|
|
11
|
-
function report() {
|
|
12
|
-
if (restored) parent.postMessage({
|
|
13
|
-
type: "workspace-preview-scroll", generation, position: [scrollX, scrollY],
|
|
14
|
-
}, "*");
|
|
15
|
-
}
|
|
16
|
-
addEventListener("message", event => {
|
|
17
|
-
const message = event.data;
|
|
18
|
-
if (event.source !== parent || !message ||
|
|
19
|
-
message.type !== "workspace-preview-restore" || message.generation !== generation) return;
|
|
20
|
-
scrollTo(message.position[0], message.position[1]);
|
|
21
|
-
restored = true;
|
|
22
|
-
report();
|
|
23
|
-
});
|
|
24
|
-
addEventListener("scroll", report, {passive: true});
|
|
25
|
-
addEventListener("load", () => parent.postMessage({
|
|
26
|
-
type: "workspace-preview-ready", generation,
|
|
27
|
-
}, "*"));
|
|
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)}},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.insertBefore(t,this.document.head.querySelector("[data-pycodex-custom-style]")),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 R=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,I)=>{let f=R.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)}}R.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
|
-
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===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[I]!==f&&c.insertBefore(f,c.children[I]||null)}),R.forEach(l=>l.remove()),h.retain(a.views.map(l=>l.id));let q=a.activeId==="board",z=u!==a.activeId,B=z||p!==a.snapshot||k!==a.loading||m!==a.selection||v!==a.displayMode;if(q&&B&&h.clear(),t.hidden=!q,e.hidden=q,!!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)),!q)){if(a.snapshot){let l=a.activeId,I=a.snapshot;h.show(l,I,a.selection,a.displayMode).catch(f=>{u!==l||p!==I||(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,R){return g.open(a,C,R)}}}export{M as Composer,N as ContentTabs,P as SnapshotDisplay,X as isWorkspaceLink,F as mountContentViews};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|