nextclaw 0.44.2 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/app/index.js +2 -2
- package/dist/cli/launcher/index.js +1 -1
- package/dist/{nextclaw-cli-app-B_ffeXOD.js → nextclaw-cli-app-Bwa2L2Jv.js} +115 -22
- package/dist/nextclaw-cli-app-Bwa2L2Jv.js.map +1 -0
- package/dist/{nextclaw-distribution.utils-BneKclXo.js → nextclaw-distribution.utils-Bba2GRvc.js} +4 -2
- package/dist/nextclaw-distribution.utils-Bba2GRvc.js.map +1 -0
- package/package.json +10 -10
- package/resources/apps/nextclaw-portable-runtime-lab/README.md +34 -0
- package/resources/apps/nextclaw-portable-runtime-lab/assets/icon.svg +12 -0
- package/resources/apps/nextclaw-portable-runtime-lab/manifest.json +74 -0
- package/resources/apps/nextclaw-portable-runtime-lab/marketplace.json +21 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-contacts.panel/assets/app.js +92 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-contacts.panel/assets/style.css +43 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-contacts.panel/index.html +43 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-contacts.panel/panel-app.json +10 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-focus.panel/assets/app.js +132 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-focus.panel/assets/style.css +39 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-focus.panel/index.html +40 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-focus.panel/panel-app.json +14 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-notes.panel/assets/app.js +117 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-notes.panel/assets/style.css +41 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-notes.panel/index.html +41 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-notes.panel/panel-app.json +12 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-panel.panel/assets/app.js +266 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-panel.panel/assets/style.css +87 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-panel.panel/index.html +154 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-panel.panel/panel-app.json +29 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-todos.panel/assets/app.js +143 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-todos.panel/assets/style.css +48 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-todos.panel/index.html +45 -0
- package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-todos.panel/panel-app.json +12 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-capabilities/service-app.json +20 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-capabilities/service.wasm +0 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-composition/service-app.json +30 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-composition/service.wasm +0 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-provider/service-app.json +30 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-provider/service.wasm +0 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-resident/service-app.json +31 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-resident/service.wasm +0 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-state/service-app.json +49 -0
- package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-state/service.wasm +0 -0
- package/ui-dist/assets/{api-DNLQPHfQ.js → api-m33O1Kb0.js} +3 -3
- package/ui-dist/assets/api-m33O1Kb0.js.gz +0 -0
- package/ui-dist/assets/{appearance-settings-page-BlAFMOGV.js → appearance-settings-page-x4Vjnfmz.js} +1 -1
- package/ui-dist/assets/appearance-settings-page-x4Vjnfmz.js.gz +0 -0
- package/ui-dist/assets/{book-open-BryfPTTa.js → book-open-BHiasMCg.js} +1 -1
- package/ui-dist/assets/{channels-list-page-Cfbgubyv.js → channels-list-page-DOxJZeXq.js} +1 -1
- package/ui-dist/assets/channels-list-page-DOxJZeXq.js.gz +0 -0
- package/ui-dist/assets/{config-split-page-Dd8xG0Jk.js → config-split-page-DBnMEP1o.js} +1 -1
- package/ui-dist/assets/config-split-page-DBnMEP1o.js.gz +0 -0
- package/ui-dist/assets/{confirm-dialog-CZIWjnJ6.js → confirm-dialog-f5NKLh5I.js} +2 -2
- package/ui-dist/assets/confirm-dialog-f5NKLh5I.js.gz +0 -0
- package/ui-dist/assets/{createLucideIcon-BxwVe8ZD.js → createLucideIcon-CHALGA42.js} +1 -1
- package/ui-dist/assets/{desktop-capabilities-page-C6YxsHeV.js → desktop-capabilities-page-DixayAe6.js} +1 -1
- package/ui-dist/assets/desktop-capabilities-page-DixayAe6.js.gz +0 -0
- package/ui-dist/assets/{desktop-update-config-BVN4QPJn.js → desktop-update-config-ChhfeOHj.js} +1 -1
- package/ui-dist/assets/desktop-update-config-ChhfeOHj.js.gz +0 -0
- package/ui-dist/assets/{dist-Cd8Nm3BW.js → dist-_UMsWSY0.js} +1 -1
- package/ui-dist/assets/{dist-Cd8Nm3BW.js.gz → dist-_UMsWSY0.js.gz} +0 -0
- package/ui-dist/assets/{dist-BRloKr3S.js → dist-sD6V4nHX.js} +1 -1
- package/ui-dist/assets/dist-sD6V4nHX.js.gz +0 -0
- package/ui-dist/assets/doc-browser-Bq7LJsQY.js +1 -0
- package/ui-dist/assets/doc-browser-Bq7LJsQY.js.gz +0 -0
- package/ui-dist/assets/doc-browser-BuDgpY2e.js +1 -0
- package/ui-dist/assets/{doc-browser-context-3JGufiMA.js → doc-browser-context-Capim3og.js} +1 -1
- package/ui-dist/assets/doc-browser-context-Capim3og.js.gz +0 -0
- package/ui-dist/assets/{extensions-Dedd2oDI.js → extensions-Bdclr3Wr.js} +1 -1
- package/ui-dist/assets/extensions-Bdclr3Wr.js.gz +0 -0
- package/ui-dist/assets/{form-actions-FWCEof3V.js → form-actions-CH0kWhEt.js} +1 -1
- package/ui-dist/assets/index-BjxnLYE3.js +109 -0
- package/ui-dist/assets/index-BjxnLYE3.js.gz +0 -0
- package/ui-dist/assets/index-CBmwJjwE.css +1 -0
- package/ui-dist/assets/index-CBmwJjwE.css.gz +0 -0
- package/ui-dist/assets/loader-circle-CH_0iPLy.js +1 -0
- package/ui-dist/assets/{mcp-marketplace-page-e9Rmfz-d.js → mcp-marketplace-page-BUsxLVB5.js} +2 -2
- package/ui-dist/assets/mcp-marketplace-page-BUsxLVB5.js.gz +0 -0
- package/ui-dist/assets/mcp-marketplace-page-GPRV3fB0.js +1 -0
- package/ui-dist/assets/{model-config-page-C173Ssil.js → model-config-page-CIevwqp4.js} +1 -1
- package/ui-dist/assets/model-config-page-CIevwqp4.js.gz +0 -0
- package/ui-dist/assets/{navigation-link-DTPOvGGL.js → navigation-link-CAUiM3zS.js} +1 -1
- package/ui-dist/assets/navigation-link-CAUiM3zS.js.gz +0 -0
- package/ui-dist/assets/plus-B3obD55V.js +1 -0
- package/ui-dist/assets/{privacy-settings-page-CRserBVF.js → privacy-settings-page-CNKfZsYH.js} +1 -1
- package/ui-dist/assets/privacy-settings-page-CNKfZsYH.js.gz +0 -0
- package/ui-dist/assets/{providers-config-page-C8jUtnZb.js → providers-config-page-DAah2NIS.js} +1 -1
- package/ui-dist/assets/providers-config-page-DAah2NIS.js.gz +0 -0
- package/ui-dist/assets/{react-BAzO6LZS.js → react-Co6JiC1n.js} +1 -1
- package/ui-dist/assets/react-Co6JiC1n.js.gz +0 -0
- package/ui-dist/assets/{refresh-cw-BCj-yjde.js → refresh-cw-BG_LX3SS.js} +1 -1
- package/ui-dist/assets/remote-DuscgfvR.js +1 -0
- package/ui-dist/assets/{rotate-cw-BPnW4qUl.js → rotate-cw-Cz7I_m7O.js} +1 -1
- package/ui-dist/assets/{runtime-config-page-aeQ1Dryz.js → runtime-config-page-Cl1RpmSC.js} +1 -1
- package/ui-dist/assets/runtime-config-page-Cl1RpmSC.js.gz +0 -0
- package/ui-dist/assets/{save-KT0MumfJ.js → save-BiHnb8ps.js} +1 -1
- package/ui-dist/assets/{search-Cv1Hx_nx.js → search-C47TyRg0.js} +1 -1
- package/ui-dist/assets/{search-config-page-b283V-fo.js → search-config-page-BAIzd6gN.js} +1 -1
- package/ui-dist/assets/search-config-page-BAIzd6gN.js.gz +0 -0
- package/ui-dist/assets/{secrets-config-page-ss7x3BOo.js → secrets-config-page-Ddi-n43R.js} +1 -1
- package/ui-dist/assets/secrets-config-page-Ddi-n43R.js.gz +0 -0
- package/ui-dist/assets/security-config-C8MHnE1r.js +1 -0
- package/ui-dist/assets/{select-BVgD4EpG.js → select-BzQKfW1e.js} +2 -2
- package/ui-dist/assets/select-BzQKfW1e.js.gz +0 -0
- package/ui-dist/assets/skeleton-BXFAE9SE.js +1 -0
- package/ui-dist/assets/{tag-chip-DZ4ircUB.js → tag-chip-C3K814HZ.js} +1 -1
- package/ui-dist/assets/x-C1r9IXet.js +1 -0
- package/ui-dist/index.html +29 -21
- package/ui-dist/index.html.gz +0 -0
- package/ui-dist/themes/island/island-atmosphere.webp +0 -0
- package/dist/nextclaw-cli-app-B_ffeXOD.js.map +0 -1
- package/dist/nextclaw-distribution.utils-BneKclXo.js.map +0 -1
- package/ui-dist/assets/api-DNLQPHfQ.js.gz +0 -0
- package/ui-dist/assets/appearance-settings-page-BlAFMOGV.js.gz +0 -0
- package/ui-dist/assets/channels-list-page-Cfbgubyv.js.gz +0 -0
- package/ui-dist/assets/config-split-page-Dd8xG0Jk.js.gz +0 -0
- package/ui-dist/assets/confirm-dialog-CZIWjnJ6.js.gz +0 -0
- package/ui-dist/assets/desktop-capabilities-page-C6YxsHeV.js.gz +0 -0
- package/ui-dist/assets/desktop-update-config-BVN4QPJn.js.gz +0 -0
- package/ui-dist/assets/dist-BRloKr3S.js.gz +0 -0
- package/ui-dist/assets/doc-browser-Bzgqmmiu.js +0 -1
- package/ui-dist/assets/doc-browser-Bzgqmmiu.js.gz +0 -0
- package/ui-dist/assets/doc-browser-D0qI0Z_R.js +0 -1
- package/ui-dist/assets/doc-browser-context-3JGufiMA.js.gz +0 -0
- package/ui-dist/assets/extensions-Dedd2oDI.js.gz +0 -0
- package/ui-dist/assets/index-CXkHcESm.css +0 -1
- package/ui-dist/assets/index-CXkHcESm.css.gz +0 -0
- package/ui-dist/assets/index-D25tRg1p.js +0 -109
- package/ui-dist/assets/index-D25tRg1p.js.gz +0 -0
- package/ui-dist/assets/loader-circle-BoyCoFps.js +0 -1
- package/ui-dist/assets/mcp-marketplace-page-Cn-9CDh3.js +0 -1
- package/ui-dist/assets/mcp-marketplace-page-e9Rmfz-d.js.gz +0 -0
- package/ui-dist/assets/model-config-page-C173Ssil.js.gz +0 -0
- package/ui-dist/assets/navigation-link-DTPOvGGL.js.gz +0 -0
- package/ui-dist/assets/plus-D1R4Vipq.js +0 -1
- package/ui-dist/assets/privacy-settings-page-CRserBVF.js.gz +0 -0
- package/ui-dist/assets/providers-config-page-C8jUtnZb.js.gz +0 -0
- package/ui-dist/assets/react-BAzO6LZS.js.gz +0 -0
- package/ui-dist/assets/remote-6BHWP5zi.js +0 -1
- package/ui-dist/assets/runtime-config-page-aeQ1Dryz.js.gz +0 -0
- package/ui-dist/assets/search-config-page-b283V-fo.js.gz +0 -0
- package/ui-dist/assets/secrets-config-page-ss7x3BOo.js.gz +0 -0
- package/ui-dist/assets/security-config-HHz62oxH.js +0 -1
- package/ui-dist/assets/select-BVgD4EpG.js.gz +0 -0
- package/ui-dist/assets/skeleton-ChUmcyys.js +0 -1
- package/ui-dist/assets/x-BWDRfVWL.js +0 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const actionIds = {
|
|
2
|
+
list: "nextclaw-portable-runtime-lab-state.records_list",
|
|
3
|
+
upsert: "nextclaw-portable-runtime-lab-state.record_upsert",
|
|
4
|
+
remove: "nextclaw-portable-runtime-lab-state.record_delete",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const elements = {
|
|
8
|
+
form: document.querySelector("[data-note-form]"),
|
|
9
|
+
input: document.querySelector("[data-note-input]"),
|
|
10
|
+
search: document.querySelector("[data-search]"),
|
|
11
|
+
count: document.querySelector("[data-note-count]"),
|
|
12
|
+
characters: document.querySelector("[data-character-count]"),
|
|
13
|
+
grid: document.querySelector("[data-notes-grid]"),
|
|
14
|
+
notice: document.querySelector("[data-notice]"),
|
|
15
|
+
revision: document.querySelector("[data-proof-revision]"),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let notes = [];
|
|
19
|
+
|
|
20
|
+
function serviceActions() {
|
|
21
|
+
const bridge = window.nextclaw?.serviceActions;
|
|
22
|
+
if (!bridge) throw new Error("应用还没有连接到 NextClaw,请刷新后重试。");
|
|
23
|
+
return bridge;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function invoke(actionId, input = {}) {
|
|
27
|
+
return await serviceActions().invoke(actionId, input);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function setNotice(message, tone = "") {
|
|
31
|
+
elements.notice.textContent = message;
|
|
32
|
+
elements.notice.dataset.tone = tone;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function noteCard(note, index) {
|
|
36
|
+
const card = document.createElement("article");
|
|
37
|
+
card.className = `note-card color-${index % 4}`;
|
|
38
|
+
const content = document.createElement("p");
|
|
39
|
+
content.textContent = note.title;
|
|
40
|
+
const footer = document.createElement("footer");
|
|
41
|
+
const version = document.createElement("span");
|
|
42
|
+
version.textContent = `保存于版本 ${note.version}`;
|
|
43
|
+
const remove = document.createElement("button");
|
|
44
|
+
remove.type = "button";
|
|
45
|
+
remove.dataset.noteRemove = note.id;
|
|
46
|
+
remove.textContent = "删除";
|
|
47
|
+
footer.append(version, remove);
|
|
48
|
+
card.append(content, footer);
|
|
49
|
+
return card;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function render() {
|
|
53
|
+
const query = elements.search.value.trim().toLocaleLowerCase("zh-CN");
|
|
54
|
+
const filtered = [...notes]
|
|
55
|
+
.sort((left, right) => right.version - left.version)
|
|
56
|
+
.filter((note) => !query || note.title.toLocaleLowerCase("zh-CN").includes(query));
|
|
57
|
+
elements.grid.replaceChildren();
|
|
58
|
+
if (filtered.length === 0) {
|
|
59
|
+
const empty = document.createElement("div");
|
|
60
|
+
empty.className = "empty";
|
|
61
|
+
empty.innerHTML = query
|
|
62
|
+
? "<strong>没有找到相关便签</strong><span>换个关键词试试。</span>"
|
|
63
|
+
: "<strong>便签盒还是空的</strong><span>写下刚刚想到的第一句话吧。</span>";
|
|
64
|
+
elements.grid.append(empty);
|
|
65
|
+
} else {
|
|
66
|
+
filtered.forEach((note, index) => elements.grid.append(noteCard(note, index)));
|
|
67
|
+
}
|
|
68
|
+
elements.count.textContent = `${notes.length} 张便签`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function refresh() {
|
|
72
|
+
const data = await invoke(actionIds.list);
|
|
73
|
+
notes = (Array.isArray(data.records) ? data.records : [])
|
|
74
|
+
.filter((record) => Array.isArray(record.tags) && record.tags.includes("note"));
|
|
75
|
+
elements.revision.textContent = `revision ${data.revision} · ${data.contentHash}`;
|
|
76
|
+
render();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function run(operation, successMessage) {
|
|
80
|
+
setNotice("正在保存…");
|
|
81
|
+
try {
|
|
82
|
+
await operation();
|
|
83
|
+
await refresh();
|
|
84
|
+
setNotice(successMessage, "success");
|
|
85
|
+
} catch (error) {
|
|
86
|
+
setNotice(error instanceof Error ? error.message : String(error), "error");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
elements.input.addEventListener("input", () => {
|
|
91
|
+
elements.characters.textContent = `${elements.input.value.length} / 800`;
|
|
92
|
+
});
|
|
93
|
+
elements.search.addEventListener("input", render);
|
|
94
|
+
|
|
95
|
+
elements.form.addEventListener("submit", (event) => {
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
const content = elements.input.value.trim();
|
|
98
|
+
if (!content) return;
|
|
99
|
+
void run(async () => {
|
|
100
|
+
await invoke(actionIds.upsert, {
|
|
101
|
+
id: `note-${Date.now().toString(36)}`,
|
|
102
|
+
title: content,
|
|
103
|
+
status: "note",
|
|
104
|
+
tags: ["note", "quick-capture"],
|
|
105
|
+
});
|
|
106
|
+
elements.input.value = "";
|
|
107
|
+
elements.characters.textContent = "0 / 800";
|
|
108
|
+
}, "便签已保存");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
elements.grid.addEventListener("click", (event) => {
|
|
112
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
113
|
+
const id = target?.closest("[data-note-remove]")?.dataset.noteRemove;
|
|
114
|
+
if (id) void run(() => invoke(actionIds.remove, { id }), "便签已删除");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
void refresh().catch((error) => setNotice(error instanceof Error ? error.message : String(error), "error"));
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
:root { color-scheme: light; font-family: Inter, "PingFang SC", "Microsoft YaHei", sans-serif; color: #3d342b; background: #f7f0df; }
|
|
2
|
+
* { box-sizing: border-box; }
|
|
3
|
+
body { margin: 0; min-width: 320px; background: linear-gradient(135deg, #fffdf5, #f5ecd8 58%, #eee3cf); }
|
|
4
|
+
button, input, textarea { font: inherit; }
|
|
5
|
+
button { cursor: pointer; }
|
|
6
|
+
.app-shell { width: min(900px, calc(100% - 32px)); margin: 0 auto; padding: 38px 0 52px; }
|
|
7
|
+
.hero { margin-bottom: 24px; }
|
|
8
|
+
.eyebrow { margin: 0 0 8px; color: #b36e32; font-size: 12px; font-weight: 800; letter-spacing: .18em; }
|
|
9
|
+
h1 { margin: 0; font-family: Georgia, "Noto Serif SC", serif; font-size: clamp(36px, 7vw, 58px); letter-spacing: -.04em; }
|
|
10
|
+
.hero > p:last-child { margin: 8px 0 0; color: #776b5f; }
|
|
11
|
+
.note-composer { padding: 16px; border: 1px solid #eadcc2; border-radius: 20px; background: rgba(255,255,255,.82); box-shadow: 0 18px 55px rgba(91,68,33,.1); }
|
|
12
|
+
.note-composer textarea { width: 100%; resize: vertical; border: 0; outline: 0; padding: 4px; color: #463b30; background: transparent; font-size: 17px; line-height: 1.65; }
|
|
13
|
+
.note-composer > div { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; }
|
|
14
|
+
.note-composer span { color: #a19483; font-size: 12px; }
|
|
15
|
+
.note-composer button { border: 0; border-radius: 12px; padding: 11px 20px; color: #fff; background: #8e5c32; font-weight: 700; }
|
|
16
|
+
.toolbar { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin: 24px 2px 14px; color: #827668; font-size: 13px; }
|
|
17
|
+
.toolbar label { display: grid; gap: 6px; flex: 1; max-width: 340px; }
|
|
18
|
+
.toolbar input { width: 100%; border: 1px solid #dfd1b9; border-radius: 12px; outline: 0; padding: 10px 12px; color: #473d32; background: rgba(255,255,255,.65); }
|
|
19
|
+
.notes-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
|
|
20
|
+
.note-card { display: flex; min-height: 190px; flex-direction: column; justify-content: space-between; padding: 20px; border: 1px solid rgba(105,82,49,.1); border-radius: 5px 18px 18px 18px; box-shadow: 0 10px 22px rgba(70,52,26,.08); transform: rotate(-.25deg); }
|
|
21
|
+
.note-card:nth-child(2n) { transform: rotate(.25deg); }
|
|
22
|
+
.note-card.color-0 { background: #fff0a9; }
|
|
23
|
+
.note-card.color-1 { background: #dff0d7; }
|
|
24
|
+
.note-card.color-2 { background: #f9d8cc; }
|
|
25
|
+
.note-card.color-3 { background: #dce8f6; }
|
|
26
|
+
.note-card p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.65; }
|
|
27
|
+
.note-card footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 18px; color: rgba(64,51,37,.55); font-size: 11px; }
|
|
28
|
+
.note-card button { border: 0; color: #855243; background: transparent; }
|
|
29
|
+
.empty { display: flex; min-height: 220px; grid-column: 1 / -1; flex-direction: column; align-items: center; justify-content: center; gap: 8px; border: 1px dashed #d8c9af; border-radius: 18px; color: #918574; text-align: center; }
|
|
30
|
+
.empty strong { color: #625749; font-size: 18px; }
|
|
31
|
+
.notice { min-height: 22px; margin: 14px 2px; color: #817567; font-size: 14px; }
|
|
32
|
+
.notice[data-tone="success"] { color: #557546; }
|
|
33
|
+
.notice[data-tone="error"] { color: #ad4c43; }
|
|
34
|
+
.behind-scenes { margin-top: 28px; padding: 16px 18px; border: 1px solid #e4d8c4; border-radius: 16px; color: #756a5d; background: rgba(255,255,255,.5); font-size: 13px; }
|
|
35
|
+
.behind-scenes summary { color: #574c3f; cursor: pointer; font-weight: 700; }
|
|
36
|
+
.behind-scenes p { line-height: 1.65; }
|
|
37
|
+
.behind-scenes dl { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 12px 0 0; }
|
|
38
|
+
.behind-scenes dl div { padding: 10px; border-radius: 10px; background: #fffaf0; }
|
|
39
|
+
.behind-scenes dt { color: #9d9182; font-size: 11px; }
|
|
40
|
+
.behind-scenes dd { margin: 3px 0 0; color: #50463b; overflow-wrap: anywhere; }
|
|
41
|
+
@media (max-width: 560px) { .app-shell { width: calc(100% - 22px); padding-top: 24px; } .toolbar { align-items: stretch; flex-direction: column; } .toolbar label { max-width: none; } .behind-scenes dl { grid-template-columns: 1fr; } }
|
|
@@ -0,0 +1,41 @@
|
|
|
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.0" />
|
|
6
|
+
<title>灵感便签</title>
|
|
7
|
+
<link rel="stylesheet" href="assets/style.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main class="app-shell">
|
|
11
|
+
<header class="hero">
|
|
12
|
+
<p class="eyebrow">QUICK NOTES</p>
|
|
13
|
+
<h1>灵感便签</h1>
|
|
14
|
+
<p>先记下来,不让刚刚想到的东西溜走。</p>
|
|
15
|
+
</header>
|
|
16
|
+
|
|
17
|
+
<form class="note-composer" data-note-form>
|
|
18
|
+
<textarea data-note-input rows="4" maxlength="800" placeholder="写下一句话、一个想法,或者稍后要查的东西…"></textarea>
|
|
19
|
+
<div><span data-character-count>0 / 800</span><button type="submit">保存便签</button></div>
|
|
20
|
+
</form>
|
|
21
|
+
|
|
22
|
+
<div class="toolbar">
|
|
23
|
+
<label><span>搜索便签</span><input data-search type="search" placeholder="输入关键词" /></label>
|
|
24
|
+
<span data-note-count>0 张便签</span>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<section class="notes-grid" data-notes-grid aria-live="polite">
|
|
28
|
+
<div class="empty">正在打开你的便签盒…</div>
|
|
29
|
+
</section>
|
|
30
|
+
|
|
31
|
+
<p class="notice" data-notice></p>
|
|
32
|
+
|
|
33
|
+
<details class="behind-scenes">
|
|
34
|
+
<summary>为什么它也是运行时 Demo?</summary>
|
|
35
|
+
<p>便签和“今日清单”是两个独立 Panel,却复用同一个底层数据 Component。产品可以变,稳定的存储与授权能力不用为每个页面重新实现。</p>
|
|
36
|
+
<dl><div><dt>当前数据版本</dt><dd data-proof-revision>等待读取</dd></div><div><dt>可被 Agent 使用</dt><dd>是,同一 Service Action</dd></div></dl>
|
|
37
|
+
</details>
|
|
38
|
+
</main>
|
|
39
|
+
<script src="assets/app.js"></script>
|
|
40
|
+
</body>
|
|
41
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nextclaw-portable-runtime-lab-notes",
|
|
3
|
+
"title": "灵感便签",
|
|
4
|
+
"description": "随手记下一句话,之后还能搜索回来。",
|
|
5
|
+
"entry": "index.html",
|
|
6
|
+
"actions": [
|
|
7
|
+
"nextclaw-portable-runtime-lab-state.records_list",
|
|
8
|
+
"nextclaw-portable-runtime-lab-state.record_upsert",
|
|
9
|
+
"nextclaw-portable-runtime-lab-state.record_delete",
|
|
10
|
+
"nextclaw-portable-runtime-lab-state.data_snapshot"
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
const actions = {
|
|
2
|
+
counterRead: "nextclaw-portable-runtime-lab-state.counter_read",
|
|
3
|
+
counterIncrement: "nextclaw-portable-runtime-lab-state.counter_increment",
|
|
4
|
+
recordsSeed: "nextclaw-portable-runtime-lab-state.records_seed",
|
|
5
|
+
recordsList: "nextclaw-portable-runtime-lab-state.records_list",
|
|
6
|
+
recordUpsert: "nextclaw-portable-runtime-lab-state.record_upsert",
|
|
7
|
+
recordDelete: "nextclaw-portable-runtime-lab-state.record_delete",
|
|
8
|
+
dataSnapshot: "nextclaw-portable-runtime-lab-state.data_snapshot",
|
|
9
|
+
stateRuntime: "nextclaw-portable-runtime-lab-state.runtime_info",
|
|
10
|
+
networkAllowed: "nextclaw-portable-runtime-lab-capabilities.network_allowed",
|
|
11
|
+
networkDenied: "nextclaw-portable-runtime-lab-capabilities.network_denied",
|
|
12
|
+
structuredFailure: "nextclaw-portable-runtime-lab-capabilities.structured_failure",
|
|
13
|
+
timeout: "nextclaw-portable-runtime-lab-capabilities.simulate_timeout",
|
|
14
|
+
capabilityRuntime: "nextclaw-portable-runtime-lab-capabilities.runtime_info",
|
|
15
|
+
residentStatus: "nextclaw-portable-runtime-lab-resident.resident_status",
|
|
16
|
+
residentEmitEvent: "nextclaw-portable-runtime-lab-resident.resident_emit_event",
|
|
17
|
+
residentReset: "nextclaw-portable-runtime-lab-resident.resident_reset",
|
|
18
|
+
providerStatus: "nextclaw-portable-runtime-lab-provider.provider_status",
|
|
19
|
+
composeContact: "nextclaw-portable-runtime-lab-composition.compose_contact",
|
|
20
|
+
providerDenied: "nextclaw-portable-runtime-lab-composition.provider_denied",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const scenarios = {
|
|
24
|
+
shared: document.querySelector('[data-scenario="shared"]'),
|
|
25
|
+
data: document.querySelector('[data-scenario="data"]'),
|
|
26
|
+
resident: document.querySelector('[data-scenario="resident"]'),
|
|
27
|
+
composition: document.querySelector('[data-scenario="composition"]'),
|
|
28
|
+
network: document.querySelector('[data-scenario="network"]'),
|
|
29
|
+
failure: document.querySelector('[data-scenario="failure"]'),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const residentElements = {
|
|
33
|
+
epoch: document.querySelector('[data-resident="epoch"]'),
|
|
34
|
+
memoryCount: document.querySelector('[data-resident="memory-count"]'),
|
|
35
|
+
eventCount: document.querySelector('[data-resident="event-count"]'),
|
|
36
|
+
lastEvent: document.querySelector('[data-resident="last-event"]'),
|
|
37
|
+
events: document.querySelector("[data-resident-events]"),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const compositionElements = {
|
|
41
|
+
name: document.querySelector('[data-field="contact-name"]'),
|
|
42
|
+
email: document.querySelector('[data-field="contact-email"]'),
|
|
43
|
+
tags: document.querySelector('[data-field="contact-tags"]'),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const dataElements = {
|
|
47
|
+
id: document.querySelector('[data-field="record-id"]'),
|
|
48
|
+
title: document.querySelector('[data-field="record-title"]'),
|
|
49
|
+
status: document.querySelector('[data-field="record-status"]'),
|
|
50
|
+
records: document.querySelector("[data-records]"),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function bridge() {
|
|
54
|
+
const serviceActions = window.nextclaw?.serviceActions;
|
|
55
|
+
if (!serviceActions) throw new Error("NextClaw Panel bridge 尚未就绪,请刷新后重试。");
|
|
56
|
+
return serviceActions;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function invoke(actionId, input = {}) {
|
|
60
|
+
return await bridge().invoke(actionId, input);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function setState(scenario, state, value) {
|
|
64
|
+
const container = scenarios[scenario];
|
|
65
|
+
const result = container.querySelector(".result");
|
|
66
|
+
container.dataset.state = state;
|
|
67
|
+
result.textContent = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
68
|
+
for (const button of container.querySelectorAll("button")) {
|
|
69
|
+
button.disabled = state === "pending";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function errorValue(error) {
|
|
74
|
+
return {
|
|
75
|
+
code: error?.code ?? "ERROR",
|
|
76
|
+
message: error instanceof Error ? error.message : String(error),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function run(scenario, operation) {
|
|
81
|
+
setState(scenario, "pending", "正在通过宿主执行…");
|
|
82
|
+
try {
|
|
83
|
+
const result = await operation();
|
|
84
|
+
setState(scenario, "success", result);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
setState(scenario, "error", errorValue(error));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function renderRecords(data) {
|
|
91
|
+
const records = Array.isArray(data?.records) ? data.records : [];
|
|
92
|
+
dataElements.records.replaceChildren();
|
|
93
|
+
if (records.length === 0) {
|
|
94
|
+
const row = document.createElement("tr");
|
|
95
|
+
const cell = document.createElement("td");
|
|
96
|
+
cell.colSpan = 5;
|
|
97
|
+
cell.textContent = "当前没有记录";
|
|
98
|
+
row.append(cell);
|
|
99
|
+
dataElements.records.append(row);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
for (const record of records) {
|
|
103
|
+
const row = document.createElement("tr");
|
|
104
|
+
for (const value of [
|
|
105
|
+
record.id,
|
|
106
|
+
record.title,
|
|
107
|
+
record.status,
|
|
108
|
+
Array.isArray(record.tags) ? record.tags.join(", ") : "",
|
|
109
|
+
record.version,
|
|
110
|
+
]) {
|
|
111
|
+
const cell = document.createElement("td");
|
|
112
|
+
cell.textContent = String(value ?? "");
|
|
113
|
+
row.append(cell);
|
|
114
|
+
}
|
|
115
|
+
dataElements.records.append(row);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function refreshData() {
|
|
120
|
+
const data = await invoke(actions.recordsList);
|
|
121
|
+
renderRecords(data);
|
|
122
|
+
return {
|
|
123
|
+
revision: data.revision,
|
|
124
|
+
recordCount: data.recordCount,
|
|
125
|
+
contentHash: data.contentHash,
|
|
126
|
+
persistedBy: data.persistedBy,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function recordInput() {
|
|
131
|
+
return {
|
|
132
|
+
id: dataElements.id.value.trim(),
|
|
133
|
+
title: dataElements.title.value.trim(),
|
|
134
|
+
status: dataElements.status.value,
|
|
135
|
+
tags: ["panel", "verification"],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function renderResident(data) {
|
|
140
|
+
residentElements.epoch.textContent = String(data?.instanceEpoch ?? "—");
|
|
141
|
+
residentElements.memoryCount.textContent = String(data?.inMemoryEventCount ?? "—");
|
|
142
|
+
residentElements.eventCount.textContent = String(data?.eventCount ?? "—");
|
|
143
|
+
residentElements.lastEvent.textContent = data?.lastEventKind
|
|
144
|
+
? `${data.lastEventKind} · ${data.lastEventId}`
|
|
145
|
+
: "—";
|
|
146
|
+
const events = Array.isArray(data?.recentEvents) ? [...data.recentEvents].reverse() : [];
|
|
147
|
+
residentElements.events.replaceChildren();
|
|
148
|
+
if (events.length === 0) {
|
|
149
|
+
const row = document.createElement("tr");
|
|
150
|
+
const cell = document.createElement("td");
|
|
151
|
+
cell.colSpan = 3;
|
|
152
|
+
cell.textContent = "当前没有事件";
|
|
153
|
+
row.append(cell);
|
|
154
|
+
residentElements.events.append(row);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
for (const event of events) {
|
|
158
|
+
const row = document.createElement("tr");
|
|
159
|
+
for (const value of [event.eventId, event.kind, event.triggeredAt]) {
|
|
160
|
+
const cell = document.createElement("td");
|
|
161
|
+
cell.textContent = String(value ?? "");
|
|
162
|
+
row.append(cell);
|
|
163
|
+
}
|
|
164
|
+
residentElements.events.append(row);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function refreshResident() {
|
|
169
|
+
const data = await invoke(actions.residentStatus);
|
|
170
|
+
renderResident(data);
|
|
171
|
+
return {
|
|
172
|
+
started: data.started,
|
|
173
|
+
instanceEpoch: data.instanceEpoch,
|
|
174
|
+
inMemoryEventCount: data.inMemoryEventCount,
|
|
175
|
+
eventCount: data.eventCount,
|
|
176
|
+
lastEventId: data.lastEventId,
|
|
177
|
+
persistedBy: data.persistedBy,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const operations = {
|
|
182
|
+
shared: () => run("shared", async () => {
|
|
183
|
+
const state = await invoke(actions.stateRuntime);
|
|
184
|
+
const capability = await invoke(actions.capabilityRuntime);
|
|
185
|
+
return {
|
|
186
|
+
sameRunner: state.runnerPid === capability.runnerPid,
|
|
187
|
+
stateComponent: state,
|
|
188
|
+
capabilityComponent: capability,
|
|
189
|
+
};
|
|
190
|
+
}),
|
|
191
|
+
"data-refresh": () => run("data", refreshData),
|
|
192
|
+
"data-seed": () => run("data", async () => {
|
|
193
|
+
await invoke(actions.recordsSeed);
|
|
194
|
+
return await refreshData();
|
|
195
|
+
}),
|
|
196
|
+
"data-save": () => run("data", async () => {
|
|
197
|
+
await invoke(actions.recordUpsert, recordInput());
|
|
198
|
+
return await refreshData();
|
|
199
|
+
}),
|
|
200
|
+
"data-delete": () => run("data", async () => {
|
|
201
|
+
await invoke(actions.recordDelete, { id: dataElements.id.value.trim() });
|
|
202
|
+
return await refreshData();
|
|
203
|
+
}),
|
|
204
|
+
"resident-refresh": () => run("resident", refreshResident),
|
|
205
|
+
"resident-event": () => run("resident", async () => {
|
|
206
|
+
await invoke(actions.residentEmitEvent, {
|
|
207
|
+
eventId: `panel-${Date.now()}`,
|
|
208
|
+
kind: "panel-manual",
|
|
209
|
+
triggeredAt: new Date().toISOString(),
|
|
210
|
+
});
|
|
211
|
+
return await refreshResident();
|
|
212
|
+
}),
|
|
213
|
+
"resident-reset": () => run("resident", async () => {
|
|
214
|
+
await invoke(actions.residentReset);
|
|
215
|
+
return await refreshResident();
|
|
216
|
+
}),
|
|
217
|
+
"composition-run": () => run("composition", () => invoke(actions.composeContact, {
|
|
218
|
+
name: compositionElements.name.value,
|
|
219
|
+
email: compositionElements.email.value,
|
|
220
|
+
tags: compositionElements.tags.value.split(",").map((tag) => tag.trim()),
|
|
221
|
+
})),
|
|
222
|
+
"provider-status": () => run("composition", () => invoke(actions.providerStatus)),
|
|
223
|
+
"provider-denied": () => run("composition", () => invoke(actions.providerDenied)),
|
|
224
|
+
"network-allowed": () => run("network", () => invoke(actions.networkAllowed)),
|
|
225
|
+
"network-denied": () => run("network", () => invoke(actions.networkDenied)),
|
|
226
|
+
"structured-failure": () => run("failure", async () => {
|
|
227
|
+
try {
|
|
228
|
+
await invoke(actions.structuredFailure);
|
|
229
|
+
return { failedAsExpected: false };
|
|
230
|
+
} catch (error) {
|
|
231
|
+
return { failedAsExpected: true, error: errorValue(error) };
|
|
232
|
+
}
|
|
233
|
+
}),
|
|
234
|
+
"timeout-recovery": () => run("failure", async () => {
|
|
235
|
+
let timeout;
|
|
236
|
+
try {
|
|
237
|
+
await invoke(actions.timeout);
|
|
238
|
+
timeout = { timedOutAsExpected: false };
|
|
239
|
+
} catch (error) {
|
|
240
|
+
timeout = { timedOutAsExpected: error?.code === "SERVICE_APP_RUNTIME_FAILED", error: errorValue(error) };
|
|
241
|
+
}
|
|
242
|
+
const persistedState = await invoke(actions.dataSnapshot);
|
|
243
|
+
const recoveredRuntime = await invoke(actions.stateRuntime);
|
|
244
|
+
return { timeout, recovered: true, persistedState, recoveredRuntime };
|
|
245
|
+
}),
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
for (const button of document.querySelectorAll("[data-run]")) {
|
|
249
|
+
button.addEventListener("click", () => operations[button.dataset.run]?.());
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
void refreshResident().then((result) => {
|
|
253
|
+
setState("resident", "success", result);
|
|
254
|
+
}).catch((error) => {
|
|
255
|
+
setState("resident", "error", errorValue(error));
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const residentPoll = setInterval(() => {
|
|
259
|
+
if (document.visibilityState !== "visible") return;
|
|
260
|
+
void refreshResident().then((result) => {
|
|
261
|
+
setState("resident", "success", result);
|
|
262
|
+
}).catch((error) => {
|
|
263
|
+
setState("resident", "error", errorValue(error));
|
|
264
|
+
});
|
|
265
|
+
}, 2000);
|
|
266
|
+
window.addEventListener("pagehide", () => clearInterval(residentPoll), { once: true });
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: light dark;
|
|
3
|
+
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
4
|
+
color: #dce9f8;
|
|
5
|
+
background: #07111f;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
* { box-sizing: border-box; }
|
|
9
|
+
body { margin: 0; min-width: 300px; background: radial-gradient(circle at 85% -10%, #123763 0, transparent 36%), #07111f; }
|
|
10
|
+
button, pre { font: inherit; }
|
|
11
|
+
.shell { width: min(1020px, 100%); margin: 0 auto; padding: 34px 24px 48px; }
|
|
12
|
+
.hero { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; }
|
|
13
|
+
.eyebrow { margin: 0 0 10px; color: #5eead4; font-size: 11px; font-weight: 700; letter-spacing: .14em; }
|
|
14
|
+
h1 { margin: 0; font-size: clamp(28px, 5vw, 46px); letter-spacing: -.04em; line-height: 1.05; }
|
|
15
|
+
.lead { max-width: 660px; margin: 14px 0 0; color: #91a8c2; font-size: 15px; line-height: 1.7; }
|
|
16
|
+
.live-badge { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; border: 1px solid #214363; border-radius: 999px; padding: 7px 11px; color: #b8d2ec; font-size: 12px; }
|
|
17
|
+
.live-badge i { width: 7px; height: 7px; border-radius: 50%; background: #2dd4bf; box-shadow: 0 0 0 4px #2dd4bf1f; }
|
|
18
|
+
.topology { display: grid; grid-template-columns: 1fr auto 1.12fr auto 1fr; align-items: center; gap: 12px; margin: 34px 0; color: #4f7194; }
|
|
19
|
+
.node { min-width: 0; border: 1px solid #1c3550; border-radius: 14px; padding: 14px; text-align: center; color: #dce9f8; background: #0b1a2bba; font-weight: 650; }
|
|
20
|
+
.node small { display: block; margin-top: 5px; color: #6f8aa8; font-size: 11px; font-weight: 500; }
|
|
21
|
+
.node.host { border-color: #2563eb80; background: linear-gradient(135deg, #0f2945, #10213b); box-shadow: inset 0 0 24px #2563eb16; }
|
|
22
|
+
.scenario-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
|
|
23
|
+
.scenario { display: flex; min-height: 280px; flex-direction: column; gap: 18px; border: 1px solid #18314c; border-radius: 18px; padding: 20px; background: #0a1828dc; transition: border-color .2s, background .2s; }
|
|
24
|
+
.scenario[data-state="pending"] { border-color: #38bdf866; background: #0b1d31; }
|
|
25
|
+
.scenario[data-state="success"] { border-color: #2dd4bf66; }
|
|
26
|
+
.scenario[data-state="error"] { border-color: #fb718566; }
|
|
27
|
+
.scenario.benchmark-evidence { grid-column: 1 / -1; min-height: auto; }
|
|
28
|
+
.scenario.data-workbench { grid-column: 1 / -1; min-height: auto; }
|
|
29
|
+
.scenario.resident-workbench { grid-column: 1 / -1; min-height: auto; }
|
|
30
|
+
.scenario.composition-workbench { grid-column: 1 / -1; min-height: auto; }
|
|
31
|
+
.scenario-copy { display: flex; gap: 13px; }
|
|
32
|
+
.number { color: #5eead4; font-size: 12px; font-weight: 800; line-height: 25px; }
|
|
33
|
+
h2 { margin: 0; font-size: 17px; letter-spacing: -.015em; }
|
|
34
|
+
.scenario p { margin: 7px 0 0; color: #8099b5; font-size: 13px; line-height: 1.55; }
|
|
35
|
+
.button-row { display: flex; flex-wrap: wrap; gap: 9px; }
|
|
36
|
+
button { min-height: 36px; border: 1px solid #3b82f6; border-radius: 9px; padding: 8px 13px; color: white; background: #2563eb; font-size: 13px; font-weight: 650; cursor: pointer; transition: background .15s, border-color .15s, transform .15s; }
|
|
37
|
+
button:hover:not(:disabled) { background: #1d4ed8; }
|
|
38
|
+
button:active:not(:disabled) { transform: translateY(1px); }
|
|
39
|
+
button:focus-visible { outline: 2px solid #93c5fd; outline-offset: 2px; }
|
|
40
|
+
button.secondary { border-color: #2b4866; color: #bed0e3; background: #10243a; }
|
|
41
|
+
button.secondary:hover:not(:disabled) { background: #16304b; }
|
|
42
|
+
button.danger { border-color: #9f3b52; background: #7f1d36; }
|
|
43
|
+
button.danger:hover:not(:disabled) { background: #9f2444; }
|
|
44
|
+
button:disabled { cursor: wait; opacity: .55; }
|
|
45
|
+
.data-editor { display: grid; grid-template-columns: minmax(120px, .7fr) minmax(220px, 2fr) minmax(110px, .6fr); gap: 10px; }
|
|
46
|
+
.data-editor label { display: grid; gap: 6px; color: #7892ae; font-size: 11px; font-weight: 650; }
|
|
47
|
+
.data-editor input, .data-editor select { width: 100%; min-height: 38px; border: 1px solid #23425f; border-radius: 9px; padding: 8px 10px; color: #dce9f8; background: #071321; font: inherit; font-size: 13px; }
|
|
48
|
+
.data-editor input:focus, .data-editor select:focus { outline: 2px solid #3b82f6; outline-offset: 1px; }
|
|
49
|
+
.data-table-wrap { overflow-x: auto; border: 1px solid #142a40; border-radius: 11px; }
|
|
50
|
+
table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
51
|
+
th, td { padding: 9px 11px; border-bottom: 1px solid #142a40; text-align: left; }
|
|
52
|
+
th { color: #7892ae; background: #071321; font-size: 10px; letter-spacing: .04em; text-transform: uppercase; }
|
|
53
|
+
td { color: #a8bfd7; }
|
|
54
|
+
tbody tr:last-child td { border-bottom: 0; }
|
|
55
|
+
.data-meta { flex: none; min-height: 58px; max-height: 110px; }
|
|
56
|
+
.composition-editor { grid-template-columns: 1fr 1.3fr 1fr; }
|
|
57
|
+
.composition-result { min-height: 130px; }
|
|
58
|
+
.resident-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin: 0; }
|
|
59
|
+
.resident-metrics div { min-width: 0; border: 1px solid #18314c; border-radius: 12px; padding: 12px; background: #071321; }
|
|
60
|
+
.resident-metrics dt { color: #7892ae; font-size: 10px; }
|
|
61
|
+
.resident-metrics dd { overflow: hidden; margin: 6px 0 0; color: #9be8dc; font-family: "SFMono-Regular", Consolas, monospace; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
|
|
62
|
+
.result { flex: 1; min-height: 92px; max-height: 190px; overflow: auto; margin: auto 0 0; border: 1px solid #142a40; border-radius: 11px; padding: 12px; color: #8eabc8; background: #050d17; font-family: "SFMono-Regular", Consolas, monospace; font-size: 11px; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
63
|
+
.scenario[data-state="success"] .result { color: #9be8dc; }
|
|
64
|
+
.scenario[data-state="error"] .result { color: #fda4af; }
|
|
65
|
+
.metric-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin: 0; }
|
|
66
|
+
.metric-grid div { border: 1px solid #18314c; border-radius: 12px; padding: 13px; background: #071321; }
|
|
67
|
+
.metric-grid dt { min-height: 34px; color: #7892ae; font-size: 11px; line-height: 1.45; }
|
|
68
|
+
.metric-grid dd { margin: 7px 0 0; color: #9be8dc; font-family: "SFMono-Regular", Consolas, monospace; font-size: 18px; font-weight: 700; }
|
|
69
|
+
.metric-grid small { color: #6884a2; font-size: 10px; font-weight: 600; }
|
|
70
|
+
.benchmark-note { margin-top: 0 !important; }
|
|
71
|
+
footer { margin-top: 18px; border-left: 2px solid #2dd4bf; padding: 8px 0 8px 14px; color: #718aa5; font-size: 12px; line-height: 1.65; }
|
|
72
|
+
footer strong { color: #a8bfd7; }
|
|
73
|
+
|
|
74
|
+
@media (max-width: 720px) {
|
|
75
|
+
.shell { padding: 24px 15px 36px; }
|
|
76
|
+
.hero { flex-direction: column; }
|
|
77
|
+
.topology { grid-template-columns: 1fr; }
|
|
78
|
+
.topology > span { transform: rotate(90deg); text-align: center; }
|
|
79
|
+
.scenario-grid { grid-template-columns: 1fr; }
|
|
80
|
+
.data-editor { grid-template-columns: 1fr; }
|
|
81
|
+
.metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
82
|
+
.resident-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@media (prefers-reduced-motion: reduce) {
|
|
86
|
+
* { scroll-behavior: auto !important; transition: none !important; }
|
|
87
|
+
}
|