@wilshi/dsh-agentos-context 0.0.0-stage → 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/LICENSE +21 -0
- package/LICENSE.opencontext +17 -0
- package/README.i18n.yaml +67 -0
- package/README.md +145 -2
- package/README.zh.md +144 -0
- package/THIRD_PARTY_NOTICES.md +13 -0
- package/cordis.patch.yml +5 -0
- package/docs/CONTRACT.md +51 -0
- package/docs/IMPLEMENTATION-STATUS.md +56 -0
- package/lib/client.js +42545 -0
- package/lib/index.js +9319 -0
- package/package.json +99 -4
- package/prototype/PROTOTYPE.md +125 -0
- package/prototype/index.html +248 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; connect-src 'none'; base-uri 'none'; form-action 'none'">
|
|
7
|
+
<title>agentOS context · 本体增强记忆交互原型</title>
|
|
8
|
+
<style>
|
|
9
|
+
/* agentOS context prototype. DSH-inspired token roles; no external fonts or assets. */
|
|
10
|
+
:root{color-scheme:light;--bg:#f5f7fa;--surface:#ffffff;--soft:#f0f3f8;--sidebar:#f7f8fa;--text:#202b3d;--muted:#63718a;--line:#e1e6ef;--accent:#3864da;--accent-soft:#edf2ff;--success:#147354;--success-bg:#ebf7f1;--warn:#93610d;--warn-bg:#fff5df;--danger:#b54747;--danger-bg:#fff0ee;--shadow:0 8px 30px #2132490c;--radius:12px;font:14px/1.55 'Segoe UI','Microsoft YaHei UI','PingFang SC',sans-serif}
|
|
11
|
+
[data-theme="dark"]{color-scheme:dark;--bg:#161b24;--surface:#202734;--soft:#293343;--sidebar:#191f29;--text:#e4ebf8;--muted:#a4b1c6;--line:#354256;--accent:#95afff;--accent-soft:#2c3c63;--success:#78d5ac;--success-bg:#203e36;--warn:#f0c576;--warn-bg:#413724;--danger:#f39a95;--danger-bg:#422e34;--shadow:none}
|
|
12
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text)}button,input,select,textarea{font:inherit;color:inherit}button,a,input,select,textarea{-webkit-tap-highlight-color:transparent}button{cursor:pointer}button:disabled{cursor:not-allowed;opacity:.5}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{outline:3px solid var(--accent);outline-offset:3px}button{min-height:44px;border:1px solid var(--line);background:var(--surface);border-radius:8px;padding:9px 14px;display:inline-flex;gap:7px;align-items:center;justify-content:center;white-space:normal}button:hover:not(:disabled){border-color:var(--accent);background:var(--accent-soft)}button.primary{background:var(--accent);color:#fff;border-color:transparent}[data-theme="dark"] button.primary{color:#142447}button.primary:hover:not(:disabled){filter:brightness(.96)}button.ghost{background:transparent;border-color:transparent;color:var(--muted)}button.link{padding:4px 6px;color:var(--accent);border:0;background:transparent;min-height:44px}button.danger{color:var(--danger);border-color:var(--danger)}button.square{width:44px;padding:10px;flex-shrink:0}.icon{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}.primary .icon{stroke:currentColor}svg{max-width:100%}input,select,textarea{border:1px solid var(--line);border-radius:8px;background:var(--surface);padding:10px 12px;min-height:44px;max-width:100%}input::placeholder,textarea::placeholder{color:var(--muted)}textarea{resize:vertical;width:100%;min-height:108px}h1,h2,h3,h4,p{margin:0}h1{font-size:25px;line-height:1.3;letter-spacing:-.5px}h2{font-size:19px;line-height:1.4}h3{font-size:15px}small,.small{font-size:12px}.muted{color:var(--muted)}.mono{font-family:Consolas,'Cascadia Code',monospace;font-size:12px;overflow-wrap:anywhere}.row{display:flex;align-items:center;gap:12px}.row.wrap{flex-wrap:wrap}.between{justify-content:space-between}.grow{flex:1;min-width:0}.stack{display:flex;flex-direction:column;gap:18px}.stack.tight{gap:10px}.section-label{font-size:11px;text-transform:uppercase;letter-spacing:1.2px;color:var(--muted);font-weight:700}.spacer{flex:1}.tag{display:inline-flex;gap:5px;align-items:center;white-space:nowrap;padding:3px 8px;border-radius:5px;background:var(--soft);color:var(--muted);font-size:11px;font-weight:500}.tag.blue{color:var(--accent);background:var(--accent-soft)}.tag.green{color:var(--success);background:var(--success-bg)}.tag.amber{color:var(--warn);background:var(--warn-bg)}.tag.red{color:var(--danger);background:var(--danger-bg)}.tag .icon{height:12px;width:12px}.dot{width:6px;height:6px;border-radius:100%;background:currentColor}.divider{border:0;border-top:1px solid var(--line);margin:0}.footnote{color:var(--muted);font-size:12px;line-height:1.7}.eyebrow{color:var(--accent);font-size:11px;font-weight:700;letter-spacing:1.6px}
|
|
13
|
+
.demo-bar{height:48px;padding:0 18px;background:var(--surface);border-bottom:1px solid var(--line);display:flex;align-items:center;gap:8px;position:relative;z-index:20}.demo-caption{font-size:11px;color:var(--muted);margin-right:auto}.demo-bar button,.demo-bar select{min-height:34px;font-size:12px;padding:5px 10px}.demo-bar .square{width:34px}.demo-bar .demo-tab.active{color:var(--accent);background:var(--accent-soft);border-color:transparent}.demo-bar label{display:flex;gap:6px;align-items:center;font-size:11px;color:var(--muted)}.app-shell{height:calc(100vh - 48px);height:calc(100dvh - 48px);display:grid;grid-template-columns:188px minmax(0,1fr);overflow:hidden}.sidebar{background:var(--sidebar);border-right:1px solid var(--line);display:flex;flex-direction:column;padding:22px 14px;gap:24px;min-height:0}.brand{display:flex;gap:9px;align-items:center;padding:0 8px;color:var(--text);font-size:17px;font-weight:650;letter-spacing:.3px}.brand-mark{color:var(--accent);display:grid;place-items:center;width:31px;height:31px;border-radius:9px;background:var(--accent-soft)}.brand-mark .icon{width:23px;height:23px}.sidebar-group{display:flex;flex-direction:column;gap:4px}.sidebar .section-label{padding:0 12px;margin-bottom:5px}.side-item{justify-content:flex-start;min-height:43px;border:0;background:transparent;color:var(--muted);padding:10px 12px;font-size:13px;text-align:left;gap:10px}.side-item.active{background:var(--accent-soft);color:var(--accent);font-weight:600}.side-item.inert{display:flex;align-items:center;border-radius:8px;cursor:default}.sidebar-foot{margin-top:auto;border-top:1px solid var(--line);padding-top:15px}.avatar{width:29px;height:29px;display:grid;place-items:center;background:var(--soft);border:1px solid var(--line);border-radius:9px;font-size:12px;flex-shrink:0}.content-shell{min-width:0;min-height:0;display:flex;flex-direction:column;position:relative}.app-header{height:62px;min-height:62px;border-bottom:1px solid var(--line);background:var(--surface);display:flex;align-items:center;gap:12px;padding:0 28px}.breadcrumb{display:flex;align-items:center;gap:10px;font-size:12px;color:var(--muted)}.breadcrumb b{color:var(--text);font-weight:500}.app-header .tag{margin-left:auto}.scroll-area{overflow:auto;min-height:0;flex:1}.page{padding:26px 30px 34px;max-width:1250px;margin:0 auto}.page-heading{display:flex;gap:18px;align-items:center;justify-content:space-between;margin-bottom:24px}.page-heading p{margin-top:6px;color:var(--muted);font-size:13px}.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}.panel-pad{padding:24px}.status-line{display:flex;gap:9px;align-items:center;font-size:12px;color:var(--success)}.intro-symbol{height:42px;width:42px;border-radius:12px;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center}.intro-symbol .icon{height:24px;width:24px}.intro-grid{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);gap:20px}.settings-row{display:flex;gap:20px;align-items:center;justify-content:space-between;padding:19px 0;border-top:1px solid var(--line)}.settings-row.first{border:0;padding-top:0}.settings-row p{font-size:12px;color:var(--muted);margin-top:4px;max-width:650px}.switch{width:42px;min-width:42px;min-height:24px;height:24px;border-radius:30px;background:var(--line);padding:3px;border:0;position:relative}.switch:hover:not(:disabled){border:0;background:var(--line)}.switch:after{content:'';width:18px;height:18px;border-radius:100%;background:var(--surface);position:absolute;left:3px;top:3px;box-shadow:0 1px 3px #0002}.switch[aria-checked="true"]{background:var(--accent)}.switch[aria-checked="true"]:after{left:21px}.metric-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:20px 0;margin:18px 0}.metric strong{font-size:26px;font-weight:600;display:block;line-height:1.4;font-variant-numeric:tabular-nums}.metric span{font-size:11px;color:var(--muted)}.soft-note{padding:13px 15px;background:var(--soft);border-radius:9px;color:var(--muted);font-size:12px;display:flex;gap:9px}.soft-note.blue{background:var(--accent-soft);color:var(--accent)}.soft-note.amber{background:var(--warn-bg);color:var(--warn)}details>summary{cursor:pointer;min-height:44px;display:flex;align-items:center;justify-content:space-between;color:var(--muted);list-style:none}details>summary:after{content:'+';font-size:19px}details[open]>summary:after{content:'−'}.save-bar{position:sticky;bottom:0;z-index:8;display:flex;justify-content:flex-end;align-items:center;gap:10px;background:var(--surface);border-top:1px solid var(--line);padding:12px 24px}.save-bar .muted{margin-right:auto;font-size:12px}
|
|
14
|
+
.workspace-header{padding:21px 28px 0;background:var(--surface)}.workspace-title{display:flex;align-items:center;gap:12px}.workspace-title h1{font-size:22px}.workspace-title .footnote{margin-top:3px}.primary-tabs{display:flex;align-items:center;gap:23px;margin-top:18px;border-bottom:1px solid var(--line)}.tab{border:0;border-radius:0;position:relative;background:transparent;color:var(--muted);padding:11px 4px;min-height:47px;white-space:nowrap}.tab.active{color:var(--accent);font-weight:600;background:transparent}.tab.active:after{content:'';position:absolute;height:2px;background:var(--accent);left:0;right:0;bottom:-1px}.tab:hover:not(:disabled){border:0;background:transparent}.counter{padding:0 6px;border-radius:5px;font-size:11px;background:var(--soft);color:var(--muted)}.active .counter{background:var(--accent-soft);color:var(--accent)}.workspace-body{padding:22px 28px 28px;overflow:auto;flex:1;min-height:0}.filter-bar{display:flex;align-items:center;gap:10px;margin-bottom:16px;flex-wrap:wrap}.search-box{position:relative;flex:1;min-width:160px}.search-box input{width:100%;padding-left:37px}.search-box>.icon{position:absolute;top:13px;left:12px;color:var(--muted)}.segmented{display:inline-flex;border:1px solid var(--line);border-radius:8px;padding:3px;background:var(--soft);gap:2px}.segmented button{background:transparent;border:0;border-radius:6px;min-height:36px;padding:6px 10px;font-size:12px;color:var(--muted)}.segmented button.active{background:var(--surface);color:var(--text);box-shadow:0 1px 3px #0000000c}.sub-toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;gap:12px;flex-wrap:wrap}.memory-table{width:100%;border-collapse:collapse;font-size:12px}.memory-table th{font-size:11px;font-weight:500;color:var(--muted);text-align:left;background:var(--soft);padding:11px 17px}.memory-table td{padding:16px 17px;border-top:1px solid var(--line);vertical-align:middle}.memory-table th:first-child{border-radius:12px 0 0 0}.memory-table th:last-child{border-radius:0 12px 0 0}.memory-title{font-size:13px;font-weight:500;text-align:left;border:0;padding:0;background:transparent;justify-content:flex-start;min-height:28px}.memory-title:hover:not(:disabled){background:transparent;border:0;color:var(--accent)}.memory-table .title-cell{width:46%}.memory-meta{margin-top:5px;font-size:11px;color:var(--muted);display:flex;gap:8px;align-items:center;flex-wrap:wrap}.memory-table tr:hover td{background:color-mix(in srgb,var(--accent-soft) 38%,var(--surface))}.table-foot{padding:12px 17px;border-top:1px solid var(--line);font-size:11px;color:var(--muted);display:flex;justify-content:space-between}.entity-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:15px}.entity-card{padding:20px;text-align:left;align-items:flex-start;flex-direction:column;white-space:normal}.entity-card .entity-name{font-size:15px;font-weight:600}.entity-icon{width:36px;height:36px;border-radius:9px;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center}.badge-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap}.entity-card p{font-size:12px;color:var(--muted)}
|
|
15
|
+
.chat-tabs{display:flex;align-items:center;gap:18px;margin-left:20px;height:100%}.chat-tabs button{height:100%;padding:0 8px}.chat-scroll{overflow:auto;flex:1;padding:35px max(32px,calc((100% - 760px)/2)) 28px;min-height:0;transition:padding .2s}.chat-scroll.has-drawer{padding-right:506px;padding-left:34px}.chat-message{margin-bottom:25px;display:flex;gap:13px}.chat-message.user{justify-content:flex-end}.user-bubble{background:var(--soft);border-radius:14px 14px 3px 14px;padding:15px 18px;max-width:85%;font-size:14px}.assistant-text{line-height:1.9;min-width:0}.assistant-text p{margin-bottom:12px}.check-list{padding-left:18px;margin:12px 0}.check-list li{margin-bottom:12px}.cite{min-height:25px;border:0;display:inline-flex;align-items:center;padding:1px 6px;font-size:10px;vertical-align:middle;color:var(--accent);background:var(--accent-soft);border-radius:4px}.answer-source{display:flex;gap:7px;flex-wrap:wrap;margin-top:16px}.source-chip{font-size:11px;min-height:32px;padding:6px 9px}.composer{background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow);padding:12px;margin:0 30px 22px}.composer textarea{border:0;min-height:53px;background:transparent;outline-offset:0;resize:vertical;padding:7px 8px}.composer .footnote{font-size:10px;padding-left:8px}.composer.has-drawer{margin-right:506px}.drawer{position:absolute;right:0;top:62px;bottom:0;width:472px;border-left:1px solid var(--line);background:var(--surface);z-index:10;display:flex;flex-direction:column;box-shadow:-10px 0 30px #23304408}.drawer-head{padding:22px 22px 14px;display:flex;gap:12px;align-items:flex-start}.drawer-head h2{font-size:18px}.drawer-head p{font-size:12px;color:var(--muted);margin-top:4px}.drawer-tabs{padding:0 22px}.drawer-tabs .primary-tabs{margin-top:0;gap:21px}.drawer-content{padding:18px 22px;overflow:auto;flex:1;min-height:0}.memory-card{padding:17px 16px;border:1px solid var(--line);border-radius:10px;margin-bottom:12px;background:var(--surface)}.memory-card h3{font-size:13px;margin-top:10px;line-height:1.6}.memory-card .footnote{margin-top:5px;font-size:11px}.memory-card .card-actions{display:flex;justify-content:space-between;align-items:center;margin-top:10px;gap:8px}.memory-card .card-actions button{font-size:11px;min-height:36px}.memory-card.excluded{background:var(--soft)}.why{font-size:11px;color:var(--muted);background:var(--soft);padding:10px;border-radius:6px;margin-top:8px}.drawer-footer{border-top:1px solid var(--line);padding:13px 22px;display:flex;gap:9px;flex-wrap:wrap}.drawer-footer button{font-size:12px}.drawer-footer .primary{flex:1}.drawer-notice{font-size:11px;color:var(--muted);padding:12px 22px;background:var(--soft)}
|
|
16
|
+
.graph-split{display:grid;grid-template-columns:minmax(0,1fr) 286px;gap:16px;align-items:stretch}.graph-panel{min-width:0;overflow:hidden;display:flex;flex-direction:column}.graph-toolbar{padding:12px 15px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;gap:8px}.graph-toolbar button{min-height:34px;padding:5px 10px;font-size:11px}.graph-stage{position:relative;min-height:355px;flex:1;overflow:hidden;background-color:var(--surface);background-image:radial-gradient(var(--line) .75px,transparent .75px);background-size:18px 18px}.graph-stage svg{display:block;width:100%;height:100%;min-height:355px}.graph-node{cursor:pointer}.graph-node rect.node-bg{fill:var(--surface);stroke:var(--line);stroke-width:1.4}.graph-node.selected rect.node-bg{fill:var(--accent-soft);stroke:var(--accent);stroke-width:2}.graph-node .node-label{fill:var(--text);font-size:14px;font-weight:550}.graph-node .node-type{fill:var(--muted);font-size:10px}.graph-node .node-sub{fill:var(--muted);font-size:11px}.graph-node .node-dot{fill:var(--accent)}.graph-node.historical rect.node-bg{stroke:var(--muted);stroke-dasharray:4 4;fill:var(--soft)}.graph-node.inferred rect.node-bg{stroke:var(--warn);stroke-dasharray:5 4}.graph-edge{cursor:pointer}.graph-edge .edge-line{fill:none;stroke:#abbad4;stroke-width:1.5}.graph-edge.selected .edge-line{stroke:var(--accent);stroke-width:2.6}.graph-edge .edge-label{font-size:10px;fill:var(--muted)}.graph-edge .label-bg{fill:var(--surface);stroke:var(--line);stroke-width:.7}.graph-edge.inferred .edge-line{stroke:var(--warn);stroke-dasharray:5 5}.graph-edge.selected .label-bg{fill:var(--accent-soft);stroke:var(--accent)}.graph-legend{position:absolute;bottom:12px;left:15px;display:flex;gap:12px;flex-wrap:wrap;font-size:10px;color:var(--muted);background:var(--surface);padding:5px 8px;border-radius:6px;border:1px solid var(--line)}.graph-legend i{width:16px;height:0;border-top:2px solid #a4b3cb;display:inline-block;margin-right:4px}.graph-legend .dashed{border-top-style:dashed;border-color:var(--warn)}.graph-info{font-size:11px;color:var(--muted);padding:10px 15px;border-top:1px solid var(--line)}.timeline-strip{border-top:1px solid var(--line);padding:12px 16px;background:var(--surface)}.timeline-strip input{width:100%;padding:0;min-height:24px;accent-color:var(--accent);border:0}.timeline-labels{display:flex;justify-content:space-between;color:var(--muted);font-size:10px}.detail-card{padding:20px;min-width:0;overflow-wrap:anywhere}.detail-card h3{margin-top:11px;margin-bottom:7px;font-size:16px}.detail-card .section-label{margin-top:20px;margin-bottom:8px}.detail-card .footnote{font-size:11px}.evidence-excerpt{border-left:2px solid var(--accent);padding:10px 12px;background:var(--accent-soft);font-size:12px;line-height:1.8;border-radius:0 6px 6px 0}.evidence-excerpt.amber{border-left-color:var(--warn);background:var(--warn-bg);color:var(--warn)}.key-values{display:grid;grid-template-columns:72px minmax(0,1fr);gap:8px 10px;font-size:12px}.key-values dt{color:var(--muted)}.key-values dd{margin:0;overflow-wrap:anywhere}.mini-path{display:flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:11px;color:var(--muted)}.mini-path b{font-weight:500;color:var(--accent);background:var(--accent-soft);border-radius:5px;padding:4px 7px}.graph-query{display:flex;gap:8px;margin-bottom:14px}.graph-query input{flex:1;min-width:0}.history-banner{padding:10px 14px;margin-bottom:12px;background:var(--warn-bg);color:var(--warn);font-size:12px;border-radius:8px;display:flex;justify-content:space-between;align-items:center;gap:10px}.history-banner button{font-size:11px;min-height:32px;background:transparent;border-color:currentColor}
|
|
17
|
+
.review-layout{display:grid;grid-template-columns:235px minmax(0,1fr);gap:18px}.review-queue{display:flex;flex-direction:column;gap:8px}.queue-card{display:flex;flex-direction:column;align-items:flex-start;padding:15px;gap:8px;white-space:normal;text-align:left;border-radius:10px;background:var(--surface)}.queue-card.active{background:var(--accent-soft);border-color:var(--accent)}.queue-card b{font-size:13px}.queue-card p{font-size:11px;color:var(--muted)}.review-detail{padding:23px;min-width:0}.review-detail h2{font-size:18px}.comparison{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin:20px 0}.comparison>section{border:1px solid var(--line);border-radius:9px;padding:17px}.comparison p{font-size:13px;margin:12px 0;line-height:1.8}.review-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:18px}.review-actions button{font-size:12px}.audit-item{display:flex;gap:12px;margin-top:17px;font-size:12px}.audit-dot{height:9px;width:9px;background:var(--accent);border-radius:100%;margin-top:5px;flex-shrink:0}.settings-tabs{display:flex;gap:12px;margin-bottom:17px;flex-wrap:wrap}.schema-types{display:flex;gap:6px;flex-wrap:wrap;margin:15px 0}.schema-types .tag{padding:7px 10px}.rule-row{display:flex;align-items:center;gap:9px;padding:13px 0;border-top:1px solid var(--line);font-size:12px}.rule-row select{min-width:120px}.rule-arrow{color:var(--muted)}.validation-result{padding:13px 15px;border-radius:8px;font-size:12px;margin-top:13px;background:var(--success-bg);color:var(--success)}.validation-result.bad{background:var(--danger-bg);color:var(--danger)}
|
|
18
|
+
.state-screen{min-height:390px;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:35px;gap:15px}.state-screen .intro-symbol{height:54px;width:54px}.state-screen p{max-width:430px;font-size:13px;color:var(--muted)}.skeleton{height:12px;border-radius:5px;background:var(--soft);width:260px;max-width:100%;margin:5px 0}.skeleton.short{width:180px}.offline-banner{display:flex;gap:8px;align-items:center;justify-content:space-between;background:var(--warn-bg);color:var(--warn);padding:10px 14px;border-radius:8px;margin-bottom:14px;font-size:12px}.offline-banner button{min-height:32px;font-size:11px;background:transparent;border-color:var(--warn)}.toast-region{position:fixed;bottom:25px;left:50%;transform:translateX(-50%);z-index:100;max-width:min(90%,660px)}.toast{background:var(--text);color:var(--surface);padding:12px 16px;border-radius:10px;display:flex;align-items:center;gap:15px;box-shadow:0 7px 24px #0002;font-size:12px}.toast button{border:0;background:transparent;color:inherit;padding:3px 6px;min-height:32px;font-weight:600}.toast .icon{width:16px;height:16px}.overlay-panel{position:absolute;inset:0;background:#17243c22;backdrop-filter:blur(1px);z-index:35;display:flex;justify-content:flex-end}.evidence-drawer{width:460px;max-width:100%;height:100%;background:var(--surface);box-shadow:-10px 0 35px #13203118;display:flex;flex-direction:column}.evidence-drawer .drawer-content{padding:20px 26px}.evidence-drawer .primary-tabs{margin-top:0}.quote-block{padding:18px;background:var(--soft);border-radius:9px;font-size:13px;line-height:1.85;margin-top:12px}.quote-block mark{background:var(--accent-soft);color:var(--accent);padding:1px 3px}.source-caption{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--muted);margin-top:12px}.version-row{padding:16px 0;border-bottom:1px solid var(--line);display:flex;gap:13px;font-size:12px}.version-row .version-date{width:68px;color:var(--muted);flex-shrink:0}.relation-row{display:flex;align-items:center;gap:10px;padding:12px 0;border-bottom:1px solid var(--line);font-size:12px;flex-wrap:wrap}.relation-row b{font-weight:500}
|
|
19
|
+
dialog{border:1px solid var(--line);border-radius:15px;background:var(--surface);color:var(--text);padding:0;width:530px;max-width:calc(100% - 32px);max-height:calc(100dvh - 48px);box-shadow:0 24px 80px #12243b33}dialog::backdrop{background:#16233c55;backdrop-filter:blur(2px)}.modal-head{padding:21px 24px 16px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--line)}.modal-head h2{font-size:18px}.modal-body{padding:22px 24px;display:flex;flex-direction:column;gap:17px;max-height:65vh;overflow:auto}.field{display:flex;flex-direction:column;gap:6px;font-size:12px}.field>label{font-weight:600}.field p{font-size:11px;color:var(--muted)}.modal-footer{display:flex;justify-content:flex-end;gap:10px;padding:16px 24px;border-top:1px solid var(--line)}.inline-error{color:var(--danger);font-size:12px;line-height:1.7;min-height:0}.change-preview{background:var(--soft);padding:15px;border-radius:8px;font-size:12px}.change-preview ul{padding-left:16px;margin:8px 0 0}.inline-code{background:var(--soft);padding:2px 5px;border-radius:4px;font:11px Consolas,monospace}.mobile-only{display:none}.flow-diagram{width:100%;height:auto}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
20
|
+
@media(min-width:1700px){.page{max-width:1270px}.workspace-body{padding:28px 40px}.graph-stage,.graph-stage svg{min-height:450px}.drawer{width:500px}.chat-scroll.has-drawer{padding-right:540px}}
|
|
21
|
+
@media(max-width:1180px){.app-shell{grid-template-columns:158px minmax(0,1fr)}.page,.workspace-body{padding:22px}.app-header{padding:0 22px}.graph-split{grid-template-columns:minmax(0,1fr)}.detail-card{display:grid;grid-template-columns:1fr 1fr;gap:12px}.detail-card .section-label{margin-top:8px}.detail-card h3{margin-top:0}.graph-stage,.graph-stage svg{min-height:400px}.intro-grid{grid-template-columns:minmax(0,1fr)}.chat-scroll.has-drawer{padding-right:430px;padding-left:24px}.drawer{width:410px}.composer.has-drawer{margin-right:430px}.entity-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.review-layout{grid-template-columns:205px minmax(0,1fr)}}
|
|
22
|
+
@media(max-width:800px){.app-shell{grid-template-columns:1fr}.sidebar{display:none}.demo-caption .hide-narrow{display:none}.demo-bar{padding:0 10px;gap:4px}.demo-bar label span{display:none}.demo-bar button,.demo-bar select{padding:5px 7px}.app-header{padding:0 18px}.page{padding:24px 18px}.workspace-header{padding:20px 18px 0}.workspace-body{padding:18px}.chat-scroll,.chat-scroll.has-drawer{padding:24px 20px}.composer,.composer.has-drawer{margin:0 18px 18px}.drawer{width:440px;max-width:100%;box-shadow:-20px 0 70px #10213d22}.page-heading{align-items:flex-start}.page-heading h1{font-size:23px}.primary-tabs{gap:21px}.review-layout{grid-template-columns:minmax(0,1fr)}.review-queue{flex-direction:row;overflow:auto}.queue-card{min-width:180px}.graph-stage,.graph-stage svg{min-height:345px}.detail-card{display:block}.mobile-only{display:inline-flex}.breadcrumb .hide-narrow{display:none}.memory-table th,.memory-table td{padding:13px 12px}.memory-table th:nth-child(4),.memory-table td:nth-child(4){display:none}.memory-table .title-cell{width:55%}.app-header .tag{font-size:10px}.settings-row{gap:12px}.settings-row select{max-width:170px}}
|
|
23
|
+
@media(max-width:520px){.demo-bar{height:auto;min-height:46px;flex-wrap:wrap;padding:6px 8px;gap:5px}.demo-caption{font-size:10px;flex:1}.demo-bar .reset-label{display:none}.demo-bar label{font-size:10px}.demo-bar select{max-width:105px}.app-shell{height:calc(100dvh - 82px)}.demo-bar .demo-tab{font-size:11px;min-height:32px}.demo-bar .square{min-height:32px;width:32px}.app-header{height:auto;min-height:62px;flex-wrap:wrap;gap:0 12px;padding:8px 14px}.chat-tabs{margin-left:0;gap:16px}.chat-tabs .tab{height:40px;min-height:40px}.app-header .tag{display:none}.app-header .breadcrumb{font-size:11px}.page,.workspace-body{padding:18px 14px}.page-heading{flex-direction:column;gap:14px;margin-bottom:18px}.panel-pad{padding:18px}.intro-grid{gap:15px}.settings-row{align-items:flex-start}.settings-row .switch{margin-top:4px}.settings-row p{font-size:11px}.settings-row select{max-width:140px;font-size:11px;padding:8px}.metric strong{font-size:22px}.primary-tabs{gap:12px;overflow-x:auto}.tab{font-size:12px}.workspace-title h1{font-size:20px}.workspace-title .footnote{font-size:11px}.workspace-title>button{padding:9px;font-size:11px}.workspace-header{padding:18px 14px 0}.filter-bar{gap:8px}.filter-bar .search-box{flex-basis:100%}.filter-bar select{flex:1;min-width:0;font-size:11px}.filter-bar>button{padding:8px 12px;font-size:12px}.memory-table thead{display:none}.memory-table,.memory-table tbody,.memory-table tr,.memory-table td{display:block;width:100%}.memory-table tr{padding:12px 15px;border-bottom:1px solid var(--line)}.memory-table td{border:0;padding:3px 0}.memory-table .title-cell{width:100%}.memory-table td:nth-child(2){font-size:11px;color:var(--muted)}.memory-table td:nth-child(3){padding-top:8px}.memory-title{min-height:35px;font-size:13px}.table-foot{flex-direction:column;gap:4px}.entity-grid{grid-template-columns:minmax(0,1fr)}.graph-query{flex-wrap:wrap}.graph-query input{flex-basis:100%;font-size:12px}.graph-query button{font-size:11px}.graph-toolbar{flex-wrap:wrap;padding:10px}.graph-stage{overflow:auto;min-height:390px}.graph-stage svg{width:640px;max-width:none;height:390px;min-height:390px}.graph-legend{position:sticky;bottom:5px;left:5px;margin:0 5px;width:max-content;max-width:calc(100% - 10px);font-size:9px}.graph-toolbar .segmented button{font-size:10px;padding:5px 8px}.comparison{grid-template-columns:minmax(0,1fr)}.review-detail{padding:18px}.review-actions{flex-direction:column}.review-actions button{width:100%}.drawer{top:62px;width:100%}.drawer-head{padding:18px}.drawer-content{padding:16px 18px}.drawer-tabs{padding:0 18px}.drawer-footer{padding:12px 18px}.chat-scroll{padding:24px 17px}.chat-message{gap:9px}.assistant-text{font-size:13px}.user-bubble{font-size:13px}.composer{margin:0 14px 14px}.composer .footnote{max-width:175px}.composer button{font-size:12px}.evidence-drawer .drawer-content{padding:18px}.rule-row{flex-wrap:wrap}.rule-row select{min-width:100px}.toast{font-size:11px}.modal-head,.modal-body,.modal-footer{padding:17px 18px}.modal-footer button{font-size:12px}.page-heading .tag{white-space:normal}}
|
|
24
|
+
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/* Height follows the real prototype-toolbar row count; no fixed mobile toolbar assumption. */
|
|
28
|
+
#app{height:100vh;height:100dvh;display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden}
|
|
29
|
+
#app>.app-shell{height:auto;min-height:0}
|
|
30
|
+
@media(max-width:520px){
|
|
31
|
+
.demo-bar{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(86px,1.1fr) 32px 32px;height:auto;gap:5px;padding:6px 8px}
|
|
32
|
+
.demo-caption{grid-column:1/-1;min-width:0;display:flex;align-items:center;gap:5px}
|
|
33
|
+
.demo-bar label{min-width:0}.demo-bar select{width:100%;min-width:0;max-width:100%}
|
|
34
|
+
.demo-bar .demo-tab{white-space:nowrap;font-size:10px;padding:5px 4px}.demo-bar .demo-tab .icon{width:14px;height:14px}
|
|
35
|
+
.drawer .card-actions button,.drawer button.link{min-height:44px}
|
|
36
|
+
.switch{width:44px;min-width:44px;min-height:44px;height:44px;background:transparent!important}
|
|
37
|
+
.switch:before{content:'';position:absolute;width:42px;height:24px;top:10px;left:1px;border-radius:30px;background:var(--line)}
|
|
38
|
+
.switch[aria-checked="true"]:before{background:var(--accent)}.switch:after{top:13px;left:4px}.switch[aria-checked="true"]:after{left:22px}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* v1.1 native DSH alignment. Alias values copied from the shipped theme for offline preview only. Production must inherit host tokens and primitives. */
|
|
42
|
+
body{--dsw-alias-bg-base:#fff;--dsw-alias-bg-layer-1:#fff;--dsw-alias-bg-layer-2:#fff;--dsw-alias-bg-module-platform:#f5f6f7;--dsw-alias-brand-primary:#0f1115;--dsw-alias-border-l2:#0000001a;--dsw-alias-border-l4:#00000029;--dsw-alias-label-primary:#0f1115;--dsw-alias-label-secondary:#61666b;--dsw-alias-label-tertiary:#81858c;--dsw-alias-interactive-bg-hover:#2631480f;--dsw-font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei","Helvetica Neue",Helvetica,Arial,sans-serif;--bg:var(--dsw-alias-bg-base);--surface:var(--dsw-alias-bg-layer-1);--soft:var(--dsw-alias-bg-module-platform);--sidebar:var(--dsw-alias-bg-module-platform);--text:var(--dsw-alias-label-primary);--muted:var(--dsw-alias-label-secondary);--line:var(--dsw-alias-border-l2);--accent:var(--dsw-alias-brand-primary);--accent-soft:var(--dsw-alias-bg-module-platform);--shadow:none;font-family:var(--dsw-font-family)}
|
|
43
|
+
body[data-ds-dark-theme]{--dsw-alias-bg-base:#151517;--dsw-alias-bg-layer-1:#232324;--dsw-alias-bg-layer-2:#2c2c2e;--dsw-alias-bg-module-platform:#353638;--dsw-alias-brand-primary:#f9fafb;--dsw-alias-border-l2:#ffffff1f;--dsw-alias-border-l4:#fff3;--dsw-alias-label-primary:#f9fafb;--dsw-alias-label-secondary:#cfd3d6;--dsw-alias-label-tertiary:#adb2b8;--dsw-alias-interactive-bg-hover:#ffffff14;}
|
|
44
|
+
body button.primary{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-base)}
|
|
45
|
+
body button:hover:not(:disabled){border-color:var(--dsw-alias-border-l4);background:var(--dsw-alias-interactive-bg-hover)}
|
|
46
|
+
body button.primary:hover:not(:disabled){background:var(--dsw-alias-brand-primary);filter:brightness(.88)}
|
|
47
|
+
body .page-heading h1{font-size:20px;line-height:28px;letter-spacing:0;margin-top:0!important;font-weight:600}
|
|
48
|
+
body .page-heading p{font-size:12px;margin-top:4px}body .page-heading{margin-bottom:18px}
|
|
49
|
+
body h2{font-size:16px;font-weight:600}body h3{font-size:14px;font-weight:600}body .workspace-title h1{font-size:18px;line-height:26px}
|
|
50
|
+
body .panel-pad{padding:16px 18px}body .panel{box-shadow:none}body .settings-row{padding:16px 0}body .settings-row p{font-size:12px;line-height:18px}
|
|
51
|
+
body .intro-symbol{height:32px;width:32px;border-radius:8px}body .intro-symbol .icon{width:19px;height:19px}
|
|
52
|
+
body .intro-grid{grid-template-columns:minmax(0,1.45fr) minmax(0,1fr);gap:16px}body .metric strong{font-size:22px;line-height:30px}body .metric-row{padding:14px 0;margin:16px 0}
|
|
53
|
+
body .tag{border-radius:5px;font-weight:400}body .tag.blue{color:var(--text);background:var(--soft)}
|
|
54
|
+
body .graph-stage{background-image:none}body .graph-toolbar label{min-height:44px;cursor:pointer}body .graph-toolbar input[type="checkbox"]{width:18px;height:18px}body .graph-node rect.node-bg{stroke:var(--dsw-alias-border-l4)}body .graph-node.selected rect.node-bg{stroke:var(--accent)}
|
|
55
|
+
body .graph-node .node-dot{fill:var(--muted)}body .graph-edge .edge-line{stroke:var(--dsw-alias-label-tertiary)}body .graph-edge.selected .edge-line{stroke:var(--accent)}
|
|
56
|
+
body .graph-edge.inferred .edge-line{stroke:var(--warn)}body .graph-info{font-size:12px}
|
|
57
|
+
body .evidence-excerpt{border-left-width:2px}body .side-item{font-size:13px}body .sidebar{padding-top:20px}
|
|
58
|
+
body .soft-note.blue{color:var(--text)}body .workspace-header{padding-top:18px}body .primary-tabs{margin-top:14px}
|
|
59
|
+
body .drawer-head h2{font-size:16px}body .drawer{box-shadow:-1px 0 8px #00000008}body .overlay-panel{backdrop-filter:none}
|
|
60
|
+
@media(max-width:1180px){body .intro-grid{grid-template-columns:minmax(0,1fr)}}
|
|
61
|
+
@media(max-width:520px){.primary-tabs{overflow-y:hidden}.tab.active:after{bottom:0}body .page-heading h1{font-size:19px}body .panel-pad{padding:16px}body .workspace-title h1{font-size:18px}}
|
|
62
|
+
|
|
63
|
+
</style>
|
|
64
|
+
</head>
|
|
65
|
+
<body>
|
|
66
|
+
<div id="app" data-prototype-state="ready"></div><div id="modal-root"></div><div id="toast-root" class="toast-region" aria-live="polite"></div>
|
|
67
|
+
<noscript>此交互原型需要 JavaScript,所有数据仅保留在页面内存中。</noscript>
|
|
68
|
+
<script>window.__prototypeErrors=[]; window.addEventListener("error",function(e){window.__prototypeErrors.push(e.message)}); window.addEventListener("unhandledrejection",function(e){window.__prototypeErrors.push(String(e.reason))});</script>
|
|
69
|
+
<script>
|
|
70
|
+
/* All business records below are invented fixtures. No production data, credentials, or network calls. */
|
|
71
|
+
window.OC = (() => {
|
|
72
|
+
const icons={memory:'M9 4a3 3 0 0 0-5 2 3 3 0 0 0-1 5 3 3 0 0 0 2 5 3 3 0 0 0 4 4V4m6 0a3 3 0 0 1 5 2 3 3 0 0 1 1 5 3 3 0 0 1-2 5 3 3 0 0 1-4 4V4M9 8H7m8 6h3M9 16H6m9-8h3',search:'m21 21-5-5M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0',settings:'M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8M10 3l-1 3-3 1-3-1-1 4 3 2v3l-2 2 3 3 3-1 3 2 3-2 3 1 3-3-2-3 1-3 2-2-2-3-3 1-3-3z',graph:'M5 6h5m4 0h5M7 7l4 9m7-9-5 9M4 3h5v5H4zM16 3h5v5h-5zM9 16h6v5H9z',list:'M8 6h13M8 12h13M8 18h13M3 6h.1M3 12h.1M3 18h.1',check:'m5 12 4 4L19 6',close:'m6 6 12 12M18 6 6 18',arrow:'M5 12h14m-5-5 5 5-5 5',back:'M19 12H5m5-5-5 5 5 5',expand:'M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5',file:'M6 3h8l4 4v14H6zM14 3v5h4M9 12h6m-6 4h6',shield:'m12 3 8 3v6c0 5-8 9-8 9s-8-4-8-9V6zM8 12l3 3 5-6',clock:'M12 8v5l3 2M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0',link:'m10 13 4-4m-6 7-1 1a4 4 0 0 1-6-6l5-5a4 4 0 0 1 6 0m0 12a4 4 0 0 0 6 0l5-5a4 4 0 0 0-6-6l-1 1',person:'M16 7a4 4 0 1 1-8 0 4 4 0 0 1 8 0M4 21v-2a8 8 0 0 1 16 0v2',folder:'M3 6V4h6l3 3h9v13H3z',alert:'m12 3 10 18H2zM12 9v5m0 3h.1',more:'M5 12h.1m6.9 0h.1m6.9 0h.1',sun:'M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1 1m12 12 1 1M5 19l1-1M18 6l1-1M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0',chat:'M21 15a3 3 0 0 1-3 3H8l-5 3V5a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3z',plus:'M12 5v14M5 12h14',layers:'m12 3 10 5-10 5L2 8zM2 12l10 5 10-5M2 16l10 5 10-5',refresh:'M20 6v5h-5M4 18v-5h5M5 8a8 8 0 0 1 14-3l1 3M4 16l1 3a8 8 0 0 0 14-3',external:'M14 3h7v7m0-7L10 14M10 3H3v18h18v-7',eye:'M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12m13 0a3 3 0 1 1-6 0 3 3 0 0 1 6 0',edit:'m4 16-1 5 5-1L21 7l-4-4zM14 6l4 4',archive:'M3 3h18v5H3zM5 8v13h14V8M9 12h6',lock:'M6 11V7a6 6 0 0 1 12 0v4M4 11h16v10H4zM12 15v3'};
|
|
73
|
+
const memories=[
|
|
74
|
+
{id:'m1',title:'重置工作区前,必须由用户明确二次确认',type:'约束',entity:'DEMO-042',scope:'Atlas 项目',status:'confirmed',source:'示例需求说明 §4.2',date:'09-18',text:'示例要求:工作区重置请求必须由用户提供明确的二次确认;未确认时不得执行。此条只描述要求,不证明代码已经实现。',why:'当前问题询问“重置前的条件”;此约束直接关联 DEMO-042。',evidence:'SRC-01',validFrom:'2026-09-18',basis:'需求要求'},
|
|
75
|
+
{id:'m2',title:'工作区重置必须校验项目与使用者归属',type:'约束',entity:'DEMO-042',scope:'Atlas 项目',status:'confirmed',source:'示例需求说明 §4.3',date:'09-18',text:'示例要求:仅允许操作当前项目授权范围内的工作区。负责人关系本身不构成访问许可。',why:'与本次接口所属需求直接关联,适用于当前项目范围。',evidence:'SRC-01',validFrom:'2026-09-18',basis:'需求要求'},
|
|
76
|
+
{id:'m3',title:'恢复后需要完成真实对话验收;结果尚未收录',type:'验收要求',entity:'DEMO-042',scope:'Atlas 项目',status:'confirmed',source:'示例验收清单 §2',date:'09-19',text:'示例清单要求:恢复后执行一次真实对话验收。当前演示资料没有该验收结果,不得据此声称已通过或已上线。',why:'补充“要求”与“已通过”之间的证据边界,防止将计划当成完成。',evidence:'SRC-02',validFrom:'2026-09-19',basis:'待验收'},
|
|
77
|
+
{id:'m4',title:'旧方案:重置前保存临时副本',type:'方案',entity:'DEMO-042',scope:'Atlas 项目',status:'historical',source:'示例方案 v1 §3',date:'09-10',text:'历史示例:旧版方案曾建议在重置前生成临时副本。该记录仅用于解释历史变化,不参与当前默认召回。',why:'已被更新方案替代,默认不注入当前回答。',evidence:'SRC-03',validFrom:'2026-09-10',validTo:'2026-09-14',basis:'历史方案'},
|
|
78
|
+
{id:'m5',title:'正式纪要:示例生产环境不保留副本',type:'决策',entity:'DEMO-042',scope:'Atlas 项目',status:'disputed',source:'示例评审纪要 v2 §5',date:'09-14',text:'仅为冲突演示:正式纪要陈述示例生产环境不保留副本。另有后续草稿提出相反说法,尚未确认范围,故不作为无争议事实注入。',why:'同一环境存在反向陈述,需先处理冲突。',evidence:'SRC-04',validFrom:'2026-09-14',basis:'正式纪要'},
|
|
79
|
+
{id:'m6',title:'文档服务可能依赖索引器,尚无直接证据',type:'推断',entity:'文档服务',scope:'Atlas 项目',status:'pending',source:'系统候选关系 · 演示',date:'09-19',text:'仅在同一段资料中共同出现,不能证明依赖。此候选不进入默认回答,也不参与传递推理。',why:'仅为实体共同出现产生的候选,不能当作依赖。',evidence:null,validFrom:'2026-09-19',basis:'未确认'},
|
|
80
|
+
{id:'m7',title:'本项目的关键决策使用中文说明并附来源',type:'偏好',entity:'沟通方式',scope:'个人偏好',status:'confirmed',source:'示例用户明确说明',date:'09-16',text:'演示偏好:在 Atlas 项目中,以中文解释关键决策并附上依据。偏好不扩大任何数据访问或执行权限。',why:'当前项目适用的个人表达偏好。',evidence:'SRC-05',validFrom:'2026-09-16',basis:'用户明确说明'},
|
|
81
|
+
{id:'m8',title:'后续草稿:重置前仍需保存副本',type:'方案',entity:'DEMO-042',scope:'Atlas 项目',status:'disputed',source:'示例讨论草稿 §1',date:'09-19',text:'仅为冲突演示:后续草稿写明应保留副本,但未说明是否仅针对测试环境。不能仅凭时间较新就自动覆盖正式纪要。',why:'与已有决策范围重叠,等待确认环境。',evidence:'SRC-06',validFrom:'2026-09-19',basis:'未批准草稿'},
|
|
82
|
+
{id:'m9',title:'测试报告记录了检查,但不代表生产验收完成',type:'记录',entity:'DEMO-042',scope:'Atlas 项目',status:'confirmed',source:'示例测试报告 §1',date:'09-20',text:'示例记录有测试步骤,但没有生产验收签字。系统保留“有测试记录”与“已获上线批准”的区别。',why:'提供项目进展的边界信息。',evidence:'SRC-07',validFrom:'2026-09-20',basis:'测试记录'}
|
|
83
|
+
];
|
|
84
|
+
const entities=[{id:'req',name:'DEMO-042',type:'需求',icon:'file',desc:'工作区重置需求',aliases:['042 需求','重置需求'],count:6},{id:'api',name:'工作区重置接口',type:'接口',icon:'layers',desc:'与需求有关联,但不是同一实体',aliases:['reset 接口'],count:3},{id:'project',name:'Atlas 文档服务',type:'项目',icon:'folder',desc:'演示项目 · 非真实业务空间',aliases:['Atlas'],count:8},{id:'rule',name:'用户二次确认',type:'约束',icon:'shield',desc:'未确认时,不得执行重置',aliases:['确认约束'],count:1},{id:'person',name:'示例负责人',type:'人员',icon:'person',desc:'经办关系不等于授权',aliases:[],count:1},{id:'source',name:'示例需求说明',type:'证据',icon:'file',desc:'原始片段与关系出处',aliases:['SRC-01'],count:2}];
|
|
85
|
+
const config={capture:true,distill:true,ontology:true,recall:true,model:'follow'};
|
|
86
|
+
const s={surface:'settings',view:'memories',subview:'list',drawer:false,drawerTab:'used',theme:'light',demoState:'ready',scope:'all',status:'confirmed',query:'',memory:structuredClone(memories),usedSnapshot:structuredClone(memories.filter(m=>['m1','m2','m3'].includes(m.id))),evidenceSnapshot:null,entities:structuredClone(entities),config:{...config},draft:{...config},excluded:[],used:['m1','m2','m3'],pinned:[],recallPaused:false,why:null,evidence:null,evidenceTab:'source',graphSelected:'req',selectedEdge:null,depth:1,inferred:false,graphMode:'graph',zoom:1,time:3,question:false,review:'conflict',resolved:false,merged:false,history:[],returnFrom:'settings',advanced:false,schemaSubject:'Requirement',schemaResult:null,modal:null,modalError:'',toast:null,extraMessages:[],chatDraft:'',chatScroll:0,revision:1,undo:null};
|
|
87
|
+
const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
|
88
|
+
const icon=(n)=>'<svg class="icon" aria-hidden="true" viewBox="0 0 24 24"><path d="'+(icons[n]||icons.file)+'"></path></svg>';
|
|
89
|
+
const statusMap={confirmed:['来源已核对','green','check'],historical:['历史 · 不再召回','','clock'],pending:['推断 · 待确认','amber','alert'],disputed:['存在冲突','red','alert'],retired:['已停止召回','','archive']};
|
|
90
|
+
const badge=(label,color='',i='')=>'<span class="tag '+color+'">'+(i?icon(i):'')+esc(label)+'</span>';
|
|
91
|
+
const statusTag=m=>{const a=statusMap[m.status]||['待确认','amber','alert'];return badge(...a)};
|
|
92
|
+
const canWrite=()=>!['offline','denied','disabled','loading','error'].includes(s.demoState);
|
|
93
|
+
const dirty=()=>JSON.stringify(s.config)!==JSON.stringify(s.draft);
|
|
94
|
+
const find=id=>s.memory.find(m=>m.id===id);
|
|
95
|
+
const nextUsed=()=>s.recallPaused||!s.config.recall?[]:[...new Set([...s.used,...s.pinned])].filter(id=>!s.excluded.includes(id)&&find(id)?.status==='confirmed');
|
|
96
|
+
const checkpoint=()=>{s.undo=JSON.stringify({memory:s.memory,entities:s.entities,resolved:s.resolved,merged:s.merged,history:s.history,revision:s.revision,excluded:s.excluded,pinned:s.pinned});};
|
|
97
|
+
const reset=()=>{Object.assign(s,{surface:'settings',view:'memories',subview:'list',drawer:false,drawerTab:'used',demoState:'ready',scope:'all',status:'confirmed',query:'',memory:structuredClone(memories),usedSnapshot:structuredClone(memories.filter(m=>['m1','m2','m3'].includes(m.id))),evidenceSnapshot:null,entities:structuredClone(entities),config:{...config},draft:{...config},excluded:[],used:['m1','m2','m3'],pinned:[],recallPaused:false,why:null,evidence:null,evidenceTab:'source',graphSelected:'req',selectedEdge:null,depth:1,inferred:false,graphMode:'graph',zoom:1,time:3,question:false,review:'conflict',resolved:false,merged:false,history:[],returnFrom:'settings',advanced:false,schemaSubject:'Requirement',schemaResult:null,modal:null,modalError:'',toast:null,extraMessages:[],chatDraft:'',chatScroll:0,revision:1,undo:null});};
|
|
98
|
+
return {s,esc,icon,badge,statusTag,canWrite,dirty,find,nextUsed,checkpoint,reset,memories};
|
|
99
|
+
})();
|
|
100
|
+
|
|
101
|
+
</script>
|
|
102
|
+
<script>
|
|
103
|
+
/* App surfaces built from shared demo state. The settings and session views never own separate memories. */
|
|
104
|
+
(() => {
|
|
105
|
+
const O=window.OC, {s,esc,icon,badge,statusTag,canWrite,dirty,find,nextUsed}=O;
|
|
106
|
+
const B=(a,t,cls='',extra='',i='')=>'<button type="button" class="'+cls+'" data-action="'+a+'" '+extra+'>'+(i?icon(i):'')+t+'</button>';
|
|
107
|
+
const countReview=()=>Number(!s.resolved)+Number(!s.merged);
|
|
108
|
+
const disabled=()=>canWrite()?'':'disabled';
|
|
109
|
+
function demoBar(){return '<div class="demo-bar" aria-label="原型演示工具"><span class="demo-caption">'+icon('layers')+' <b>设计原型</b><span class="hide-narrow"> · 演示数据 · 未接入真实记忆</span></span>'+B('surface-chat','会话入口','demo-tab '+(s.surface==='chat'?'active':''),'','chat')+B('surface-settings','设置入口','demo-tab '+(s.surface!=='chat'?'active':''),'','settings')+'<label><span>演示状态</span><select id="demo-state" aria-label="切换演示状态">'+[['ready','正常'],['loading','加载中'],['empty','无记忆'],['offline','离线只读'],['error','读取失败'],['denied','无权限'],['disabled','插件停用'],['stale','并发冲突'],['traceUnavailable','引用不可追踪'],['sourceUnavailable','来源不可访问'],['truncated','图谱截断']].map(([k,v])=>'<option value="'+k+'" '+(s.demoState===k?'selected':'')+'>'+v+'</option>').join('')+'</select></label>'+B('theme',icon('sun'),'square','aria-label="切换深浅主题"')+B('reset',icon('refresh')+'<span class="reset-label">重置演示</span>','ghost','aria-label="重置演示"')+'</div>';}
|
|
110
|
+
function sidebar(){const isSettings=s.surface!=='chat';return '<aside class="sidebar"><div class="brand"><span class="brand-mark">'+icon('memory')+'</span>DSH</div><div class="sidebar-group"><span class="section-label">'+(isSettings?'设置':'工作空间')+'</span>'+(isSettings?'<span class="side-item inert">'+icon('layers')+'通用</span><span class="side-item inert">'+icon('memory')+'模型</span><span class="side-item inert">'+icon('link')+'连接器</span>'+B('surface-settings','插件','side-item active','','settings'):'<span class="side-item inert">'+icon('folder')+'Atlas 示例项目</span>'+B('surface-chat','接口设计复盘','side-item active','','chat')+'<span class="side-item inert">'+icon('chat')+'版本发布检查</span>')+'</div><div class="sidebar-group"><span class="section-label">'+(isSettings?'当前插件':'最近关注')+'</span>'+(isSettings?'<span class="side-item inert active">'+icon('memory')+'agentOS context</span>':'<span class="side-item inert">'+icon('file')+'DEMO-042</span><span class="side-item inert">'+icon('shield')+'安全约束</span>')+'</div><div class="sidebar-foot stack tight">'+B(isSettings?'surface-chat':'surface-settings',isSettings?'返回对话':'设置','side-item','',isSettings?'back':'settings')+'<div class="row" style="padding:4px 10px"><span class="avatar">演</span><div><div class="small">本地演示</div><div class="footnote">没有连接生产数据</div></div></div></div></aside>';}
|
|
111
|
+
function header(){return '<header class="app-header"><div class="breadcrumb">'+(s.surface==='chat'?'<span class="hide-narrow">示例项目</span><span class="hide-narrow">/</span><b>接口设计复盘</b>':'<span>设置</span><span>/</span><span>插件</span><span>/</span><b>agentOS context</b>')+'</div>'+(s.surface==='chat'?'<nav class="chat-tabs" aria-label="会话视图">'+B('chat-close','对话','tab '+(!s.drawer?'active':''))+B('drawer-open',icon('memory')+'记忆 <span class="counter">'+(['denied','traceUnavailable'].includes(s.demoState)?'—':s.used.length)+'</span>','tab '+(s.drawer?'active':''),'id="memory-trigger" '+(s.demoState==='disabled'?'disabled':''))+'</nav>':'')+badge('仅本地原型','','lock')+'</header>';}
|
|
112
|
+
function stateScreen(){const m={traceUnavailable:['memory','本轮引用暂不可追踪','相关记忆不等于已注入的记忆。未取得真实引用记录时,不显示伪造的引用清单。','恢复演示追踪'],sourceUnavailable:['lock','原始来源当前不可访问','不能展示未授权原文,也不能用摘要冒充原文。请由来源所有者核对权限。','恢复示例来源'],loading:['memory','正在整理记忆','后台提炼不应阻塞对话。此处可模拟完成加载。','模拟加载完成'],empty:['memory','从一条重要的事开始','记忆会在你明确保存,或开启允许的自动捕获后出现。','载入示例资料'],error:['alert','暂时无法读取记忆','示例错误:读取超时。没有保存任何更改,可重试。','重试读取'],denied:['lock','没有此范围的查看权限','不展示节点、关系、数量或证据摘要。图谱关系不会扩大权限。','恢复演示权限'],disabled:['memory','agentOS context 当前未启用','现有对话仍可继续。开启插件后恢复记忆浏览与召回。','模拟启用']}[s.demoState];if(!m)return '';return '<section class="panel state-screen" role="status"><div class="intro-symbol">'+icon(m[0])+'</div><h2>'+m[1]+'</h2><p>'+m[2]+'</p>'+(s.demoState==='loading'?'<div class="skeleton"></div><div class="skeleton short"></div>':'')+B('restore',m[3],'primary')+'</section>';}
|
|
113
|
+
function offline(){return s.demoState==='offline'?'<div class="offline-banner">'+icon('alert')+'<span>离线 · 显示演示缓存,只能查看;修改和保存暂不可用。</span>'+B('restore','重试连接')+'</div>':'';}
|
|
114
|
+
function settings(){const n=s.memory.filter(m=>m.status==='confirmed').length;return '<div class="scroll-area"><main class="page" data-screen-label="设置中的 agentOS context 配置"><div class="page-heading"><div><h1>agentOS context 记忆库</h1><p>自动记忆、关联理解与召回设置</p></div>'+badge('原型 v1.1 · 沿用 DSH 风格','','layers')+'</div>'+offline()+(stateScreen()||'<div class="intro-grid"><section class="panel panel-pad"><div class="row" style="margin-bottom:22px"><span class="intro-symbol">'+icon('memory')+'</span><div class="grow"><h2>记忆设置</h2><p class="footnote">保留原始依据,按对象整理长期记忆。</p></div>'+badge('已启用','green','check')+'</div>'+settingSwitch('capture','自动记忆','捕获允许范围内的重要信息;关闭不会删除已有记忆。',true)+settingSwitch('distill','写入前提炼','提取事实、决策与约束;保留来源,不把回答当成证据。')+'<div class="settings-row"><div><h3>提炼模型</h3><p>只用于整理;不改变当前对话模型。</p></div><select id="distill-model" aria-label="提炼模型" '+disabled()+'><option value="follow" '+(s.draft.model==='follow'?'selected':'')+'>跟随会话模型</option><option value="designated" '+(s.draft.model==='designated'?'selected':'')+'>指定模型(演示)</option></select></div>'+settingSwitch('ontology','关联理解','按类型、关系与约束组织记忆;只在有依据时建立正式关系。')+settingSwitch('recall','回答时使用记忆','按问题召回相关且有效的信息,与自动捕获分开控制。')+'</section><div class="stack"><section class="panel panel-pad"><div class="row between"><h3>记忆管理</h3>'+icon('graph')+'</div><p class="footnote" style="margin-top:9px">一个地方,查记忆、看关系、核对依据。</p><div class="metric-row"><div class="metric"><strong>'+s.memory.length+'</strong><span>演示记忆</span></div><div class="metric"><strong>'+n+'</strong><span>来源已核对</span></div><div class="metric"><strong>'+countReview()+'</strong><span>需要你确认</span></div></div>'+B('workbench','打开记忆工作台','primary','data-view="memories" style="width:100%"','arrow')+'<p class="footnote" style="margin-top:11px">和对话上方的“记忆”使用同一份数据,不重复维护。</p></section><section class="panel panel-pad"><h3>使用说明</h3><div class="stack tight" style="margin-top:16px"><div class="row small">'+icon('check')+'默认只看当前有效记忆</div><div class="row small">'+icon('file')+'点击结论即可核对原文</div><div class="row small">'+icon('clock')+'历史与推断不会冒充当前事实</div></div></section><div class="soft-note">'+icon('shield')+'<span>原型使用虚构数据。开关和修改只影响本次演示,不写入真实记忆。</span></div></div></div><section class="panel panel-pad" style="margin-top:18px"><details><summary>高级设置与本体管理</summary><div class="stack tight" style="margin-top:12px"><p class="footnote">类型、关系与规则仅面向需要治理知识的用户;日常对话无需理解本体术语。</p><div class="row wrap">'+B('workbench','类型与关系','', 'data-view="settings" data-advanced="true"','graph')+B('workbench','范围、隐私与审计','','data-view="settings"','shield')+'</div><p class="footnote">凭据继续由 DSH 统一管理,原型不收集、不显示、不修改密钥。</p></div></details></section>')+'</main></div>'+(dirty()?'<footer class="save-bar"><span class="muted">有尚未保存的设置 · 仅影响演示</span>'+B('config-cancel','取消更改')+B('config-save','保存设置','primary',disabled())+'</footer>':'');}
|
|
115
|
+
function settingSwitch(k,title,desc,first=false){return '<div class="settings-row '+(first?'first':'')+'"><div class="grow"><h3>'+title+'</h3><p>'+desc+'</p></div><button type="button" role="switch" aria-label="'+title+'" aria-checked="'+s.draft[k]+'" class="switch" data-action="config-toggle" data-key="'+k+'" '+disabled()+'></button></div>';}
|
|
116
|
+
function workspace(){const nav=[['memories','记忆','memory'],['graph','图谱','graph'],['review','待确认','alert'],['settings','设置','settings']];return '<div class="workspace-header"><div class="workspace-title"><span class="intro-symbol">'+icon('memory')+'</span><div class="grow"><h1>记忆工作台</h1><p class="footnote">查清一件事,从记忆到关系,再到依据。</p></div>'+B('workspace-back',s.returnFrom==='chat'?'返回当前对话':'返回插件设置','ghost','','back')+'</div><nav class="primary-tabs" aria-label="记忆工作台导航">'+nav.map(([k,t,i])=>B('view',icon(i)+t+(k==='review'&&countReview()>0?'<span class="counter">'+countReview()+'</span>':''),'tab '+(s.view===k?'active':''),'data-view="'+k+'" aria-current="'+(s.view===k?'page':'false')+'"')).join('')+'</nav></div><main class="workspace-body" data-screen-label="'+({memories:'记忆浏览',graph:'本体图谱',review:'待确认',settings:'记忆设置'}[s.view])+'">'+offline()+(stateScreen()||(s.view==='memories'?memoryPage():s.view==='graph'?O.graphPage():s.view==='review'?O.reviewPage():O.advancedPage()))+'</main>';}
|
|
117
|
+
function memoryPage(){const rows=s.memory.filter(m=>(s.scope==='all'||m.scope===s.scope)&&(s.status==='all'||m.status===s.status)&&(!s.query||(m.title+' '+m.entity+' '+m.source+' '+m.type).toLowerCase().includes(s.query.toLowerCase())));return '<div class="filter-bar"><div class="search-box">'+icon('search')+'<input id="memory-query" aria-label="搜索记忆或对象" placeholder="搜索记忆、对象或关键词" value="'+esc(s.query)+'"></div>'+B('search','搜索')+B('clear-filters','清除筛选','ghost')+'<select id="memory-scope" aria-label="记忆范围">'+[['all','全部可见范围'],['Atlas 项目','Atlas 示例项目'],['个人偏好','个人偏好']].map(([v,t])=>'<option value="'+v+'" '+(s.scope===v?'selected':'')+'>'+t+'</option>').join('')+'</select><select id="memory-status" aria-label="记忆状态">'+[['all','所有状态'],['confirmed','来源已核对'],['disputed','存在冲突'],['pending','推断待确认'],['historical','历史记录'],['retired','停止召回']].map(([v,t])=>'<option value="'+v+'" '+(s.status===v?'selected':'')+'>'+t+'</option>').join('')+'</select></div><div class="sub-toolbar"><div class="segmented" aria-label="记忆展示方式">'+[['list','记忆列表','list'],['entities','对象档案','layers'],['sources','来源资料','file']].map(([v,t,i])=>B('subview',icon(i)+t,s.subview===v?'active':'','data-subview="'+v+'"')).join('')+'</div>'+B('add','记住一条','primary',disabled(),'plus')+'</div>'+(s.subview==='entities'?entityCards():s.subview==='sources'?sourceCards():rows.length===0?'<div class="panel state-screen">'+icon('search')+'<h2>没有找到相关记忆</h2><p>试试对象名称,或清除筛选条件。</p>'+B('clear-filters','清除筛选')+'</div>':'<section class="panel" style="overflow:hidden"><table class="memory-table"><thead><tr><th>记忆</th><th>对象 / 范围</th><th>依据状态</th><th>更新</th></tr></thead><tbody>'+rows.map(m=>'<tr><td class="title-cell">'+B('evidence',esc(m.title),'memory-title','data-id="'+m.id+'"')+'<div class="memory-meta">'+badge(m.type)+'<span>'+esc(m.source)+'</span></div></td><td><div>'+esc(m.entity)+'</div><div class="memory-meta">'+esc(m.scope)+'</div></td><td>'+statusTag(m)+'<div class="memory-meta">'+esc(m.basis)+'</div></td><td class="muted">'+m.date+'</td></tr>').join('')+'</tbody></table><div class="table-foot"><span>'+rows.length+' 条演示记忆 · 点击标题查看依据与版本</span><span>“来源已核对”不等于实现或验收通过</span></div></section>')+'<p class="footnote" style="margin-top:14px">'+icon('shield')+' 已保存、是否有依据、是否用于本轮回答,是三个独立状态。</p>';}
|
|
118
|
+
function entityCards(){return '<div class="entity-grid">'+s.entities.filter(e=>!s.query||(e.name+e.desc+e.aliases.join(' ')).includes(s.query)).map(e=>B('entity','<span class="entity-icon">'+icon(e.icon)+'</span><span class="badge-row">'+badge(e.type,'blue')+badge('同一规范身份')+'</span><span class="entity-name">'+esc(e.name)+'</span><p>'+esc(e.desc)+'</p><span class="small muted">'+(e.aliases.length?e.aliases.length+' 个别名':'暂无别名')+' · 查看档案 '+icon('arrow')+'</span>','panel entity-card','data-id="'+e.id+'"')).join('')+'</div>';}
|
|
119
|
+
function sourceCards(){const groups=[...new Set(s.memory.filter(m=>m.evidence).map(m=>m.evidence))];return '<div class="entity-grid">'+groups.map(id=>{const matches=s.memory.filter(m=>m.evidence===id);return B('evidence','<span class="entity-icon">'+icon('file')+'</span>'+badge('演示原始资料')+'<span class="entity-name">'+esc(matches[0].source)+'</span><p>'+id+' · 支持 '+matches.length+' 条主张</p><span class="small muted">可展开原文、定位片段与版本</span>','panel entity-card','data-id="'+matches[0].id+'"');}).join('')+'</div>';}
|
|
120
|
+
function chat(){const answer='<p>根据本次引用的记忆,重置前需要注意三件事:</p><ol class="check-list"><li><b>明确确认。</b> 用户未二次确认时,不应执行重置。 '+B('evidence','1','cite','data-id="m1" aria-label="查看引用 1 的证据"')+'</li><li><b>检查归属。</b> 确认工作区在当前项目和使用者的授权范围内。 '+B('evidence','2','cite','data-id="m2" aria-label="查看引用 2 的证据"')+'</li><li><b>补齐验收证据。</b> 资料写明恢复后要验收,但当前没有通过记录。 '+B('evidence','3','cite','data-id="m3" aria-label="查看引用 3 的证据"')+'</li></ol><p>因此,我能确认的是<b>已收录的要求</b>,还不能据此判断“已经实现”或“可以上线”。</p><div class="answer-source">'+B('drawer-open',icon('memory')+'本轮引用 '+s.used.length+' 条','source-chip')+badge('演示回答 · 非实时生成')+'</div>';return '<div class="chat-scroll '+(s.drawer?'has-drawer':'')+'" data-screen-label="会话及本轮记忆抽屉"><div class="chat-message user"><div class="user-bubble">重置工作区前,有哪些必须满足的条件?</div></div><div class="chat-message"><div class="avatar">'+icon('memory')+'</div><div class="assistant-text">'+(s.demoState==='denied'?'<p>该范围的记忆不可见,演示回答与来源已隐藏。</p>':answer)+'</div></div>'+s.extraMessages.map(m=>'<div class="chat-message '+(m.role==='user'?'user':'')+'">'+(m.role==='user'?'<div class="user-bubble">'+esc(m.text)+'</div>':'<div class="avatar">'+icon('memory')+'</div><div class="assistant-text">'+esc(m.text)+'</div>')+'</div>').join('')+'</div><div class="composer '+(s.drawer?'has-drawer':'')+'"><label class="sr-only" for="chat-input">演示对话输入</label><textarea id="chat-input" placeholder="继续提问,或输入“记住:…”" rows="2">'+esc(s.chatDraft)+'</textarea><div class="row between"><span class="footnote">仅演示界面流程,不调用模型。</span>'+B('chat-send','发送(演示)','primary','','arrow')+'</div></div>'+(s.drawer?drawer():'');}
|
|
121
|
+
function drawer(){const pending=['m7','m6'].filter(id=>find(id));return '<aside class="drawer" aria-label="本轮记忆"><div class="drawer-head"><span class="intro-symbol">'+icon('memory')+'</span><div class="grow"><h2>本轮记忆</h2><p>第 1 轮引用快照 · 来源可查,使用可控。</p></div>'+B('chat-close',icon('close'),'square ghost','aria-label="关闭本轮记忆"')+'</div><div class="drawer-tabs"><nav class="primary-tabs" aria-label="本轮记忆分类">'+B('drawer-tab','已引用 <span class="counter">'+(['denied','traceUnavailable'].includes(s.demoState)?'—':s.used.length)+'</span>','tab '+(s.drawerTab==='used'?'active':''),'data-tab="used"')+B('drawer-tab','相关候选 <span class="counter">'+pending.length+'</span>','tab '+(s.drawerTab==='related'?'active':''),'data-tab="related"')+'</nav></div><div class="drawer-content">'+offline()+(stateScreen()||(s.drawerTab==='used'?'<div class="soft-note blue" style="margin-bottom:15px">'+icon('link')+'<span>已注入的本轮快照不会被改写。排除只对下一轮起效。</span></div>':'<div class="soft-note" style="margin-bottom:15px">'+icon('search')+'<span>候选不代表本轮已用,也不代表事实成立。</span></div>')+(s.drawerTab==='used'?s.used:pending).map(id=>{const m=s.drawerTab==='used'?(s.usedSnapshot.find(x=>x.id===id)||find(id)):find(id);if(!m)return '';const excluded=s.excluded.includes(id)||find(id)?.status==='retired';return '<article class="memory-card '+(excluded?'excluded':'')+'"><div class="row between">'+badge(m.type,'blue')+(excluded?badge('下轮不再引用','','archive'):statusTag(m))+'</div><h3>'+esc(m.title)+'</h3><p class="footnote">'+esc(m.source)+' · '+esc(m.scope)+'</p><div class="card-actions">'+B('evidence','查看依据','link','data-id="'+id+'" '+(s.drawerTab==='used'?'data-snapshot="true"':''),'file')+(s.drawerTab==='used'?B('exclude',excluded?'恢复下轮使用':'下轮不用','ghost','data-id="'+id+'" '+disabled(),excluded?'refresh':'archive'):m.status==='confirmed'?B('pin',s.pinned.includes(id)?'已固定到下轮':'下轮固定引用','ghost','data-id="'+id+'" '+disabled(),'plus'):B('to-review','去核对','ghost','', 'alert'))+'</div>'+B('why',(s.why===id?'收起原因':'为什么'+(s.drawerTab==='used'?'引用':'推荐')),'link','data-id="'+id+'"')+(s.why===id?'<div class="why">'+esc(m.why)+'</div>':'')+'</article>';}).join(''))+'</div><div class="drawer-notice"><div class="row between"><span>本会话暂不调用记忆</span><button class="switch" role="switch" type="button" aria-label="本会话暂不调用记忆" aria-checked="'+s.recallPaused+'" data-action="pause-recall" '+disabled()+'></button></div><p style="margin-top:5px">下轮候选 '+nextUsed().length+' 条;不删除记忆,也不清除已有上下文。</p></div><footer class="drawer-footer">'+B('add','记住一条','',disabled(),'plus')+B('workbench','管理全部记忆','primary','data-view="memories"','external')+'</footer></aside>';}
|
|
122
|
+
function render(){const focus=document.activeElement?.id;const oldChat=document.querySelector(".chat-scroll");if(oldChat)s.chatScroll=oldChat.scrollTop;document.documentElement.dataset.theme=s.theme;document.body.toggleAttribute('data-ds-dark-theme',s.theme==='dark');document.getElementById('app').innerHTML=demoBar()+'<div class="app-shell">'+sidebar()+'<div class="content-shell">'+header()+(s.surface==='settings'?settings():s.surface==='chat'?chat():workspace())+(s.evidence?O.evidencePanel():'')+'</div></div>';document.getElementById('toast-root').innerHTML=s.toast?'<div class="toast" role="status">'+icon('check')+'<span>'+esc(s.toast)+'</span>'+(s.undo?B('undo','撤销'):'')+B('dismiss-toast',icon('close'),'','aria-label="关闭提示"')+'</div>':'';if(focus)document.getElementById(focus)?.focus({preventScroll:true});const newChat=document.querySelector(".chat-scroll");if(newChat)newChat.scrollTop=s.chatScroll;O.renderModal?.();}
|
|
123
|
+
Object.assign(O,{B,render,countReview,disabled,settingSwitch,memoryPage,stateScreen,offline});
|
|
124
|
+
})();
|
|
125
|
+
|
|
126
|
+
</script>
|
|
127
|
+
<script>
|
|
128
|
+
/* Graph, evidence, review, and expert controls. All paths refer to synthetic fixtures. */
|
|
129
|
+
(() => {
|
|
130
|
+
const O=window.OC,{s,esc,icon,badge,statusTag,canWrite,find,B,countReview}=O;
|
|
131
|
+
const dates=['09-10','09-14','09-18','09-20'];
|
|
132
|
+
function graphData(){const historical=s.time<3, old=s.time===0, mid=s.time===1;
|
|
133
|
+
const nodes=[{id:'project',x:25,y:30,title:'Atlas 文档服务',type:'项目',sub:'范围边界 · 演示'},{id:'req',x:300,y:30,title:'DEMO-042',type:'需求',sub:'工作区重置'},{id:'api',x:25,y:185,title:'工作区重置接口',type:'接口',sub:'不是需求的别名'},{id:'rule',x:300,y:185,title:old?'先保存临时副本':mid?'不保留副本(示例)':'用户二次确认',type:old?'历史约束':'约束',sub:old?'旧方案 v1':mid?'正式纪要 v2':'必须明确确认',historical:old},{id:'source',x:575,y:185,title:old?'示例方案 v1':mid?'示例评审纪要':'示例需求说明',type:'证据',sub:old?'SRC-03':mid?'SRC-04':'SRC-01 · §4.2'}];
|
|
134
|
+
const edges=[{id:'p-r',from:'project',to:'req',label:'包含需求',d:'M205 62 H300',lx:252,ly:51},{id:'r-a',from:'req',to:'api',label:'对应接口',d:'M340 94 C340 141 115 132 115 185',lx:196,ly:135},{id:'r-c',from:'req',to:'rule',label:'具有约束',d:'M390 94 V185',lx:390,ly:141},{id:'c-s',from:'rule',to:'source',label:'依据',d:'M480 217 H575',lx:528,ly:206}];
|
|
135
|
+
if(s.depth===2){nodes.push({id:'person',x:575,y:30,title:'示例负责人',type:'人员',sub:'经办不等于授权'});edges.push({id:'r-u',from:'req',to:'person',label:'经办人',d:'M480 62 H575',lx:528,ly:51});if(s.time>=2){nodes.push({id:'test',x:300,y:340,title:'恢复后对话验收',type:'验收要求',sub:'没有通过记录'});edges.push({id:'c-t',from:'rule',to:'test',label:'关联验收',d:'M390 249 V340',lx:390,ly:294});}if(s.inferred){nodes.push({id:'candidate',x:25,y:340,title:'索引器',type:'候选关系',sub:'只有共现,未证实依赖',inferred:true});edges.push({id:'a-i',from:'api',to:'candidate',label:'可能依赖 · 待核对',d:'M115 249 V340',lx:115,ly:294,inferred:true});}}
|
|
136
|
+
return {nodes,edges,historical};}
|
|
137
|
+
function graphSvg(){const {nodes,edges}=graphData();const w=800/s.zoom,h=440/s.zoom;return '<svg id="knowledge-graph" viewBox="'+((800-w)/2)+' '+((440-h)/2)+' '+w+' '+h+'" role="group" aria-label="Atlas 示例项目本体关系图"><defs><marker id="arrowhead" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M0 0 10 5 0 10" fill="#9dadc9"></path></marker></defs>'+edges.map(e=>'<g class="graph-edge '+(e.inferred?'inferred ':'')+((s.selectedEdge===e.id||s.question&&['r-c','c-s'].includes(e.id))?'selected':'')+'" data-action="graph-edge" data-id="'+e.id+'" role="button" tabindex="0" aria-label="查看关系:'+e.label+'"><path d="'+e.d+'" fill="none" stroke="transparent" stroke-width="20"></path><path class="edge-line" d="'+e.d+'" marker-end="url(#arrowhead)"></path><rect class="label-bg" x="'+(e.lx-(e.inferred?53:29))+'" y="'+(e.ly-8)+'" width="'+(e.inferred?106:58)+'" height="19" rx="5"></rect><text class="edge-label" x="'+e.lx+'" y="'+(e.ly+5)+'" text-anchor="middle">'+e.label+'</text></g>').join('')+nodes.map(n=>'<g class="graph-node '+(s.graphSelected===n.id?'selected ':'')+(n.historical?'historical ':'')+(n.inferred?'inferred':'')+'" data-action="graph-node" data-id="'+n.id+'" tabindex="0" role="button" aria-label="查看对象:'+n.title+'" transform="translate('+n.x+','+n.y+')"><rect class="node-bg" width="180" height="64" rx="11"></rect><circle class="node-dot" cx="14" cy="14" r="3"></circle><text class="node-type" x="24" y="18">'+n.type+'</text><text class="node-label" x="13" y="38">'+n.title+'</text><text class="node-sub" x="13" y="54">'+n.sub+'</text></g>').join('')+'</svg>';}
|
|
138
|
+
function graphDetail(){const {nodes,edges}=graphData(),n=nodes.find(n=>n.id===s.graphSelected)||nodes[1],e=edges.find(e=>e.id===s.selectedEdge);const dc=find(s.time===0?'m4':s.time===1?'m5':'m1');const refs={'p-r':{quote:'Atlas 示例项目包含 DEMO-042 工作区重置需求。',source:'示例对象身份清单',id:'entity:project'},project:{quote:'这是虚构的 Atlas 文档服务项目;范围不跨越其他项目。',source:'示例对象身份清单',id:'entity:project'},'r-a':{quote:'工作区重置接口与 DEMO-042 需求相关,但类型不同,不能作为同一实体合并。',source:'示例对象身份清单',id:'entity:api'},api:{quote:'接口是技术对象;需求是工作对象。两者通过对应关系相连。',source:'示例对象身份清单',id:'entity:api'},'r-u':{quote:'示例负责人经办 DEMO-042;经办关系不构成操作授权。',source:'示例对象身份清单',id:'entity:person'},person:{quote:'这是虚构的经办人员身份;不据此推导任何访问权限。',source:'示例对象身份清单',id:'entity:person'},'c-t':{quote:find('m3').text,source:find('m3').source,id:'m3'},test:{quote:find('m3').text,source:find('m3').source,id:'m3'},'a-i':{quote:find('m6').text,source:'无直接证据',id:'m6',pending:true},candidate:{quote:find('m6').text,source:'无直接证据',id:'m6',pending:true}};const info=refs[e?.id||n.id]||{quote:dc.text,source:dc.source,id:dc.id};const pathEdge=e||edges.find(x=>x.id===({project:'p-r',req:'r-c',api:'r-a',rule:'r-c',source:'c-s',person:'r-u',test:'c-t',candidate:'a-i'}[n.id]));const text=e?e.label:n.title;return '<aside class="panel detail-card" aria-label="图谱对象与关系详情"><div><div class="row between"><span class="section-label">'+(e?'关系详情':'对象档案')+'</span>'+badge(info.pending?'待确认':'可追溯',info.pending?'amber':'blue')+'</div><h3>'+text+'</h3><p class="footnote">'+(e?'每条关系都有含义、范围与证据。':n.sub)+'</p><div class="section-label">'+(e?'关系路径':'关联路径')+'</div><div class="mini-path"><b>'+ (nodes.find(x=>x.id===pathEdge?.from)?.title||n.title)+'</b><span>→</span><b>'+(nodes.find(x=>x.id===pathEdge?.to)?.title||'未记录关联')+'</b></div></div><div><div class="section-label">'+(info.pending?'证据缺口':'证据片段')+'</div><div class="evidence-excerpt '+(info.pending?'amber':'')+'">'+esc(info.quote)+'</div><div class="source-caption">'+icon('file')+esc(info.source)+'</div><dl class="key-values" style="margin-top:18px"><dt>范围</dt><dd>Atlas 示例项目</dd><dt>查看时点</dt><dd>2026-'+dates[s.time]+'</dd><dt>判断边界</dt><dd>要求不等于已实现</dd></dl>'+B('evidence','展开完整依据','link','data-id="'+info.id+'"','external')+'</div></aside>';}
|
|
139
|
+
function graphPage(){const g=graphData();return (s.demoState==='truncated'?'<div class="offline-banner"><span>图谱展示已截断(演示):只显示当前子图,不将局部结果当成完整关系。</span>'+B('restore','缩小范围并重试')+'</div>':'')+'<div class="sub-toolbar"><div><h2>关系图谱</h2><p class="footnote" style="margin-top:4px">按对象展开,而不是一次展示整张大网。</p></div><div class="segmented">'+B('graph-mode','关系图',s.graphMode==='graph'?'active':'','data-mode="graph"','graph')+B('graph-mode','关系列表',s.graphMode==='list'?'active':'','data-mode="list"','list')+'</div></div><div class="graph-query"><input id="graph-question" aria-label="向图谱提问" placeholder="试问:为什么需要二次确认?" value="'+(s.question?'为什么需要二次确认?':'')+'">'+B('graph-question','查看解释路径','primary','','search')+B('graph-clear','清除高亮','ghost')+'</div>'+(s.time<3?'<div class="history-banner"><span>'+icon('clock')+' 正在回看 2026-'+dates[s.time]+' 的示例状态 · 历史只读</span>'+B('time-current','回到当前')+'</div>':'')+(s.question?'<div class="soft-note blue" style="margin-bottom:14px">'+icon('link')+'<span>解释路径:需求 → 明确确认约束 → 原始依据。这里只说明要求来源,不证明已实现。</span></div>':'')+'<div class="graph-split"><section class="panel graph-panel"><div class="graph-toolbar"><div class="segmented">'+B('depth','一层',s.depth===1?'active':'','data-depth="1"')+B('depth','两层',s.depth===2?'active':'','data-depth="2"')+'</div><label class="row small" style="gap:5px"><input type="checkbox" id="show-inferred" '+(s.inferred?'checked':'')+' style="min-height:16px">显示待确认关系</label><div class="row" style="gap:3px">'+B('zoom-out','−','', 'aria-label="缩小图谱"')+B('zoom-in','+','', 'aria-label="放大图谱"')+B('zoom-reset',icon('expand'),'','aria-label="适配图谱"')+'</div></div>'+(s.graphMode==='graph'?'<div class="graph-stage">'+graphSvg()+'<div class="graph-legend"><span><i></i>有来源关系</span><span><i class="dashed"></i>待确认,不用于推理</span></div></div>':'<div style="padding:16px">'+g.edges.map(e=>'<div class="relation-row"><b>'+g.nodes.find(n=>n.id===e.from)?.title+'</b><span>'+e.label+' →</span><b>'+g.nodes.find(n=>n.id===e.to)?.title+'</b>'+B('graph-edge','查看依据','link','data-id="'+e.id+'"')+'</div>').join('')+'</div>')+'<div class="graph-info">'+g.nodes.length+' 个可见对象 · '+g.edges.length+' 条关系 · 点击对象或连线查看详情'+(s.time<3?' · 历史视图':'')+'</div><div class="timeline-strip"><div class="row between small"><span>'+icon('clock')+' 信息生效时间</span><span class="muted">4 个演示时间点 · '+(s.time===3?'当前':'历史只读')+'</span></div><input id="graph-time" type="range" aria-label="图谱历史时间点" min="0" max="3" step="1" value="'+s.time+'"><div class="timeline-labels"><span>09-10 旧方案</span><span>09-14 评审</span><span>09-18 约束</span><span>09-20 当前</span></div></div></section>'+graphDetail()+'</div>';}
|
|
140
|
+
function reviewPage(){return '<div class="sub-toolbar"><div><h2>待确认事项</h2><p class="footnote" style="margin-top:4px">不按时间自动覆盖,不把同名对象直接合并。</p></div>'+badge(countReview()+' 项待确认',countReview()?'amber':'green')+'</div><div class="review-layout"><nav class="review-queue" aria-label="待确认队列">'+B('review-select',badge(s.resolved?'已处理':'内容冲突',s.resolved?'green':'red')+'<b>备份策略不一致</b><p>正式纪要与后续草稿存在范围重叠。</p>','queue-card '+(s.review==='conflict'?'active':''),'data-review="conflict"')+B('review-select',badge(s.merged?'已处理':'名称待对齐',s.merged?'green':'amber')+'<b>两个名称是同一需求吗?</b><p>仅同类型候选;接口保持独立。</p>','queue-card '+(s.review==='merge'?'active':''),'data-review="merge"')+'</nav><section class="panel review-detail">'+(s.review==='conflict'?conflictDetail():mergeDetail())+'</section></div>'+(s.history.length?'<section class="panel panel-pad" style="margin-top:18px"><h3>本次演示操作记录</h3>'+s.history.map(h=>'<div class="audit-item"><span class="audit-dot"></span><div><b>'+esc(h.title)+'</b><p class="footnote">'+esc(h.text)+'</p></div></div>').join('')+'</section>':'');}
|
|
141
|
+
function conflictDetail(){if(s.resolved)return '<div class="state-screen" style="min-height:330px"><span class="intro-symbol">'+icon('check')+'</span><h2>冲突已处理(演示)</h2><p>处理结果已同步到记忆、图谱和后续召回候选。第 1 轮引用快照保持不变。</p>'+B('view','到记忆中查看','primary','data-view="memories"')+B('undo','撤销这次演示处理','ghost',s.undo?'':'disabled')+'</div>';return '<div class="row between"><h2>重置前是否保留副本?</h2>'+badge('同对象 · 范围待核对','amber')+'</div><p class="footnote" style="margin-top:8px">两条记录都已保存,但这并不意味着它们都成立。</p><div class="comparison"><section><div class="row between">'+badge('记录 A','blue')+'<span class="small muted">09-14</span></div><h3 style="margin-top:12px">正式评审纪要</h3><p>示例生产环境重置时,不保留工作区副本。</p><div class="badge-row">'+badge('正式纪要')+badge('当前有争议','red')+'</div>'+B('evidence','查看原文','link','data-id="m5"','file')+'</section><section><div class="row between">'+badge('记录 B','amber')+'<span class="small muted">09-19</span></div><h3 style="margin-top:12px">后续讨论草稿</h3><p>重置前仍需保存副本,但未说明适用环境。</p><div class="badge-row">'+badge('未批准草稿')+badge('范围待核对','amber')+'</div>'+B('evidence','查看原文','link','data-id="m8"','file')+'</section></div><div class="soft-note amber">'+icon('alert')+'<span>较新的记录不会自动覆盖旧决策。请核对来源权限、适用环境和生效时间。</span></div><div class="review-actions">'+B('resolve','以正式纪要为当前依据','primary',O.disabled())+B('split-scope','两条适用于不同环境','',O.disabled())+B('review-later','暂不处理','ghost')+'</div><p class="footnote" style="margin-top:14px">保存前会展示影响范围,并要求说明处理理由。原始证据保留。</p>';}
|
|
142
|
+
function mergeDetail(){return s.merged?'<div class="state-screen"><span class="intro-symbol">'+icon('check')+'</span><h2>别名已对齐(演示)</h2><p>同一需求共享规范 ID。接口对象仍然独立,通过关系连接。</p>'+B('entity','查看需求档案','primary','data-id="req"')+'</div>':'<h2>这是同一个需求的两种叫法吗?</h2><p class="footnote" style="margin-top:8px">只因名字相似不能自动合并,先核对类型、外部 ID 和证据。</p><div class="comparison"><section>'+badge('需求','blue')+'<h3 style="margin-top:12px">DEMO-042</h3><p>规范外部编号一致。已关联明确确认等约束。</p></section><section>'+badge('需求候选','amber')+'<h3 style="margin-top:12px">工作区重置需求</h3><p>示例来源给出相同编号;可作为别名候选。</p></section></div><div class="soft-note">'+icon('shield')+'<span>“工作区重置接口”是接口类型,不在此合并范围内;合并不扩大访问权限。</span></div><div class="review-actions">'+B('merge-preview','预览合并影响','primary',O.disabled())+B('review-later','暂不合并','ghost')+'</div>';}
|
|
143
|
+
function advancedPage(){return '<div class="settings-tabs segmented">'+B('advanced-tab','基础与隐私',!s.advanced?'active':'','data-mode="basic"')+B('advanced-tab','类型与关系',s.advanced?'active':'','data-mode="advanced"')+'</div>'+(s.advanced?schemaPage():'<section class="panel panel-pad"><h2>使用策略</h2><p class="footnote" style="margin:7px 0 23px">捕获、长期保存和回答召回分别控制,默认使用最少必要信息。</p>'+O.settingSwitch('capture','自动记忆','关闭仅停止新的自动捕获,不删除已保存资料。',true)+O.settingSwitch('recall','回答时使用记忆','关闭仅停止后续注入,已有会话上下文不被清除。')+O.settingSwitch('ontology','关联理解','启用受控类型与关系;候选推断默认不进入回答。')+'<div class="settings-row"><div><h3>默认范围</h3><p>原型仅提供个人 / Atlas 示例项目;不进行跨项目共享。</p></div>'+badge('按来源权限过滤','green','shield')+'</div><div class="settings-row"><div><h3>敏感信息与凭据</h3><p>密钥不入记忆;模型和网关凭据仍使用 DSH 原有管理。</p></div>'+badge('由 DSH 管理','','lock')+'</div><div class="row wrap">'+B('config-save','保存设置','primary',O.disabled()+' '+(!O.dirty()?'disabled':''))+B('privacy-info','停止召回与删除的区别','link','','file')+'</div></section><div class="soft-note" style="margin-top:17px">'+icon('clock')+'<span>永久删除必须单独授权并回报清理进度;“停止召回”不等于删除原文、会话记录或备份。</span></div>');}
|
|
144
|
+
function schemaPage(){return '<section class="panel panel-pad"><div class="row between"><div><span class="eyebrow">专家选项 · 日常使用无需配置</span><h2 style="margin-top:7px">类型、关系与规则</h2></div>'+badge('本体版本 1.0 · 演示','blue')+'</div><p class="footnote" style="margin-top:10px">不是自由标签:明确允许的关系方向与对象类型,错误不会被静默接受。</p><div class="schema-types">'+['项目 Project','需求 Requirement','接口 API','人员 Person','约束 Constraint','主张 Claim','证据 Evidence'].map(t=>badge(t,'blue')).join('')+'</div><div class="rule-row"><b>具有约束</b><span class="rule-arrow">Requirement → Constraint</span><span class="spacer"></span>'+badge('已定义','green','check')+'</div><div class="rule-row"><b>由证据支持</b><span class="rule-arrow">Claim → Evidence</span><span class="spacer"></span>'+badge('要求片段引用','green')+'</div><div class="rule-row"><b>替代旧主张</b><span class="rule-arrow">Claim → Claim</span><span class="spacer"></span>'+badge('必须校验范围与时间','amber')+'</div><div style="margin-top:25px"><h3>试一条类型校验</h3><p class="footnote" style="margin-top:6px">把主语切成“人员”,再校验,就能看到明确的错误位置。</p><div class="rule-row"><select id="schema-subject" aria-label="校验主语类型" '+O.disabled()+'><option value="Requirement" '+(s.schemaSubject==='Requirement'?'selected':'')+'>需求 Requirement</option><option value="Person" '+(s.schemaSubject==='Person'?'selected':'')+'>人员 Person</option></select><span>具有约束 →</span>'+badge('约束 Constraint','blue')+B('schema-check','校验示例','primary',O.disabled())+'</div>'+(s.schemaResult!==null?'<div class="validation-result '+(s.schemaResult?'':'bad')+'" role="status">'+(s.schemaResult?'通过:主语 Requirement、关系 has_constraint、宾语 Constraint 符合类型定义。':'拒绝:has_constraint 的主语必须是 Requirement;当前是 Person。没有写入任何关系。')+'</div>':'')+'</div></section><section class="panel panel-pad" style="margin-top:16px"><div class="row between"><div><h3>受控规则,而不是自动执行器</h3><p class="footnote" style="margin-top:5px">只允许有界派生;不调用工具、不推导权限、不用共现证明因果。</p></div>'+badge('默认一层 · 最多两层')+'</div><div class="soft-note" style="margin-top:15px">'+icon('shield')+'<span>类型继承、基数约束、双时态、规则试跑与版本迁移在完整方案中定义;本原型只实际演示上述 domain/range 校验。</span></div><div class="row wrap" style="margin-top:13px">'+B('schema-preview','预览规则发布流程','',O.disabled(),'eye')+B('workbench','回到图谱','link','data-view="graph"','graph')+'</div></section>';}
|
|
145
|
+
function evidencePanel(){const entity=s.evidence.startsWith('entity:')?s.entities.find(e=>e.id===s.evidence.split(':')[1]):null,m=s.evidenceSnapshot||(entity?{id:'entity:'+entity.id,title:entity.name,type:entity.type,status:'confirmed',scope:'Atlas 项目',source:'示例对象身份清单',text:entity.desc+'。此处是演示身份资料,不把其他记忆的证据当作该对象的身份证明。',evidence:'FIXTURE-'+entity.id,validFrom:'2026-09-20',date:'09-20'}:find(s.evidence));if(!m)return '';return '<div class="overlay-panel" data-action="evidence-backdrop"><aside class="evidence-drawer" aria-label="依据与对象详情" role="dialog" aria-modal="true"><div class="drawer-head"><div class="grow"><span class="section-label">'+(entity?'对象档案':'依据与关系')+'</span><h2 style="margin-top:7px">'+esc(entity?entity.name:m.title)+'</h2></div>'+B('evidence-close',icon('close'),'square ghost','aria-label="关闭依据面板"')+'</div><div style="padding:0 26px"><div class="badge-row" style="margin-bottom:13px">'+(entity?badge(entity.type,'blue')+badge('规范实体'):badge(m.type,'blue')+statusTag(m))+'</div><nav class="primary-tabs" aria-label="详情分类">'+[['source','依据'],['relations','关系'],['history','版本']].map(([v,t])=>B('evidence-tab',t,'tab '+(s.evidenceTab===v?'active':''),'data-tab="'+v+'"')).join('')+'</nav></div><div class="drawer-content">'+(entity?'<dl class="key-values"><dt>规范身份</dt><dd>'+esc(entity.name)+'</dd><dt>别名</dt><dd>'+esc(entity.aliases.join('、')||'暂无')+'</dd><dt>类型</dt><dd>'+esc(entity.type)+'</dd><dt>身份边界</dt><dd>'+esc(entity.desc)+'</dd></dl><hr class="divider" style="margin:20px 0">':'')+(s.evidenceTab==='source'?'<div class="row between"><h3>原始依据</h3>'+badge(m.evidence?'可追溯片段':'无直接依据',m.evidence?'green':'amber')+'</div><div class="quote-block">'+esc(m.text)+'</div><div class="source-caption">'+icon('file')+esc(m.source)+'</div><dl class="key-values" style="margin-top:24px"><dt>保存状态</dt><dd>'+(m.status==='retired'?'已停止召回,原文仍保留':'已保存于演示数据')+'</dd><dt>依据状态</dt><dd>'+(m.status==='pending'?'推断,未核对':m.status==='disputed'?'存在相反陈述':'来源已核对,不等于客观真或已完成')+'</dd><dt>本轮使用</dt><dd>'+(s.used.includes(m.id)?'第 1 轮已引用;快照固定':'未被第 1 轮引用')+'</dd><dt>适用范围</dt><dd>'+esc(m.scope)+'</dd><dt>生效时间</dt><dd>'+m.validFrom+(m.validTo?' 至 '+m.validTo:' 起')+'</dd><dt>记录时间</dt><dd>2026-'+m.date+'(演示)</dd><dt>证据编号</dt><dd>'+esc(m.evidence||'尚无')+'</dd></dl><details style="margin-top:20px"><summary>为什么保留这个区别?</summary><p class="footnote">来源核对、用户确认、已保存和本轮引用各有含义。反复引用同一来源不会增加独立证据,也不会证明需求已经实现。</p></details>':s.evidenceTab==='relations'?'<h3>可解释的关联</h3><div class="relation-row"><b>DEMO-042</b><span>具有约束 →</span><b>用户二次确认</b></div><div class="relation-row"><b>约束主张</b><span>由证据支持 →</span><b>SRC-01 §4.2</b></div><div class="relation-row"><b>DEMO-042</b><span>对应接口 →</span><b>工作区重置接口</b></div><p class="footnote" style="margin-top:18px">需求与接口是独立实体。上述关联均为演示,不反映真实业务。</p>'+B('evidence-to-graph','在图谱中查看','primary','style="margin-top:17px"','graph'):'<h3>记忆修订与来源版本</h3><div class="version-row"><span class="version-date">'+m.date+'</span><div><b>当前记录 · v'+s.revision+'</b><p class="footnote">'+esc(m.source)+'</p></div></div><div class="version-row"><span class="version-date">来源</span><div><b>'+esc(m.evidence||'待核对')+'</b><p class="footnote">原始内容不可被派生摘要覆盖。</p></div></div>'+s.history.map(h=>'<div class="version-row"><span class="version-date">本次演示</span><div>'+esc(h.title)+'<p class="footnote">'+esc(h.text)+'</p></div></div>').join('')+'<div class="soft-note" style="margin-top:20px">'+icon('clock')+'<span>完整实现将支持“当时所知”与“最新所知的过去”两种查询。本原型不模拟完整双时态数据库。</span></div>')+'</div><footer class="drawer-footer">'+(entity?'':B('edit','修订','', 'data-id="'+m.id+'" '+O.disabled(),'edit')+B('retire','停止召回','', 'data-id="'+m.id+'" '+O.disabled()+' '+(m.status==='retired'?'disabled':''),'archive'))+B('evidence-to-graph','查看图谱','primary','','graph')+'</footer></aside></div>';}
|
|
146
|
+
Object.assign(O,{graphPage,graphData,reviewPage,advancedPage,evidencePanel});
|
|
147
|
+
})();
|
|
148
|
+
|
|
149
|
+
</script>
|
|
150
|
+
<script>
|
|
151
|
+
/* Local state transitions only. Network, persistence, authentication, and inference engines are intentionally absent. */
|
|
152
|
+
(() => {
|
|
153
|
+
const O=window.OC,{s,esc,icon,badge,B,find,canWrite}=O;
|
|
154
|
+
let modalReturn=null,evidenceReturn=null,busy=false;
|
|
155
|
+
function notify(t,keepUndo=false){s.toast=t;if(!keepUndo)s.undo=null;}
|
|
156
|
+
function closeModal(){document.getElementById('oc-modal')?.close();s.modal=null;s.modalError='';busy=false;document.getElementById('modal-root').innerHTML='';if(modalReturn?.isConnected)modalReturn.focus({preventScroll:true});}
|
|
157
|
+
function openModal(type,data={}){modalReturn=document.activeElement;s.modal={type,...data};s.modalError='';O.renderModal();}
|
|
158
|
+
function openEvidence(id,snapshot=false){evidenceReturn=document.activeElement;s.evidence=id;s.evidenceSnapshot=snapshot?s.usedSnapshot.find(m=>m.id===id):null;s.evidenceTab='source';O.render();document.querySelector('[data-action="evidence-close"]')?.focus({preventScroll:true});}
|
|
159
|
+
function closeEvidence(){s.evidence=null;s.evidenceSnapshot=null;O.render();if(evidenceReturn){const action=evidenceReturn.dataset?.action,id=evidenceReturn.dataset?.id;const q=action?'[data-action="'+action+'"]'+(id?'[data-id="'+id+'"]':''):'';document.querySelector(q||'#memory-trigger')?.focus({preventScroll:true});}}
|
|
160
|
+
function navigate(dest){if(O.dirty()){openModal('navigate',{dest});return;}applyNavigation(dest);}
|
|
161
|
+
function applyNavigation(dest){s.evidence=null;s.evidenceSnapshot=null;if(dest==='chat'){s.surface='chat';s.drawer=false;}else if(dest==='settings'){s.surface='settings';}else if(dest==='back'){s.surface=s.returnFrom==='chat'?'chat':'settings';s.drawer=s.returnFrom==='chat';}else{if(s.surface!=='workspace')s.returnFrom=s.surface==='chat'?'chat':'settings';s.surface='workspace';s.view=dest==='workbench-settings'?'settings':dest;s.drawer=false;}O.render();}
|
|
162
|
+
function showError(text){s.modalError=text;const el=document.getElementById('modal-error');if(el)el.textContent=text;document.getElementById('modal-submit')?.removeAttribute('disabled');busy=false;}
|
|
163
|
+
const field=(id,title,content,hint='')=>'<div class="field"><label for="'+id+'">'+title+'</label>'+content+(hint?'<p>'+hint+'</p>':'')+'</div>';
|
|
164
|
+
const reasonField=()=>field('change-reason','处理理由(必填)','<textarea id="change-reason" placeholder="说明依据与适用范围,不要填写敏感信息。" rows="2">'+esc(s.modal?.reason||'')+'</textarea>');
|
|
165
|
+
function renderModal(){const m=s.modal,root=document.getElementById('modal-root');if(!m){root.innerHTML='';return;}const title={add:'记住一条重要信息',edit:'修订记忆',retire:'停止后续召回?',resolve:'确认这次冲突处理',split:'区分适用环境',merge:'合并影响预览',schema:'规则发布预览',privacy:'停止召回,不等于删除',navigate:'有尚未保存的设置'}[m.type]||'确认';let body='',submit='确认(仅演示)';
|
|
166
|
+
if(m.type==='add'||m.type==='edit'){const old=m.id?find(m.id):null;body=field('memory-content','记忆内容(必填)','<textarea id="memory-content" placeholder="例如:本项目关键决策需要附原始依据。" maxlength="1600">'+esc(m.content??old?.text??'')+'</textarea>','不要填入密钥、密码或未经授权的个人信息。')+field('save-scope','保存范围','<select id="save-scope"><option value="Atlas 项目" '+((m.scope??old?.scope)==='Atlas 项目'?'selected':'')+'>Atlas 示例项目</option><option value="个人偏好" '+((m.scope??old?.scope)==='个人偏好'?'selected':'')+'>个人偏好</option></select>','不会自动跨项目共享。')+(m.type==='edit'?reasonField():'<div class="soft-note">'+icon('shield')+'<span>显式保存不等于客观为真。会记录为“用户陈述”,并保留来源;不会改写本轮引用快照。</span></div>');submit=m.type==='edit'?'保存修订(演示)':'保存记忆(演示)';}
|
|
167
|
+
if(m.type==='retire'){body='<p>停止“'+esc(find(m.id)?.title)+'”参与后续默认召回。</p><div class="change-preview"><b>会改变</b><ul><li>记忆的后续可用状态</li><li>新的检索与图谱默认结果</li></ul><b style="display:block;margin-top:12px">不会改变</b><ul><li>原始证据与历史审计</li><li>已经发送的对话或本轮引用快照</li></ul></div>'+reasonField();submit='确认停止召回';}
|
|
168
|
+
if(m.type==='resolve'){body='<div class="change-preview"><b>Atlas 项目 · 影响 2 条主张</b><ul><li>记录 A 作为当前示例生产环境的依据。</li><li>记录 B 退出当前默认召回,原文与争议历史保留。</li><li>不会修改原始资料、不会执行重置操作。</li></ul></div>'+reasonField();submit='确认并更新演示状态';}
|
|
169
|
+
if(m.type==='split'){body='<p class="footnote">两条记录各自保留,但必须明确不同的适用环境;不能只换一个标签来回避真实冲突。</p>'+field('scope-a','记录 A 适用环境','<select id="scope-a"><option>生产</option><option>测试</option></select>')+field('scope-b','记录 B 适用环境','<select id="scope-b"><option>测试</option><option>生产</option></select>')+reasonField();submit='保存环境区分(演示)';}
|
|
170
|
+
if(m.type==='merge'){body='<div class="change-preview"><b>需求 → 同一规范身份 DEMO-042</b><ul><li>新增别名“工作区重置需求”。</li><li>保留所有来源、原始 ID 与审计信息。</li><li>接口实体保持独立,不参与合并。</li><li>可见范围不增加,原始记忆数量不增加。</li></ul></div>'+reasonField();submit='确认对齐别名(演示)';}
|
|
171
|
+
if(m.type==='schema'){body='<div class="soft-note amber">'+icon('alert')+'<span>这是发布门禁的界面示意,不执行规则、不修改正式本体。</span></div><div class="change-preview"><b>发布前需要完成</b><ul><li>类型、关系端点、基数与循环校验</li><li>历史资料试跑与影响预览</li><li>权限校验与版本并发检查</li><li>人工确认、发布后回归及回滚点</li></ul></div><p class="footnote">本原型只实现 domain/range 示例校验。完整推理、迁移和持久化需后续研发。</p>';submit='我知道了';}
|
|
172
|
+
if(m.type==='privacy'){body='<div class="stack"><p><b>下轮不用:</b>仅影响当前会话后续引用。</p><p><b>停止召回:</b>让这条长期记忆不再参与新的默认检索,原文和历史仍保留。</p><p><b>永久删除:</b>必须单独确认并显示清理任务结果;范围需覆盖原文、向量、关系、缓存及明确的备份保留策略。</p><p class="footnote">记忆无法撤回模型已经收到的上下文;如需隔离,应新建会话。此原型不执行任何真实删除。</p></div>';submit='我知道了';}
|
|
173
|
+
if(m.type==='navigate'){body='<p>继续会放弃本次尚未保存的开关变化;已保存设置和真实插件不受影响。</p>';submit='放弃更改并继续';}
|
|
174
|
+
root.innerHTML='<dialog id="oc-modal" aria-labelledby="modal-title"><header class="modal-head"><h2 id="modal-title">'+title+'</h2>'+B('modal-cancel',icon('close'),'square ghost','aria-label="关闭弹窗"')+'</header><div class="modal-body">'+body+'<div id="modal-error" class="inline-error" role="alert">'+esc(s.modalError)+'</div></div><footer class="modal-footer">'+B('modal-cancel','取消')+B('modal-submit',submit,'primary','id="modal-submit" '+(!canWrite()&&!['privacy','navigate','schema'].includes(m.type)?'disabled':''))+'</footer></dialog>';const d=document.getElementById('oc-modal');d.showModal();d.addEventListener('cancel',e=>{e.preventDefault();closeModal();});const focus=d.querySelector('textarea,input,select');if(focus)focus.focus({preventScroll:true});}
|
|
175
|
+
async function submitModal(){if(busy||!s.modal)return;const m=s.modal;if(['privacy','schema'].includes(m.type)){closeModal();return;}if(m.type==='navigate'){s.draft={...s.config};const dest=m.dest;closeModal();applyNavigation(dest);return;}if(!canWrite()){showError('当前状态只允许查看,未保存任何修改。');return;}const content=document.getElementById('memory-content')?.value.trim(),reason=document.getElementById('change-reason')?.value.trim();if(['add','edit'].includes(m.type)&&!content){showError('请填写记忆内容,空内容不能保存。');return;}if(['edit','retire','resolve','split','merge'].includes(m.type)&&!reason){showError('请填写处理理由,便于追溯这次更改。');return;}if(m.type==='split'&&document.getElementById('scope-a').value===document.getElementById('scope-b').value){showError('两个环境相同,冲突仍未消除;请核对范围或取消。');return;}if(s.demoState==='stale'){showError('演示并发冲突:记录版本已经更新。你的草稿已保留;切换为“正常”后重新核对再保存。');return;}busy=true;document.getElementById('modal-submit').disabled=true;document.getElementById('modal-submit').textContent='保存中…';const scope=document.getElementById('save-scope')?.value,a=document.getElementById('scope-a')?.value,b=document.getElementById('scope-b')?.value;await new Promise(r=>setTimeout(r,280));if(!s.modal||s.modal!==m)return;if(!canWrite()){showError('状态已变化,写入取消。草稿保留,未发生部分保存。');return;}O.checkpoint();let text='演示状态已更新';
|
|
176
|
+
if(m.type==='add'){const duplicate=s.memory.find(x=>x.text===content&&x.scope===scope);if(duplicate){closeModal();notify('已存在相同内容与范围,未重复保存。');O.render();return;}const id='demo-user-'+(s.memory.length+1);s.memory.unshift({id,title:content.length>46?content.slice(0,46)+'…':content,text:content,type:'用户陈述',entity:'手动记录',scope,status:'confirmed',source:'本次演示中的明确保存',date:'09-20',evidence:'DEMO-USER-STATEMENT',validFrom:'2026-09-20',basis:'用户陈述,不保证为真',why:'用户明确保存;仍需按范围与证据审慎使用。'});s.demoState='ready';text='已保存到'+scope+'(仅演示),两个入口同步可见。';}
|
|
177
|
+
if(m.type==='edit'){const old=find(m.id);s.history.unshift({title:'修订:'+old.title,text:'保留上一版本与原始依据;理由:'+reason});old.previousRevision={text:old.text,source:old.source,evidence:old.evidence};old.source='本次演示中的用户修订';old.evidence='DEMO-USER-REVISION-'+(s.revision+1);old.basis='用户修订,需核对';old.text=content;old.title=content.length>46?content.slice(0,46)+'…':content;old.scope=scope;old.date='09-20';text='修订已保存(演示);本轮引用快照保持原样。';}
|
|
178
|
+
if(m.type==='retire'){find(m.id).status='retired';s.history.unshift({title:'停止召回:'+find(m.id).title,text:'原文和已发送对话保留;理由:'+reason});text='已停止后续召回(演示),没有删除原始资料。';}
|
|
179
|
+
if(m.type==='resolve'){s.resolved=true;find('m5').status='confirmed';find('m8').status='historical';find('m8').validTo='2026-09-20';find('m5').basis='人工核对正式纪要';s.history.unshift({title:'冲突处理:采用正式纪要作为当前依据',text:'未覆盖原文;理由:'+reason});text='冲突处理已同步(演示),历史证据保留。';}
|
|
180
|
+
if(m.type==='split'){s.resolved=true;find('m5').status='confirmed';find('m8').status='confirmed';find('m5').environment=a;find('m8').environment=b;find('m5').basis='适用于'+a;find('m8').basis='适用于'+b;s.history.unshift({title:'区分适用范围:'+a+' / '+b,text:'两条保留,按环境分别检索;理由:'+reason});text='适用环境已区分(演示),没有删除任一来源。';}
|
|
181
|
+
if(m.type==='merge'){s.merged=true;const req=s.entities.find(e=>e.id==='req');if(!req.aliases.includes('工作区重置需求'))req.aliases.push('工作区重置需求');s.history.unshift({title:'对齐需求别名:工作区重置需求 → DEMO-042',text:'接口未合并,权限不扩大;理由:'+reason});text='别名已对齐(演示),需求和接口仍是不同实体。';}
|
|
182
|
+
s.revision++;closeModal();notify(text,true);O.render();}
|
|
183
|
+
const writeActions=new Set(['config-toggle','config-save','add','edit','retire','resolve','split-scope','merge-preview','exclude','pin','pause-recall','schema-check']);
|
|
184
|
+
function act(a,el){if(el?.disabled)return;if(writeActions.has(a)&&!canWrite()){notify('当前只读,未写入任何数据。');O.render();return;}switch(a){
|
|
185
|
+
case 'surface-chat':navigate('chat');break;case 'surface-settings':navigate('settings');break;
|
|
186
|
+
case 'workbench':if(el?.dataset.advanced==='true')s.advanced=true;navigate(el?.dataset.view==='settings'?'workbench-settings':(el?.dataset.view||'memories'));break;
|
|
187
|
+
case 'workspace-back':navigate('back');break;case 'view':navigate(el.dataset.view==='settings'?'workbench-settings':el.dataset.view);break;
|
|
188
|
+
case 'drawer-open':s.drawer=true;s.surface='chat';O.render();break;
|
|
189
|
+
case 'chat-close':s.drawer=false;O.render();document.getElementById('memory-trigger')?.focus({preventScroll:true});break;
|
|
190
|
+
case 'drawer-tab':s.drawerTab=el.dataset.tab;O.render();break;
|
|
191
|
+
case 'theme':s.theme=s.theme==='light'?'dark':'light';O.render();break;
|
|
192
|
+
case 'reset':closeModal();O.reset();O.render();break;
|
|
193
|
+
case 'restore':s.demoState='ready';notify('已恢复正常演示状态。');O.render();break;
|
|
194
|
+
case 'config-toggle':s.draft[el.dataset.key]=!s.draft[el.dataset.key];O.render();break;
|
|
195
|
+
case 'config-cancel':s.draft={...s.config};O.render();break;
|
|
196
|
+
case 'config-save':if(s.demoState==='stale'){notify('版本变化,保存已阻止;草稿保留。');O.render();return;}s.config={...s.draft};notify('设置已保存(仅演示),没有修改正式插件。');O.render();break;
|
|
197
|
+
case 'subview':s.subview=el.dataset.subview;O.render();break;
|
|
198
|
+
case 'search':s.query=document.getElementById('memory-query')?.value.trim()||'';O.render();break;
|
|
199
|
+
case 'clear-filters':s.query='';s.scope='all';s.status='all';O.render();break;
|
|
200
|
+
case 'evidence':openEvidence(el.dataset.id,el.dataset.snapshot==='true');break;
|
|
201
|
+
case 'entity':openEvidence('entity:'+el.dataset.id);break;
|
|
202
|
+
case 'evidence-close':closeEvidence();break;
|
|
203
|
+
case 'evidence-backdrop':closeEvidence();break;
|
|
204
|
+
case 'evidence-tab':s.evidenceTab=el.dataset.tab;O.render();break;
|
|
205
|
+
case 'evidence-to-graph':s.evidence=null;s.evidenceSnapshot=null;navigate('graph');break;
|
|
206
|
+
case 'evidence-current':openEvidence(el.dataset.id,false);break;
|
|
207
|
+
case 'why':s.why=s.why===el.dataset.id?null:el.dataset.id;O.render();break;
|
|
208
|
+
case 'exclude':{const id=el.dataset.id;if(find(id)?.status==='retired'){notify('该记忆已停止长期召回,需要先在管理工作台修订恢复。');O.render();break;}O.checkpoint();s.excluded=s.excluded.includes(id)?s.excluded.filter(x=>x!==id):[...s.excluded,id];notify(s.excluded.includes(id)?'下轮将排除此条;本轮引用快照和长期记忆都保留。':'已恢复下轮使用资格(演示)。',true);O.render();break;}
|
|
209
|
+
case 'pin':{const id=el.dataset.id;if(find(id)?.status!=='confirmed'){notify('未核对或有争议的主张不能直接固定为事实。');O.render();break;}O.checkpoint();s.pinned=s.pinned.includes(id)?s.pinned.filter(x=>x!==id):[...s.pinned,id];notify('已更新下轮固定引用(演示),未重复保存记忆。',true);O.render();break;}
|
|
210
|
+
case 'pause-recall':s.recallPaused=!s.recallPaused;notify(s.recallPaused?'本会话后续不注入记忆;不删除历史上下文。':'已恢复本会话后续记忆注入(演示)。');O.render();break;
|
|
211
|
+
case 'add':openModal('add',{scope:'Atlas 项目',content:''});break;
|
|
212
|
+
case 'edit':openModal('edit',{id:el.dataset.id,content:find(el.dataset.id)?.text,scope:find(el.dataset.id)?.scope});break;
|
|
213
|
+
case 'retire':openModal('retire',{id:el.dataset.id});break;
|
|
214
|
+
case 'resolve':openModal('resolve');break;case 'split-scope':openModal('split');break;case 'merge-preview':openModal('merge');break;
|
|
215
|
+
case 'review-select':s.review=el.dataset.review;O.render();break;
|
|
216
|
+
case 'to-review':navigate('review');notify('待确认的推断不会自动用于回答;请先补充直接证据。');O.render();break;
|
|
217
|
+
case 'review-later':notify('保持待确认;没有更改任何当前结论。');O.render();break;
|
|
218
|
+
case 'depth':s.depth=Number(el.dataset.depth);if(s.depth===1)s.inferred=false;s.selectedEdge=null;O.render();break;
|
|
219
|
+
case 'graph-node':s.graphSelected=el.dataset.id;s.selectedEdge=null;O.render();break;
|
|
220
|
+
case 'graph-edge':s.selectedEdge=el.dataset.id;O.render();break;
|
|
221
|
+
case 'graph-mode':s.graphMode=el.dataset.mode;O.render();break;
|
|
222
|
+
case 'graph-question':{const q=document.getElementById('graph-question').value.trim();if(q&&!/确认|为什么|依据|要求/.test(q)){notify('原型只演示“为什么需要二次确认”的固定路径,不调用大模型。');O.render();break;}s.question=true;s.selectedEdge='c-s';s.graphSelected='rule';s.time=3;O.render();break;}
|
|
223
|
+
case 'graph-clear':s.question=false;s.selectedEdge=null;O.render();break;
|
|
224
|
+
case 'zoom-in':s.zoom=Math.min(1.7,s.zoom+.15);O.render();break;
|
|
225
|
+
case 'zoom-out':s.zoom=Math.max(.7,s.zoom-.15);O.render();break;
|
|
226
|
+
case 'zoom-reset':s.zoom=1;O.render();break;
|
|
227
|
+
case 'time-current':s.time=3;O.render();break;
|
|
228
|
+
case 'advanced-tab':s.advanced=el.dataset.mode==='advanced';O.render();break;
|
|
229
|
+
case 'schema-check':s.schemaResult=s.schemaSubject==='Requirement';O.render();break;
|
|
230
|
+
case 'schema-preview':openModal('schema');break;
|
|
231
|
+
case 'privacy-info':openModal('privacy');break;
|
|
232
|
+
case 'modal-cancel':closeModal();break;case 'modal-submit':submitModal();break;
|
|
233
|
+
case 'undo':if(s.undo){const snap=JSON.parse(s.undo);Object.assign(s,snap);s.undo=null;notify('已撤销最近一次演示更改;真实资料未受影响。');O.render();}break;
|
|
234
|
+
case 'dismiss-toast':s.toast=null;O.render();break;
|
|
235
|
+
case 'chat-send':{const t=document.getElementById('chat-input').value.trim();if(!t){notify('先输入一句话,再试试演示。');O.render();break;}if(/^记住[::]/.test(t)){openModal('add',{scope:'Atlas 项目',content:t.replace(/^记住[::]/,'').trim()});break;}s.chatDraft='';s.extraMessages.push({role:'user',text:t},{role:'assistant',text:'演示说明:下一轮将按你的选择使用 '+O.nextUsed().length+' 条记忆。正式实现会生成新轮次的引用记录;本页仍可核对第 1 轮的固定快照。这里没有调用真实模型。'});O.render();break;}
|
|
236
|
+
default:console.warn('Unknown prototype action',a);
|
|
237
|
+
}}
|
|
238
|
+
document.addEventListener('click',e=>{const el=e.target.closest('[data-action]');if(!el)return;if(el.dataset.action==='evidence-backdrop'&&e.target!==el)return;act(el.dataset.action,el);});
|
|
239
|
+
document.addEventListener('change',e=>{const el=e.target;switch(el.id){case 'demo-state':s.demoState=el.value;s.evidence=null;s.evidenceSnapshot=null;if(s.modal){s.modal=null;document.getElementById('modal-root').innerHTML='';}break;case 'distill-model':s.draft.model=el.value;break;case 'memory-scope':s.scope=el.value;break;case 'memory-status':s.status=el.value;break;case 'show-inferred':s.inferred=el.checked;if(s.inferred)s.depth=2;break;case 'graph-time':s.time=Number(el.value);s.question=false;s.selectedEdge=null;break;case 'schema-subject':s.schemaSubject=el.value;s.schemaResult=null;break;default:return;}O.render();});
|
|
240
|
+
document.addEventListener('input',e=>{if(e.target.id==='chat-input'){s.chatDraft=e.target.value;return;}if(!s.modal)return;const map={'memory-content':'content','change-reason':'reason','save-scope':'scope'};if(map[e.target.id])s.modal[map[e.target.id]]=e.target.value;});
|
|
241
|
+
document.addEventListener('keydown',e=>{if(e.key==='Escape'){if(s.modal){e.preventDefault();closeModal();}else if(s.evidence){e.preventDefault();closeEvidence();}else if(s.drawer){e.preventDefault();s.drawer=false;O.render();document.getElementById('memory-trigger')?.focus({preventScroll:true});}return;}if(e.key==='Tab'&&s.evidence&&!s.modal){const d=document.querySelector('.evidence-drawer');const f=[...d.querySelectorAll('button:not(:disabled),input,select,textarea,[tabindex="0"]')];const first=f[0],last=f[f.length-1];if(e.shiftKey&&document.activeElement===first){e.preventDefault();last?.focus();}else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first?.focus();}}if(e.key==='Enter'&&e.target.id==='memory-query'){e.preventDefault();act('search');}if(e.key==='Enter'&&e.target.id==='graph-question'){e.preventDefault();act('graph-question');}if((e.key==='Enter'||e.key===' ')&&e.target.matches('g[data-action]')){e.preventDefault();act(e.target.dataset.action,e.target);}if((e.ctrlKey||e.metaKey)&&e.key==='Enter'&&e.target.id==='chat-input'){e.preventDefault();act('chat-send');}});
|
|
242
|
+
Object.assign(O,{renderModal,act,openModal,notify,closeModal});
|
|
243
|
+
O.render();
|
|
244
|
+
})();
|
|
245
|
+
|
|
246
|
+
</script>
|
|
247
|
+
</body>
|
|
248
|
+
</html>
|