dsh-ptc-plus 0.2.4 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +177 -0
- package/README.md +85 -69
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +1168 -144
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +1 -1
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +7 -3
- package/docs/adr/0014-persist-cell-rewrite-policy.md +7 -5
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +19 -4
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +5 -3
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +2 -2
- package/docs/architecture.md +23 -14
- package/docs/client-ui.md +29 -11
- package/docs/durability-design.md +42 -20
- package/docs/installation.md +11 -3
- package/docs/program-data-plane.md +6 -0
- package/docs/runtime-reference.md +27 -14
- package/docs/value-wire.md +1 -1
- package/index.js +38 -4
- package/internal/cell-analysis.js +89 -12
- package/internal/cell-rewriter.js +162 -4
- package/internal/config-spec.js +192 -117
- package/internal/direct-surface-owner.js +56 -5
- package/internal/edit-transport-owner.js +11 -2
- package/internal/failure-reporting.js +15 -0
- package/internal/kernel-worker.js +11 -0
- package/internal/rejected-cell-editor.js +21 -12
- package/internal/repl-convenience.js +94 -6
- package/internal/repl-memory-projection.js +277 -0
- package/internal/repl-rewrite-contract.js +15 -0
- package/internal/run-code-description.js +34 -0
- package/internal/runtime-bridge-owner.js +63 -15
- package/internal/runtime-config.js +3 -0
- package/internal/session-cell-executor.js +74 -14
- package/internal/session-events.js +7 -0
- package/internal/session-journal.js +331 -63
- package/internal/session-log-view.js +2 -1
- package/internal/session-runtime.js +110 -9
- package/internal/session-state.js +116 -4
- package/package.json +10 -7
- package/src/client-activity.js +501 -0
- package/src/client.js +456 -20
- package/README.zh.md +0 -161
- /package/assets/{ptc-plus-repair.png → ptc-plus-repair-zh.png} +0 -0
package/src/client.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import { CONFIG_FIELDS, SETTINGS_NAMESPACE } from '../internal/config-spec.js'
|
|
1
|
+
import { CONFIG_FIELDS, CONFIG_GROUPS, SETTINGS_NAMESPACE } from '../internal/config-spec.js'
|
|
2
|
+
import { derivePtcToolView } from './client-activity.js'
|
|
3
|
+
import {
|
|
4
|
+
normalizeReplMemorySnapshot,
|
|
5
|
+
unavailableReplMemorySnapshot,
|
|
6
|
+
} from '../internal/repl-memory-projection.js'
|
|
2
7
|
|
|
3
8
|
const CLIENT_STYLE_ID = 'ptc-plus-client-style'
|
|
4
9
|
const CLIENT_CSS = `
|
|
@@ -7,10 +12,24 @@ const CLIENT_CSS = `
|
|
|
7
12
|
.ptcPlusHeader:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusHeader:focus-visible,.ptcPlusButton:focus-visible,.ptcPlusInput:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:-2px}
|
|
8
13
|
.ptcPlusHeadText{display:flex;flex:1;min-width:0;flex-direction:column;align-items:flex-start;gap:1px}.ptcPlusName{font-size:14px;font-weight:600;line-height:20px}.ptcPlusDescription{color:var(--dsw-alias-label-tertiary,#74777d);font-size:12px;line-height:18px;overflow-wrap:anywhere}.ptcPlusStatus{display:inline-flex;align-items:center;flex:none;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:500;line-height:16px}.ptcPlusStatus[data-enabled=true]{color:var(--dsw-alias-state-success-primary,#16794f);background:var(--dsw-alias-state-success-tertiary,#e7f7ef)}.ptcPlusStatus[data-enabled=false]{color:var(--dsw-alias-label-tertiary,#74777d);background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
|
|
9
14
|
.ptcPlusChevron{display:flex;color:var(--dsw-alias-label-tertiary,#74777d);transition:transform .18s ease}.ptcPlusChevron[data-open=true]{transform:rotate(180deg)}.ptcPlusBody{display:grid;grid-template-rows:0fr;transition:grid-template-rows .2s ease}.ptcPlusBody[data-open=true]{grid-template-rows:1fr}.ptcPlusBodyInner{min-height:0;overflow:hidden}.ptcPlusFields{margin:0 16px;padding:8px 0 12px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}
|
|
10
|
-
.ptcPlusRow{display:flex;align-items:center;gap:12px;min-height:48px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}.ptcPlusRow:first-child{border-top:0}.ptcPlusMain{flex:1;min-width:0}.ptcPlusLabel{font-size:14px;font-weight:500;line-height:20px}.ptcPlusDetail,.ptcPlusMessage{color:var(--dsw-alias-label-tertiary,#74777d);font-size:12px;line-height:18px;overflow-wrap:anywhere}.ptcPlusInput{box-sizing:border-box;min-width:72px;width:140px;padding:5px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:var(--dsw-alias-bg-layer-1,#fff);color:inherit;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace}.ptcPlusCheck{width:18px;height:18px;accent-color:var(--dsw-alias-interactive-primary,#4d6bfe)}
|
|
11
|
-
.ptcPlusFooter{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:8px}.ptcPlusButton{min-height:32px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:transparent;color:inherit;cursor:pointer;font:500 13px/20px inherit;transition:background-color .16s ease,border-color .16s ease}.ptcPlusButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusButton:disabled,.ptcPlusInput:disabled,.ptcPlusCheck:disabled{cursor:not-allowed;opacity:.55}
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
.ptcPlusGroup+.ptcPlusGroup{margin-top:14px}.ptcPlusGroupTitle{margin:0;padding:8px 0 5px;color:var(--dsw-alias-label-secondary,#52565d);font-size:11px;font-weight:600;letter-spacing:0;line-height:16px}.ptcPlusRow{display:flex;align-items:center;gap:12px;min-height:48px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}.ptcPlusRow:first-child{border-top:0}.ptcPlusMain{flex:1;min-width:0}.ptcPlusLabel{font-size:14px;font-weight:500;line-height:20px}.ptcPlusDetail,.ptcPlusMessage{color:var(--dsw-alias-label-tertiary,#74777d);font-size:12px;line-height:18px;overflow-wrap:anywhere}.ptcPlusInput{box-sizing:border-box;min-width:72px;width:140px;padding:5px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:var(--dsw-alias-bg-layer-1,#fff);color:inherit;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace}.ptcPlusCheck{width:18px;height:18px;accent-color:var(--dsw-alias-interactive-primary,#4d6bfe)}
|
|
16
|
+
.ptcPlusFooter{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:8px}.ptcPlusButton{min-height:32px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:6px;background:transparent;color:inherit;cursor:pointer;font:500 13px/20px inherit;transition:background-color .16s ease,border-color .16s ease}.ptcPlusButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusButton:disabled,.ptcPlusInput:disabled,.ptcPlusCheck:disabled{cursor:not-allowed;opacity:.55}
|
|
17
|
+
.ptcPlusActiveShell{display:inline-flex;align-items:center}.ptcPlusActive{appearance:none;display:inline-flex;height:24px;align-items:center;gap:5px;padding:0 8px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 32%,transparent);border-radius:6px;background:var(--dsw-alias-state-success-tertiary,#e7f7ef);color:var(--dsw-alias-state-success-primary,#16794f);cursor:help;font:600 12px/18px inherit;white-space:nowrap;transition:background-color .14s ease,border-color .14s ease}.ptcPlusActive:hover,.ptcPlusActive[aria-expanded=true]{border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 48%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 16%,var(--dsw-alias-bg-layer-3,#fff))}.ptcPlusActive:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary,#16794f);outline-offset:2px}.ptcPlusReplPopover{position:fixed;z-index:2147483000;inset:auto;display:none;box-sizing:border-box;margin:0;padding:0;border:0;overflow:visible;background:transparent;color:var(--dsw-alias-label-primary,#18191c)}.ptcPlusReplPopover:popover-open,.ptcPlusReplPopover[data-open=true]{display:block}.ptcPlusReplPopover::backdrop{background:transparent}.ptcPlusReplCard{display:flex;max-height:inherit;overflow:hidden;flex-direction:column;border:1px solid color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 22%,var(--dsw-alias-border-l2,rgba(0,0,0,.1)));border-top:3px solid var(--dsw-alias-state-success-primary,#16794f);border-radius:8px;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 14px 36px rgba(16,24,40,.2),0 3px 10px rgba(16,24,40,.1);color:var(--dsw-alias-label-primary,#18191c);white-space:normal}.ptcPlusReplHead{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;column-gap:8px;padding:11px 13px 10px;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 7%,var(--dsw-alias-bg-layer-3,#fff))}.ptcPlusReplStatusDot{grid-row:1/3;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#16794f);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-state-success-primary,#16794f) 14%,transparent)}.ptcPlusReplTitle,.ptcPlusReplSummary{display:block;min-width:0}.ptcPlusReplTitle{font-size:13px;font-weight:600;line-height:19px}.ptcPlusReplSummary{color:var(--dsw-alias-label-tertiary,#74777d);font-size:11px;line-height:16px}.ptcPlusReplList{min-height:0;margin:0;padding:5px 0;overflow:auto;overscroll-behavior:contain;list-style:none;scrollbar-gutter:stable}.ptcPlusReplBinding{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:4px 10px;padding:7px 12px}.ptcPlusReplBinding:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusReplIdentity{display:flex;min-width:0;align-items:center;gap:7px}.ptcPlusReplName{min-width:0;overflow:hidden;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.ptcPlusReplKind{flex:none;padding:1px 6px;border:1px solid color-mix(in srgb,currentColor 22%,transparent);border-radius:999px;background:color-mix(in srgb,currentColor 10%,transparent);font-size:10px;font-weight:600;line-height:15px}.ptcPlusReplKind[data-kind=variable]{color:var(--dsw-alias-interactive-primary,#315fbd)}.ptcPlusReplKind[data-kind=function]{color:#7651b5}.ptcPlusReplKind[data-kind=class]{color:var(--dsw-alias-state-warning-primary,#946200)}.ptcPlusReplKind[data-kind=import]{color:#14766f}.ptcPlusReplPreview{grid-column:1;min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary,#74777d);font:11px/16px ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.ptcPlusReplInspect{grid-column:2;grid-row:1/3;display:inline-flex;align-items:center;gap:4px;padding:3px 5px;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-secondary,#52565d);cursor:pointer;font:500 11px/17px inherit;white-space:nowrap}.ptcPlusReplInspect:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06));color:var(--dsw-alias-interactive-primary,#4d6bfe)}.ptcPlusReplInspect:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:1px}.ptcPlusReplDefinition{grid-column:1/-1;min-width:0;margin-top:4px;padding:8px;border-left:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.03))}.ptcPlusReplLocation{display:block;margin-bottom:5px;color:var(--dsw-alias-label-tertiary,#74777d);font-size:10px;line-height:15px}.ptcPlusReplCode{max-height:180px;margin:0;overflow:auto;color:inherit;font:11px/16px ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.ptcPlusReplEmpty,.ptcPlusReplMore{display:block;color:var(--dsw-alias-label-tertiary,#74777d)}.ptcPlusReplEmpty{padding:18px 13px;font-size:12px;line-height:18px}.ptcPlusReplMore{padding:8px 13px;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.03));font-size:11px;line-height:17px}
|
|
18
|
+
.ptcPlusReplList{max-height:min(52vh,480px)}.ptcPlusReplBinding{grid-template-columns:minmax(0,1fr) 24px;gap:3px 8px;min-height:36px;padding:5px 12px;content-visibility:auto;contain-intrinsic-size:36px;cursor:pointer;transition:background-color .16s ease}.ptcPlusReplBinding:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:-2px}.ptcPlusReplBinding[data-expanded=true]{background:color-mix(in srgb,var(--dsw-alias-interactive-primary,#4d6bfe) 5%,transparent)}.ptcPlusReplName{grid-column:1}.ptcPlusReplName[data-kind=variable]{color:var(--dsw-alias-interactive-primary,#315fbd)}.ptcPlusReplName[data-kind=function]{color:#7651b5}.ptcPlusReplName[data-kind=class]{color:var(--dsw-alias-state-warning-primary,#946200)}.ptcPlusReplName[data-kind=import]{color:#14766f}.ptcPlusReplPreview{grid-column:1}.ptcPlusReplChevron{grid-column:2;grid-row:1/3;display:flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#74777d);transition:transform .2s ease}.ptcPlusReplChevron[data-open=true]{transform:rotate(180deg)}.ptcPlusReplDefinitionWrap{grid-column:1/-1;display:grid;grid-template-rows:0fr;min-width:0;transition:grid-template-rows .24s cubic-bezier(.2,.7,.2,1)}.ptcPlusReplDefinitionWrap[data-open=true]{grid-template-rows:1fr}.ptcPlusReplDefinitionInner{min-height:0;overflow:hidden}
|
|
19
|
+
.ptcPlusTool{display:flex;min-width:0;flex-direction:column}.ptcPlusToolPreview{display:flex;min-width:0;flex:1 1 auto;flex-direction:row;align-items:center;overflow:hidden;margin-left:7px}.ptcPlusToolPreview .ptcPlusFeatures{flex:0 1 auto;flex-wrap:nowrap;overflow:hidden;margin:0 0 0 7px}.ptcPlusToolSummaryLine{box-sizing:border-box;display:flex;min-width:0;min-height:20px;flex:1 1 auto;align-items:center;gap:7px;padding:0;color:inherit;line-height:20px}.ptcPlusToolSummary{box-sizing:border-box;display:flex;min-width:0;min-height:32px;align-items:center;gap:7px;padding:0;color:inherit;line-height:20px}.ptcPlusToolSummary[data-expandable=true]{cursor:pointer}.ptcPlusToolSummary[data-expandable=true]:hover .ptcPlusToolTitle{color:var(--dsw-alias-interactive-primary,#4d6bfe)}.ptcPlusToolSummary:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:2px}.ptcPlusToolLeading{display:flex;width:16px;height:20px;flex:none;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#74777d)}.ptcPlusToolChevron{transition:transform .16s ease}.ptcPlusToolChevron[data-open=true]{transform:rotate(180deg)}.ptcPlusToolTitle{display:flex;height:20px;flex:none;align-items:center;font-size:13px;font-weight:500;line-height:20px}.ptcPlusToolState{display:flex;height:20px;flex:none;align-items:center;color:var(--dsw-alias-label-tertiary,#74777d);font-size:11px;line-height:20px}.ptcPlusToolSummaryLine[data-state=running] .ptcPlusToolState,.ptcPlusToolSummary[data-state=running] .ptcPlusToolState{color:var(--dsw-alias-interactive-primary,#4d6bfe)}.ptcPlusToolSummaryLine[data-state=error] .ptcPlusToolState,.ptcPlusToolSummary[data-state=error] .ptcPlusToolState{color:var(--dsw-alias-state-danger-primary,#c43d3d)}.ptcPlusToolSummaryLine[data-state=stopped] .ptcPlusToolState,.ptcPlusToolSummary[data-state=stopped] .ptcPlusToolState{color:var(--dsw-alias-state-warning-primary,#a15c00)}.ptcPlusToolSep{width:3px;height:3px;flex:none;border-radius:50%;background:var(--dsw-alias-label-tertiary,#74777d)}.ptcPlusToolDescription{display:flex;min-width:0;min-height:20px;flex:1 1 auto;align-items:center;overflow:hidden;color:var(--dsw-alias-label-secondary,#52565d);font-size:13px;line-height:20px;text-overflow:ellipsis;white-space:nowrap}.ptcPlusToolPreview .ptcPlusFeature{flex:none;max-width:180px;white-space:nowrap}.ptcPlusToolPreview .ptcPlusFeatureDetail{max-width:120px}.ptcPlusToolSummaryLine[data-state=error] .ptcPlusToolDescription,.ptcPlusToolSummary[data-state=error] .ptcPlusToolDescription{color:var(--dsw-alias-state-danger-primary,#c43d3d)}.ptcPlusToolSummaryLine[data-state=stopped] .ptcPlusToolDescription,.ptcPlusToolSummary[data-state=stopped] .ptcPlusToolDescription{color:var(--dsw-alias-state-warning-primary,#a15c00)}
|
|
20
|
+
.ptcPlusFeatures{display:flex;min-width:0;flex-wrap:wrap;gap:3px 14px;margin:0 0 5px 23px}.ptcPlusFeature{display:inline-flex;min-width:0;align-items:center;gap:5px;color:var(--dsw-alias-label-secondary,#52565d);font-size:11px;line-height:17px}.ptcPlusFeature::before{width:4px;height:4px;flex:none;border-radius:50%;background:var(--dsw-alias-interactive-primary,#4d6bfe);content:''}.ptcPlusFeatureName{font-weight:500}.ptcPlusFeatureDetail{min-width:0;overflow:hidden;color:var(--dsw-alias-label-tertiary,#74777d);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}
|
|
21
|
+
.ptcPlusToolBody{margin:4px 0 8px 23px;border-left:2px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));background:var(--dsw-alias-bg-layer-2,rgba(38,49,72,.03))}.ptcPlusToolSection{display:flex;min-width:0;flex-direction:column;gap:4px;padding:9px 11px}.ptcPlusToolSection+.ptcPlusToolSection{border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}.ptcPlusToolSectionLabel{color:var(--dsw-alias-label-tertiary,#74777d);font-size:10px;font-weight:600;line-height:16px;text-transform:uppercase}.ptcPlusToolCode{max-height:320px;margin:0;overflow:auto;color:inherit;font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.ptcPlusIoCard{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.1));border-radius:12px;background:var(--dsw-alias-markdown-code-block,rgba(38,49,72,.06));overflow:hidden}.ptcPlusIoText{max-height:320px;margin:0;padding:12px 16px;overflow:auto;color:var(--dsw-alias-label-secondary,#52565d);font:12px/18px ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.ptcPlusIoText[data-error]{color:var(--dsw-alias-state-error-primary,#c43d3d)}.ptcPlusInspect{display:inline-flex;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:999px;background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-secondary,#52565d);cursor:pointer;opacity:0;font-size:11px;line-height:16px;transition:opacity .1s ease;display:inline-flex}.ptcPlusTool:hover .ptcPlusInspect,.ptcPlusInspect:focus-visible{opacity:1}.ptcPlusInspect:hover{background:var(--dsw-alias-interactive-bg-hover-solid,rgba(38,49,72,.06));color:var(--dsw-alias-label-primary,#18191c)}
|
|
22
|
+
@media(max-width:560px){.ptcPlusHeader{padding:12px}.ptcPlusFields{margin:0 12px}.ptcPlusRow{align-items:flex-start;flex-direction:column;gap:6px;padding:10px 0}.ptcPlusInput{width:100%}.ptcPlusFooter{align-items:stretch;flex-direction:column}.ptcPlusButton{width:100%}.ptcPlusFeatures,.ptcPlusToolBody{margin-left:0}.ptcPlusToolSummary .ptcPlusToolDescription{white-space:normal;overflow-wrap:anywhere}}
|
|
23
|
+
@media(prefers-reduced-motion:reduce){.ptcPlusHeader,.ptcPlusChevron,.ptcPlusBody,.ptcPlusButton,.ptcPlusActive,.ptcPlusToolChevron,.ptcPlusReplChevron,.ptcPlusReplDefinitionWrap,.ptcPlusInspect{transition:none}}
|
|
24
|
+
/* The summary button owns disclosure; definition content is a separate grid item. */
|
|
25
|
+
.ptcPlusReplBinding{padding:0;cursor:default}.ptcPlusReplBindingTrigger{appearance:none;display:grid;width:100%;grid-column:1/-1;grid-template-columns:minmax(0,1fr) 24px;gap:3px 8px;min-height:36px;padding:5px 12px;border:0;background:transparent;color:inherit;text-align:left;cursor:pointer;font:inherit;transition:background-color .16s ease}.ptcPlusReplBindingTrigger:hover,.ptcPlusReplBindingTrigger[aria-expanded=true]{background:color-mix(in srgb,var(--dsw-alias-interactive-primary,#4d6bfe) 5%,transparent)}.ptcPlusReplBindingTrigger:focus-visible{outline:2px solid var(--dsw-alias-interactive-primary,#4d6bfe);outline-offset:-2px}
|
|
26
|
+
/* High-contrast TypeScript-like token colors adapt to the active text theme. */
|
|
27
|
+
.ptcPlusReplCard .ptcPlusReplName[data-kind=variable]{color:color-mix(in srgb,#005cc5 78%,var(--dsw-alias-label-primary,#18191c))}
|
|
28
|
+
.ptcPlusReplCard .ptcPlusReplName[data-kind=function]{color:color-mix(in srgb,#795e26 78%,var(--dsw-alias-label-primary,#18191c))}
|
|
29
|
+
.ptcPlusReplCard .ptcPlusReplName[data-kind=class]{color:color-mix(in srgb,#267f99 78%,var(--dsw-alias-label-primary,#18191c))}
|
|
30
|
+
.ptcPlusReplCard .ptcPlusReplName[data-kind=import]{color:color-mix(in srgb,#af00db 78%,var(--dsw-alias-label-primary,#18191c))}
|
|
31
|
+
/* Keep the session-header action on the same compact 32px rhythm as DSH chrome. */
|
|
32
|
+
.ptcPlusActiveShell{display:inline-flex;height:28px;align-items:center;justify-content:center;line-height:0;vertical-align:middle}.ptcPlusActive{box-sizing:border-box;height:28px;justify-content:center;gap:6px;padding:0 6px;border:0;background:transparent;font-family:inherit;font-size:13px;font-weight:500;line-height:18px}.ptcPlusActive::before{width:6px;height:6px;flex:none;border-radius:50%;background:currentColor;box-shadow:0 0 0 2px color-mix(in srgb,currentColor 18%,transparent);content:''}.ptcPlusActive:hover,.ptcPlusActive[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}.ptcPlusActiveLabel{display:inline-flex;height:18px;align-items:center;line-height:18px}
|
|
14
33
|
`
|
|
15
34
|
|
|
16
35
|
/** Locale namespace owning every settings-card string (field copy plus chrome). */
|
|
@@ -31,7 +50,33 @@ const CHROME_COPY = Object.freeze({
|
|
|
31
50
|
'status.applied': '设置已立即生效',
|
|
32
51
|
'status.conflict': '设置未生效,请检查设置冲突',
|
|
33
52
|
'status.failed': '设置失败:{error}',
|
|
34
|
-
'indicator.title': 'PTC Plus
|
|
53
|
+
'indicator.title': 'PTC Plus 已启用;查看当前可复用的 REPL 绑定',
|
|
54
|
+
'memory.title': 'REPL 可复用绑定',
|
|
55
|
+
'memory.count': '{count} 个可复用绑定',
|
|
56
|
+
'memory.empty': '当前没有可复用绑定',
|
|
57
|
+
'memory.unavailable': '当前绑定状态尚不可确认',
|
|
58
|
+
'memory.more': '另有 {count} 个绑定未显示',
|
|
59
|
+
'memory.kind.variable': '变量',
|
|
60
|
+
'memory.kind.function': '函数',
|
|
61
|
+
'memory.kind.class': '类',
|
|
62
|
+
'memory.kind.import': '导入',
|
|
63
|
+
'memory.location': '第 {line} 行,第 {column} 列',
|
|
64
|
+
'tool.code': '执行',
|
|
65
|
+
'tool.codeEdit': '修正执行',
|
|
66
|
+
'tool.running': '正在运行',
|
|
67
|
+
'tool.completed': '执行完成',
|
|
68
|
+
'tool.failed': '执行失败',
|
|
69
|
+
'tool.stopped': '执行已中断',
|
|
70
|
+
'tool.source': '源码',
|
|
71
|
+
'tool.result': '结果',
|
|
72
|
+
'tool.copy': '复制代码',
|
|
73
|
+
'tool.copied': '已复制',
|
|
74
|
+
'tool.inspect': '检查调用',
|
|
75
|
+
'feature.safeEdit': '安全编辑执行',
|
|
76
|
+
'feature.codeRun': '隔离执行 code.run',
|
|
77
|
+
'feature.stateSaved': '保存 REPL 状态',
|
|
78
|
+
'feature.stateRestored': '恢复 REPL 状态',
|
|
79
|
+
'feature.stateDeleted': '删除 REPL 状态',
|
|
35
80
|
}),
|
|
36
81
|
en: Object.freeze({
|
|
37
82
|
'card.description': 'The session-bound TypeScript REPL for PTC mode.',
|
|
@@ -46,7 +91,33 @@ const CHROME_COPY = Object.freeze({
|
|
|
46
91
|
'status.applied': 'Setting applied immediately.',
|
|
47
92
|
'status.conflict': 'The setting did not take effect; check for conflicting settings.',
|
|
48
93
|
'status.failed': 'Could not save: {error}',
|
|
49
|
-
'indicator.title': 'PTC Plus is active',
|
|
94
|
+
'indicator.title': 'PTC Plus is active; view reusable REPL bindings',
|
|
95
|
+
'memory.title': 'Reusable REPL bindings',
|
|
96
|
+
'memory.count': '{count} reusable bindings',
|
|
97
|
+
'memory.empty': 'No reusable bindings',
|
|
98
|
+
'memory.unavailable': 'Current binding state cannot be confirmed',
|
|
99
|
+
'memory.more': '{count} more bindings not shown',
|
|
100
|
+
'memory.kind.variable': 'Variable',
|
|
101
|
+
'memory.kind.function': 'Function',
|
|
102
|
+
'memory.kind.class': 'Class',
|
|
103
|
+
'memory.kind.import': 'Import',
|
|
104
|
+
'memory.location': 'Line {line}, column {column}',
|
|
105
|
+
'tool.code': 'Code',
|
|
106
|
+
'tool.codeEdit': 'Code edit',
|
|
107
|
+
'tool.running': 'Running',
|
|
108
|
+
'tool.completed': 'Completed',
|
|
109
|
+
'tool.failed': 'Failed',
|
|
110
|
+
'tool.stopped': 'Stopped',
|
|
111
|
+
'tool.source': 'Source',
|
|
112
|
+
'tool.result': 'Result',
|
|
113
|
+
'tool.inspect': 'Inspect call',
|
|
114
|
+
'feature.safeEdit': 'Safe edit execution',
|
|
115
|
+
'feature.codeRun': 'Isolated code.run execution',
|
|
116
|
+
'feature.stateSaved': 'Saved REPL state',
|
|
117
|
+
'feature.stateRestored': 'Restored REPL state',
|
|
118
|
+
'feature.stateDeleted': 'Deleted REPL state',
|
|
119
|
+
'tool.copy': 'Copy code',
|
|
120
|
+
'tool.copied': 'Copied',
|
|
50
121
|
}),
|
|
51
122
|
})
|
|
52
123
|
|
|
@@ -62,6 +133,9 @@ function fieldCopy(field, locale) {
|
|
|
62
133
|
const SETTINGS_COPY = Object.freeze(Object.fromEntries(
|
|
63
134
|
['zh', 'en'].map(locale => [locale, Object.freeze({
|
|
64
135
|
...CHROME_COPY[locale],
|
|
136
|
+
...Object.assign({}, ...CONFIG_GROUPS.map(group => ({
|
|
137
|
+
[`group.${group.key}`]: locale === 'en' ? group.labelEn : group.label,
|
|
138
|
+
}))),
|
|
65
139
|
...Object.assign({}, ...CONFIG_FIELDS.map(field => fieldCopy(field, locale))),
|
|
66
140
|
})]),
|
|
67
141
|
))
|
|
@@ -77,8 +151,11 @@ window.__ModuleLoader__.load({
|
|
|
77
151
|
factory: (require) => {
|
|
78
152
|
const React = require('react')
|
|
79
153
|
const {
|
|
154
|
+
CodeBlock,
|
|
155
|
+
DisclosureRow,
|
|
80
156
|
IconCheckOutline14,
|
|
81
157
|
IconChevronDownOutline14,
|
|
158
|
+
IconInspectOutline12,
|
|
82
159
|
} = require('@deepseek-ai/dsh-client-ui-primitives')
|
|
83
160
|
const module = { exports: {} }
|
|
84
161
|
const h = React.createElement
|
|
@@ -163,6 +240,24 @@ window.__ModuleLoader__.load({
|
|
|
163
240
|
const fieldDisabled = field => unavailable
|
|
164
241
|
|| pending.has(field.key)
|
|
165
242
|
|| (field.key !== 'enabled' && !enabled)
|
|
243
|
+
const settingGroups = CONFIG_GROUPS.map(group => h('section', {
|
|
244
|
+
key: group.key,
|
|
245
|
+
className: 'ptcPlusGroup',
|
|
246
|
+
'aria-labelledby': `ptc-plus-settings-group-${group.key}`,
|
|
247
|
+
},
|
|
248
|
+
h('h3', {
|
|
249
|
+
id: `ptc-plus-settings-group-${group.key}`,
|
|
250
|
+
className: 'ptcPlusGroupTitle',
|
|
251
|
+
}, t(`group.${group.key}`)),
|
|
252
|
+
...group.fields.map(key => {
|
|
253
|
+
const field = CONFIG_FIELDS.find(candidate => candidate.key === key)
|
|
254
|
+
if (field === undefined) return null
|
|
255
|
+
return h('div', { key: field.key, className: 'ptcPlusRow' },
|
|
256
|
+
h('div', { className: 'ptcPlusMain' },
|
|
257
|
+
h('div', { className: 'ptcPlusLabel' }, t(`${field.key}.label`)),
|
|
258
|
+
field.description === '' ? null : h('div', { className: 'ptcPlusDetail' }, t(`${field.key}.description`))),
|
|
259
|
+
fieldInput(field, value[field.key], fieldDisabled(field), persist, t(`${field.key}.label`)))
|
|
260
|
+
})))
|
|
166
261
|
return h('li', { className: 'ptcPlusCard' },
|
|
167
262
|
h('button', {
|
|
168
263
|
type: 'button', className: 'ptcPlusHeader', 'aria-expanded': open,
|
|
@@ -181,11 +276,7 @@ window.__ModuleLoader__.load({
|
|
|
181
276
|
: snapshot.status === 'unavailable'
|
|
182
277
|
? h('p', { className: 'ptcPlusMessage' }, t('state.unavailable'))
|
|
183
278
|
: [
|
|
184
|
-
...
|
|
185
|
-
h('div', { className: 'ptcPlusMain' },
|
|
186
|
-
h('div', { className: 'ptcPlusLabel' }, t(`${field.key}.label`)),
|
|
187
|
-
field.description === '' ? null : h('div', { className: 'ptcPlusDetail' }, t(`${field.key}.description`))),
|
|
188
|
-
fieldInput(field, value[field.key], fieldDisabled(field), persist, t(`${field.key}.label`)))),
|
|
279
|
+
...settingGroups,
|
|
189
280
|
h('div', { key: 'footer', className: 'ptcPlusFooter' },
|
|
190
281
|
h('span', { className: 'ptcPlusMessage', role: 'status' }, status === null
|
|
191
282
|
? t(snapshot.writable ? 'footer.live' : 'footer.readOnly')
|
|
@@ -198,22 +289,367 @@ window.__ModuleLoader__.load({
|
|
|
198
289
|
name: 'settings.plugin.item', key: SETTINGS_NAMESPACE, locale: LOCALE_NS,
|
|
199
290
|
}, PTCPlusSettingsCard))
|
|
200
291
|
|
|
292
|
+
function PTCPlusToolRow({ toolName, block, inspect, t }) {
|
|
293
|
+
const [open, setOpen] = React.useState(false)
|
|
294
|
+
const view = derivePtcToolView(block, toolName)
|
|
295
|
+
const expandable = view.code !== '' || view.output !== '' || typeof inspect === 'function'
|
|
296
|
+
const stateKey = {
|
|
297
|
+
running: 'tool.running',
|
|
298
|
+
ok: 'tool.completed',
|
|
299
|
+
error: 'tool.failed',
|
|
300
|
+
stopped: 'tool.stopped',
|
|
301
|
+
}[view.state]
|
|
302
|
+
const outputSummary = view.state === 'error' && view.output !== ''
|
|
303
|
+
? view.output.split(/\r?\n/, 1)[0]
|
|
304
|
+
: ''
|
|
305
|
+
const summary = outputSummary || view.description
|
|
306
|
+
const stateText = view.state === 'ok' ? null : t(stateKey)
|
|
307
|
+
const toggle = () => {
|
|
308
|
+
if (expandable) setOpen(current => !current)
|
|
309
|
+
}
|
|
310
|
+
const summaryText = summary === '' ? null : summary
|
|
311
|
+
const summaryLine = stateText === null && summaryText === null ? null
|
|
312
|
+
: h('div', { className: 'ptcPlusToolSummaryLine', 'data-state': view.state, role: 'status' },
|
|
313
|
+
stateText === null ? null : h('span', { className: 'ptcPlusToolState' }, stateText),
|
|
314
|
+
summaryText === null ? null : h('span', { className: 'ptcPlusToolSep', 'aria-hidden': true }),
|
|
315
|
+
summaryText === null ? null : h('span', { className: 'ptcPlusToolDescription' }, summaryText))
|
|
316
|
+
const body = !open ? null : h('div', { className: 'ptcPlusToolBody' },
|
|
317
|
+
view.code === '' ? null : h('div', { className: 'ptcPlusToolSection' },
|
|
318
|
+
h('span', { className: 'ptcPlusToolSectionLabel' }, t('tool.source')),
|
|
319
|
+
typeof CodeBlock === 'function'
|
|
320
|
+
? h(CodeBlock, {
|
|
321
|
+
code: view.code, lang: 'typescript', className: 'ptcPlusToolCode',
|
|
322
|
+
copyLabel: t('tool.copy'), copiedLabel: t('tool.copied'),
|
|
323
|
+
})
|
|
324
|
+
: h('pre', { className: 'ptcPlusToolCode' }, view.code)),
|
|
325
|
+
view.output === '' ? null : h('div', { className: 'ptcPlusToolSection' },
|
|
326
|
+
h('span', { className: 'ptcPlusToolSectionLabel' }, t('tool.result')),
|
|
327
|
+
h('div', { className: 'ptcPlusIoCard' },
|
|
328
|
+
h('pre', {
|
|
329
|
+
className: 'ptcPlusIoText',
|
|
330
|
+
'data-error': view.state === 'error' || undefined,
|
|
331
|
+
}, view.output))),
|
|
332
|
+
typeof inspect !== 'function' ? null : h('button', {
|
|
333
|
+
type: 'button', className: 'ptcPlusInspect', onClick: inspect,
|
|
334
|
+
}, h(IconInspectOutline12, { 'aria-hidden': true }), t('tool.inspect')))
|
|
335
|
+
const features = view.features.length === 0 ? null : h('div', { className: 'ptcPlusFeatures' },
|
|
336
|
+
view.features.map(feature => h('span', {
|
|
337
|
+
key: `${feature.key}:${feature.detail}`, className: 'ptcPlusFeature',
|
|
338
|
+
},
|
|
339
|
+
h('span', { className: 'ptcPlusFeatureName' }, t(feature.key)),
|
|
340
|
+
feature.detail === '' ? null
|
|
341
|
+
: h('span', { className: 'ptcPlusFeatureDetail', title: feature.detail }, feature.detail))))
|
|
342
|
+
const collapsedContent = h('div', { className: 'ptcPlusToolPreview' }, summaryLine, features)
|
|
343
|
+
if (typeof DisclosureRow === 'function') {
|
|
344
|
+
return h('div', { className: 'ptcPlusTool' },
|
|
345
|
+
h(DisclosureRow, {
|
|
346
|
+
icon: expandable
|
|
347
|
+
? h(IconChevronDownOutline14, { size: 14 })
|
|
348
|
+
: h(IconCheckOutline14, { size: 14 }),
|
|
349
|
+
title: t(toolName === 'edit_run_code' ? 'tool.codeEdit' : 'tool.code'),
|
|
350
|
+
open,
|
|
351
|
+
expandable,
|
|
352
|
+
onToggle: toggle,
|
|
353
|
+
expandOnRowClick: true,
|
|
354
|
+
previewChevron: false,
|
|
355
|
+
keepContentWhenOpen: true,
|
|
356
|
+
collapsedContent,
|
|
357
|
+
children: body,
|
|
358
|
+
}))
|
|
359
|
+
}
|
|
360
|
+
return h('div', { className: 'ptcPlusTool' },
|
|
361
|
+
h('div', {
|
|
362
|
+
className: 'ptcPlusToolSummary', 'data-state': view.state,
|
|
363
|
+
'data-expandable': expandable || undefined,
|
|
364
|
+
role: expandable ? 'button' : undefined,
|
|
365
|
+
tabIndex: expandable ? 0 : undefined,
|
|
366
|
+
'aria-expanded': expandable ? open : undefined,
|
|
367
|
+
onClick: expandable ? toggle : undefined,
|
|
368
|
+
onKeyDown: expandable ? (event) => {
|
|
369
|
+
if (event.key !== 'Enter' && event.key !== ' ') return
|
|
370
|
+
event.preventDefault()
|
|
371
|
+
toggle()
|
|
372
|
+
} : undefined,
|
|
373
|
+
},
|
|
374
|
+
h('span', { className: 'ptcPlusToolLeading', 'aria-hidden': true }, expandable
|
|
375
|
+
? h(IconChevronDownOutline14, { size: 14, className: 'ptcPlusToolChevron', 'data-open': open })
|
|
376
|
+
: h(IconCheckOutline14, { size: 14 })),
|
|
377
|
+
h('span', { className: 'ptcPlusToolTitle' }, t(
|
|
378
|
+
toolName === 'edit_run_code' ? 'tool.codeEdit' : 'tool.code')),
|
|
379
|
+
view.state === 'ok' ? null
|
|
380
|
+
: h('span', { className: 'ptcPlusToolState', role: 'status' }, t(stateKey)),
|
|
381
|
+
h('span', { className: 'ptcPlusToolSep', 'aria-hidden': true }),
|
|
382
|
+
h('span', { className: 'ptcPlusToolDescription' }, summary)),
|
|
383
|
+
features,
|
|
384
|
+
body)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
ctx.slots.inject('tool.call.toolview', () => {
|
|
388
|
+
let releaseRows
|
|
389
|
+
const release = () => {
|
|
390
|
+
releaseRows?.()
|
|
391
|
+
releaseRows = undefined
|
|
392
|
+
}
|
|
393
|
+
const sync = () => {
|
|
394
|
+
const snapshot = preferenceScope.getSnapshot()
|
|
395
|
+
const enabled = snapshot.status === 'ready'
|
|
396
|
+
&& snapshot.value?.enabled === true
|
|
397
|
+
&& snapshot.value?.enhancedToolView !== false
|
|
398
|
+
if (enabled === (releaseRows !== undefined)) return
|
|
399
|
+
release()
|
|
400
|
+
if (!enabled) return
|
|
401
|
+
const disposers = []
|
|
402
|
+
try {
|
|
403
|
+
disposers.push(ctx.slots.register({
|
|
404
|
+
name: 'tool.call.toolview', key: 'run_code', locale: LOCALE_NS,
|
|
405
|
+
}, PTCPlusToolRow))
|
|
406
|
+
disposers.push(ctx.slots.register({
|
|
407
|
+
name: 'tool.call.toolview', key: 'edit_run_code', locale: LOCALE_NS,
|
|
408
|
+
}, PTCPlusToolRow))
|
|
409
|
+
} catch (error) {
|
|
410
|
+
disposers.reverse().forEach(dispose => dispose())
|
|
411
|
+
throw error
|
|
412
|
+
}
|
|
413
|
+
releaseRows = () => disposers.reverse().forEach(dispose => dispose())
|
|
414
|
+
}
|
|
415
|
+
sync()
|
|
416
|
+
const unsubscribe = preferenceScope.subscribe(sync)
|
|
417
|
+
return () => {
|
|
418
|
+
unsubscribe()
|
|
419
|
+
release()
|
|
420
|
+
}
|
|
421
|
+
})
|
|
422
|
+
|
|
201
423
|
ctx.inject(['slots', 'sessions'], (scope) => {
|
|
202
|
-
function
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
()
|
|
206
|
-
|
|
424
|
+
function replPopoverIsOpen(popover) {
|
|
425
|
+
if (popover?.dataset?.open === 'true') return true
|
|
426
|
+
try {
|
|
427
|
+
return popover?.matches?.(':popover-open') === true
|
|
428
|
+
} catch {
|
|
429
|
+
return false
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function placeReplPopover(trigger, popover) {
|
|
434
|
+
if (trigger === null || popover === null) return
|
|
435
|
+
const margin = 12
|
|
436
|
+
const gap = 8
|
|
437
|
+
const viewportWidth = document.documentElement.clientWidth || window.innerWidth
|
|
438
|
+
const viewportHeight = document.documentElement.clientHeight || window.innerHeight
|
|
439
|
+
const triggerRect = trigger.getBoundingClientRect()
|
|
440
|
+
const width = Math.min(344, Math.max(0, viewportWidth - margin * 2))
|
|
441
|
+
const left = Math.min(
|
|
442
|
+
Math.max(margin, triggerRect.right - width),
|
|
443
|
+
Math.max(margin, viewportWidth - width - margin),
|
|
207
444
|
)
|
|
445
|
+
const below = Math.max(0, viewportHeight - triggerRect.bottom - gap - margin)
|
|
446
|
+
const above = Math.max(0, triggerRect.top - gap - margin)
|
|
447
|
+
const opensAbove = below < 260 && above > below
|
|
448
|
+
const availableHeight = Math.max(80, opensAbove ? above : below)
|
|
449
|
+
popover.style.width = `${width}px`
|
|
450
|
+
popover.style.maxHeight = `${availableHeight}px`
|
|
451
|
+
popover.style.left = `${left}px`
|
|
452
|
+
popover.style.top = opensAbove
|
|
453
|
+
? `${Math.max(margin, triggerRect.top - gap - Math.min(popover.offsetHeight, availableHeight))}px`
|
|
454
|
+
: `${Math.min(viewportHeight - margin, triggerRect.bottom + gap)}px`
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function ReplMemoryCard({ memory, t, id, titleId, popoverRef, onEnter, onLeave }) {
|
|
458
|
+
const [expandedBinding, setExpandedBinding] = React.useState(null)
|
|
459
|
+
return h('div', {
|
|
460
|
+
className: 'ptcPlusReplPopover', id, ref: popoverRef, popover: 'auto',
|
|
461
|
+
role: 'dialog', 'aria-labelledby': titleId,
|
|
462
|
+
onPointerEnter: onEnter, onPointerLeave: onLeave,
|
|
463
|
+
},
|
|
464
|
+
h('div', { className: 'ptcPlusReplCard' },
|
|
465
|
+
h('div', { className: 'ptcPlusReplHead' },
|
|
466
|
+
h('span', { className: 'ptcPlusReplStatusDot', 'aria-hidden': true }),
|
|
467
|
+
h('span', { className: 'ptcPlusReplTitle', id: titleId }, t('memory.title')),
|
|
468
|
+
memory.available
|
|
469
|
+
? h('span', { className: 'ptcPlusReplSummary' }, t('memory.count', { count: memory.total }))
|
|
470
|
+
: null),
|
|
471
|
+
!memory.available
|
|
472
|
+
? h('span', { className: 'ptcPlusReplEmpty' }, t('memory.unavailable'))
|
|
473
|
+
: memory.entries.length === 0
|
|
474
|
+
? h('span', { className: 'ptcPlusReplEmpty' }, t('memory.empty'))
|
|
475
|
+
: h('ul', { className: 'ptcPlusReplList' }, memory.entries.map((binding, index) => {
|
|
476
|
+
const expanded = expandedBinding === binding.name
|
|
477
|
+
const preview = binding.definition.source.replace(/\s+/g, ' ').trim()
|
|
478
|
+
const definitionId = `${id}-binding-${index}`
|
|
479
|
+
const toggle = () => setExpandedBinding(current => (
|
|
480
|
+
current === binding.name ? null : binding.name
|
|
481
|
+
))
|
|
482
|
+
return h('li', {
|
|
483
|
+
className: 'ptcPlusReplBinding', key: binding.name,
|
|
484
|
+
'data-expanded': expanded,
|
|
485
|
+
},
|
|
486
|
+
h('button', {
|
|
487
|
+
className: 'ptcPlusReplBindingTrigger', type: 'button',
|
|
488
|
+
'aria-expanded': expanded,
|
|
489
|
+
'aria-controls': expanded ? definitionId : undefined,
|
|
490
|
+
onClick: toggle,
|
|
491
|
+
},
|
|
492
|
+
h('span', {
|
|
493
|
+
className: 'ptcPlusReplName', 'data-kind': binding.kind,
|
|
494
|
+
title: `${binding.name} - ${t(`memory.kind.${binding.kind}`)}`,
|
|
495
|
+
}, binding.name),
|
|
496
|
+
h('span', { className: 'ptcPlusReplPreview', title: preview }, preview),
|
|
497
|
+
h('span', {
|
|
498
|
+
className: 'ptcPlusReplChevron', 'data-open': expanded, 'aria-hidden': true,
|
|
499
|
+
}, h(IconChevronDownOutline14, { size: 14 }))),
|
|
500
|
+
expanded
|
|
501
|
+
? h('div', {
|
|
502
|
+
className: 'ptcPlusReplDefinitionWrap', 'data-open': true,
|
|
503
|
+
id: definitionId, role: 'region', 'aria-label': binding.name,
|
|
504
|
+
}, h('div', { className: 'ptcPlusReplDefinitionInner' },
|
|
505
|
+
h('div', { className: 'ptcPlusReplDefinition' },
|
|
506
|
+
h('span', { className: 'ptcPlusReplLocation' }, t('memory.location', {
|
|
507
|
+
line: binding.definition.line,
|
|
508
|
+
column: binding.definition.column,
|
|
509
|
+
})),
|
|
510
|
+
typeof CodeBlock === 'function'
|
|
511
|
+
? h(CodeBlock, {
|
|
512
|
+
code: binding.definition.source,
|
|
513
|
+
lang: 'typescript',
|
|
514
|
+
className: 'ptcPlusReplCode',
|
|
515
|
+
copyLabel: t('tool.copy'),
|
|
516
|
+
copiedLabel: t('tool.copied'),
|
|
517
|
+
})
|
|
518
|
+
: h('pre', { className: 'ptcPlusReplCode' }, binding.definition.source))))
|
|
519
|
+
: null)
|
|
520
|
+
})),
|
|
521
|
+
memory.omitted === 0 ? null
|
|
522
|
+
: h('span', { className: 'ptcPlusReplMore' }, t('memory.more', { count: memory.omitted }))))
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function PTCPlusSessionIndicator({ sessionId, t, useSession, useProjection, useSessions }) {
|
|
526
|
+
// The session-scoped standard kit changed between DSH lines. Prefer its
|
|
527
|
+
// public hooks when present, then fall back to the older sessions list.
|
|
528
|
+
let conversation
|
|
529
|
+
try {
|
|
530
|
+
conversation = typeof useSession === 'function'
|
|
531
|
+
? useSession(snapshot => snapshot)
|
|
532
|
+
: undefined
|
|
533
|
+
} catch {
|
|
534
|
+
conversation = undefined
|
|
535
|
+
}
|
|
536
|
+
let projectionMemory
|
|
537
|
+
try {
|
|
538
|
+
projectionMemory = typeof useProjection === 'function'
|
|
539
|
+
? useProjection('ptcPlusRepl')
|
|
540
|
+
: undefined
|
|
541
|
+
} catch {
|
|
542
|
+
projectionMemory = undefined
|
|
543
|
+
}
|
|
544
|
+
let sessions
|
|
545
|
+
try {
|
|
546
|
+
sessions = typeof useSessions === 'function'
|
|
547
|
+
? useSessions(snapshot => snapshot)
|
|
548
|
+
: React.useSyncExternalStore(
|
|
549
|
+
listener => scope.sessions?.list?.subscribe?.(listener) ?? (() => {}),
|
|
550
|
+
() => scope.sessions?.list?.getSnapshot?.() ?? {},
|
|
551
|
+
() => scope.sessions?.list?.getSnapshot?.() ?? {},
|
|
552
|
+
)
|
|
553
|
+
} catch {
|
|
554
|
+
sessions = {}
|
|
555
|
+
}
|
|
208
556
|
const settings = React.useSyncExternalStore(
|
|
209
557
|
listener => preferenceScope.subscribe(listener),
|
|
210
558
|
() => preferenceScope.getSnapshot(),
|
|
211
559
|
() => preferenceScope.getSnapshot(),
|
|
212
560
|
)
|
|
213
|
-
|
|
561
|
+
const triggerRef = React.useRef(null)
|
|
562
|
+
const popoverRef = React.useRef(null)
|
|
563
|
+
const closeTimer = React.useRef(undefined)
|
|
564
|
+
const [expanded, setExpanded] = React.useState(false)
|
|
565
|
+
const positionPopover = React.useCallback(() => {
|
|
566
|
+
if (!replPopoverIsOpen(popoverRef.current)) return
|
|
567
|
+
placeReplPopover(triggerRef.current, popoverRef.current)
|
|
568
|
+
}, [])
|
|
569
|
+
const showPopover = React.useCallback(() => {
|
|
570
|
+
if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)
|
|
571
|
+
const popover = popoverRef.current
|
|
572
|
+
if (popover === null) return
|
|
573
|
+
popover.style.visibility = 'hidden'
|
|
574
|
+
if (!replPopoverIsOpen(popover)) {
|
|
575
|
+
if (typeof popover.showPopover === 'function') {
|
|
576
|
+
try {
|
|
577
|
+
popover.showPopover()
|
|
578
|
+
} catch {
|
|
579
|
+
popover.dataset.open = 'true'
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
popover.dataset.open = 'true'
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
placeReplPopover(triggerRef.current, popover)
|
|
586
|
+
popover.style.visibility = 'visible'
|
|
587
|
+
setExpanded(true)
|
|
588
|
+
}, [])
|
|
589
|
+
const hidePopover = React.useCallback(() => {
|
|
590
|
+
const popover = popoverRef.current
|
|
591
|
+
if (popover === null) return
|
|
592
|
+
if (popover.dataset.open === 'true') delete popover.dataset.open
|
|
593
|
+
if (typeof popover.hidePopover === 'function' && replPopoverIsOpen(popover)) {
|
|
594
|
+
try { popover.hidePopover() } catch {}
|
|
595
|
+
}
|
|
596
|
+
setExpanded(false)
|
|
597
|
+
}, [])
|
|
598
|
+
const scheduleHide = React.useCallback(() => {
|
|
599
|
+
if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)
|
|
600
|
+
closeTimer.current = setTimeout(() => {
|
|
601
|
+
closeTimer.current = undefined
|
|
602
|
+
if (document.activeElement === triggerRef.current
|
|
603
|
+
|| popoverRef.current?.contains(document.activeElement)) return
|
|
604
|
+
hidePopover()
|
|
605
|
+
}, 120)
|
|
606
|
+
}, [hidePopover])
|
|
607
|
+
React.useEffect(() => {
|
|
608
|
+
const syncPopoverState = (event) => {
|
|
609
|
+
if (event.target !== popoverRef.current) return
|
|
610
|
+
setExpanded(replPopoverIsOpen(popoverRef.current))
|
|
611
|
+
}
|
|
612
|
+
window.addEventListener('resize', positionPopover)
|
|
613
|
+
document.addEventListener('scroll', positionPopover, true)
|
|
614
|
+
document.addEventListener('toggle', syncPopoverState, true)
|
|
615
|
+
return () => {
|
|
616
|
+
if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)
|
|
617
|
+
window.removeEventListener('resize', positionPopover)
|
|
618
|
+
document.removeEventListener('scroll', positionPopover, true)
|
|
619
|
+
document.removeEventListener('toggle', syncPopoverState, true)
|
|
620
|
+
const popover = popoverRef.current
|
|
621
|
+
if (popover?.dataset?.open === 'true') delete popover.dataset.open
|
|
622
|
+
if (typeof popover?.hidePopover === 'function' && replPopoverIsOpen(popover)) {
|
|
623
|
+
try { popover.hidePopover() } catch {}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}, [hidePopover, positionPopover])
|
|
627
|
+
const resolvedSessionId = sessionId ?? conversation?.sessionId
|
|
628
|
+
const session = sessions?.byId?.[resolvedSessionId] ?? conversation
|
|
629
|
+
if (!sessionUsesPtcPreset(session)
|
|
214
630
|
|| settings.status !== 'ready' || settings.value?.enabled !== true) return null
|
|
215
|
-
|
|
216
|
-
|
|
631
|
+
let memory
|
|
632
|
+
try {
|
|
633
|
+
memory = normalizeReplMemorySnapshot(projectionMemory
|
|
634
|
+
?? session?.projectionValues?.ptcPlusRepl)
|
|
635
|
+
} catch {
|
|
636
|
+
memory = unavailableReplMemorySnapshot()
|
|
637
|
+
}
|
|
638
|
+
const popoverId = `ptc-plus-repl-${String(resolvedSessionId).replace(/[^A-Za-z0-9_-]/g, '-')}`
|
|
639
|
+
const titleId = `${popoverId}-title`
|
|
640
|
+
return h('span', { className: 'ptcPlusActiveShell' },
|
|
641
|
+
h('button', {
|
|
642
|
+
type: 'button', className: 'ptcPlusActive', ref: triggerRef,
|
|
643
|
+
'aria-label': t('indicator.title'), 'aria-controls': popoverId,
|
|
644
|
+
'aria-expanded': expanded, 'aria-haspopup': 'dialog',
|
|
645
|
+
onPointerEnter: showPopover, onPointerLeave: scheduleHide,
|
|
646
|
+
onFocus: showPopover, onBlur: scheduleHide, onClick: showPopover,
|
|
647
|
+
onKeyDown: event => { if (event.key === 'Escape') hidePopover() },
|
|
648
|
+
}, h('span', { className: 'ptcPlusActiveLabel' }, 'PTC Plus')),
|
|
649
|
+
h(ReplMemoryCard, {
|
|
650
|
+
memory, t, id: popoverId, titleId, popoverRef,
|
|
651
|
+
onEnter: showPopover, onLeave: scheduleHide,
|
|
652
|
+
}))
|
|
217
653
|
}
|
|
218
654
|
scope.slots.inject('conversation.session.header.actions', () => scope.slots.register({
|
|
219
655
|
name: 'conversation.session.header.actions', id: 'ptc-plus-active', order: -9, locale: LOCALE_NS,
|