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.
Files changed (144) hide show
  1. package/dist/cli/app/index.js +2 -2
  2. package/dist/cli/launcher/index.js +1 -1
  3. package/dist/{nextclaw-cli-app-B_ffeXOD.js → nextclaw-cli-app-Bwa2L2Jv.js} +115 -22
  4. package/dist/nextclaw-cli-app-Bwa2L2Jv.js.map +1 -0
  5. package/dist/{nextclaw-distribution.utils-BneKclXo.js → nextclaw-distribution.utils-Bba2GRvc.js} +4 -2
  6. package/dist/nextclaw-distribution.utils-Bba2GRvc.js.map +1 -0
  7. package/package.json +10 -10
  8. package/resources/apps/nextclaw-portable-runtime-lab/README.md +34 -0
  9. package/resources/apps/nextclaw-portable-runtime-lab/assets/icon.svg +12 -0
  10. package/resources/apps/nextclaw-portable-runtime-lab/manifest.json +74 -0
  11. package/resources/apps/nextclaw-portable-runtime-lab/marketplace.json +21 -0
  12. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-contacts.panel/assets/app.js +92 -0
  13. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-contacts.panel/assets/style.css +43 -0
  14. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-contacts.panel/index.html +43 -0
  15. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-contacts.panel/panel-app.json +10 -0
  16. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-focus.panel/assets/app.js +132 -0
  17. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-focus.panel/assets/style.css +39 -0
  18. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-focus.panel/index.html +40 -0
  19. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-focus.panel/panel-app.json +14 -0
  20. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-notes.panel/assets/app.js +117 -0
  21. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-notes.panel/assets/style.css +41 -0
  22. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-notes.panel/index.html +41 -0
  23. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-notes.panel/panel-app.json +12 -0
  24. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-panel.panel/assets/app.js +266 -0
  25. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-panel.panel/assets/style.css +87 -0
  26. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-panel.panel/index.html +154 -0
  27. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-panel.panel/panel-app.json +29 -0
  28. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-todos.panel/assets/app.js +143 -0
  29. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-todos.panel/assets/style.css +48 -0
  30. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-todos.panel/index.html +45 -0
  31. package/resources/apps/nextclaw-portable-runtime-lab/panels/nextclaw-portable-runtime-lab-todos.panel/panel-app.json +12 -0
  32. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-capabilities/service-app.json +20 -0
  33. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-capabilities/service.wasm +0 -0
  34. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-composition/service-app.json +30 -0
  35. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-composition/service.wasm +0 -0
  36. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-provider/service-app.json +30 -0
  37. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-provider/service.wasm +0 -0
  38. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-resident/service-app.json +31 -0
  39. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-resident/service.wasm +0 -0
  40. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-state/service-app.json +49 -0
  41. package/resources/apps/nextclaw-portable-runtime-lab/service-components/nextclaw-portable-runtime-lab-state/service.wasm +0 -0
  42. package/ui-dist/assets/{api-DNLQPHfQ.js → api-m33O1Kb0.js} +3 -3
  43. package/ui-dist/assets/api-m33O1Kb0.js.gz +0 -0
  44. package/ui-dist/assets/{appearance-settings-page-BlAFMOGV.js → appearance-settings-page-x4Vjnfmz.js} +1 -1
  45. package/ui-dist/assets/appearance-settings-page-x4Vjnfmz.js.gz +0 -0
  46. package/ui-dist/assets/{book-open-BryfPTTa.js → book-open-BHiasMCg.js} +1 -1
  47. package/ui-dist/assets/{channels-list-page-Cfbgubyv.js → channels-list-page-DOxJZeXq.js} +1 -1
  48. package/ui-dist/assets/channels-list-page-DOxJZeXq.js.gz +0 -0
  49. package/ui-dist/assets/{config-split-page-Dd8xG0Jk.js → config-split-page-DBnMEP1o.js} +1 -1
  50. package/ui-dist/assets/config-split-page-DBnMEP1o.js.gz +0 -0
  51. package/ui-dist/assets/{confirm-dialog-CZIWjnJ6.js → confirm-dialog-f5NKLh5I.js} +2 -2
  52. package/ui-dist/assets/confirm-dialog-f5NKLh5I.js.gz +0 -0
  53. package/ui-dist/assets/{createLucideIcon-BxwVe8ZD.js → createLucideIcon-CHALGA42.js} +1 -1
  54. package/ui-dist/assets/{desktop-capabilities-page-C6YxsHeV.js → desktop-capabilities-page-DixayAe6.js} +1 -1
  55. package/ui-dist/assets/desktop-capabilities-page-DixayAe6.js.gz +0 -0
  56. package/ui-dist/assets/{desktop-update-config-BVN4QPJn.js → desktop-update-config-ChhfeOHj.js} +1 -1
  57. package/ui-dist/assets/desktop-update-config-ChhfeOHj.js.gz +0 -0
  58. package/ui-dist/assets/{dist-Cd8Nm3BW.js → dist-_UMsWSY0.js} +1 -1
  59. package/ui-dist/assets/{dist-Cd8Nm3BW.js.gz → dist-_UMsWSY0.js.gz} +0 -0
  60. package/ui-dist/assets/{dist-BRloKr3S.js → dist-sD6V4nHX.js} +1 -1
  61. package/ui-dist/assets/dist-sD6V4nHX.js.gz +0 -0
  62. package/ui-dist/assets/doc-browser-Bq7LJsQY.js +1 -0
  63. package/ui-dist/assets/doc-browser-Bq7LJsQY.js.gz +0 -0
  64. package/ui-dist/assets/doc-browser-BuDgpY2e.js +1 -0
  65. package/ui-dist/assets/{doc-browser-context-3JGufiMA.js → doc-browser-context-Capim3og.js} +1 -1
  66. package/ui-dist/assets/doc-browser-context-Capim3og.js.gz +0 -0
  67. package/ui-dist/assets/{extensions-Dedd2oDI.js → extensions-Bdclr3Wr.js} +1 -1
  68. package/ui-dist/assets/extensions-Bdclr3Wr.js.gz +0 -0
  69. package/ui-dist/assets/{form-actions-FWCEof3V.js → form-actions-CH0kWhEt.js} +1 -1
  70. package/ui-dist/assets/index-BjxnLYE3.js +109 -0
  71. package/ui-dist/assets/index-BjxnLYE3.js.gz +0 -0
  72. package/ui-dist/assets/index-CBmwJjwE.css +1 -0
  73. package/ui-dist/assets/index-CBmwJjwE.css.gz +0 -0
  74. package/ui-dist/assets/loader-circle-CH_0iPLy.js +1 -0
  75. package/ui-dist/assets/{mcp-marketplace-page-e9Rmfz-d.js → mcp-marketplace-page-BUsxLVB5.js} +2 -2
  76. package/ui-dist/assets/mcp-marketplace-page-BUsxLVB5.js.gz +0 -0
  77. package/ui-dist/assets/mcp-marketplace-page-GPRV3fB0.js +1 -0
  78. package/ui-dist/assets/{model-config-page-C173Ssil.js → model-config-page-CIevwqp4.js} +1 -1
  79. package/ui-dist/assets/model-config-page-CIevwqp4.js.gz +0 -0
  80. package/ui-dist/assets/{navigation-link-DTPOvGGL.js → navigation-link-CAUiM3zS.js} +1 -1
  81. package/ui-dist/assets/navigation-link-CAUiM3zS.js.gz +0 -0
  82. package/ui-dist/assets/plus-B3obD55V.js +1 -0
  83. package/ui-dist/assets/{privacy-settings-page-CRserBVF.js → privacy-settings-page-CNKfZsYH.js} +1 -1
  84. package/ui-dist/assets/privacy-settings-page-CNKfZsYH.js.gz +0 -0
  85. package/ui-dist/assets/{providers-config-page-C8jUtnZb.js → providers-config-page-DAah2NIS.js} +1 -1
  86. package/ui-dist/assets/providers-config-page-DAah2NIS.js.gz +0 -0
  87. package/ui-dist/assets/{react-BAzO6LZS.js → react-Co6JiC1n.js} +1 -1
  88. package/ui-dist/assets/react-Co6JiC1n.js.gz +0 -0
  89. package/ui-dist/assets/{refresh-cw-BCj-yjde.js → refresh-cw-BG_LX3SS.js} +1 -1
  90. package/ui-dist/assets/remote-DuscgfvR.js +1 -0
  91. package/ui-dist/assets/{rotate-cw-BPnW4qUl.js → rotate-cw-Cz7I_m7O.js} +1 -1
  92. package/ui-dist/assets/{runtime-config-page-aeQ1Dryz.js → runtime-config-page-Cl1RpmSC.js} +1 -1
  93. package/ui-dist/assets/runtime-config-page-Cl1RpmSC.js.gz +0 -0
  94. package/ui-dist/assets/{save-KT0MumfJ.js → save-BiHnb8ps.js} +1 -1
  95. package/ui-dist/assets/{search-Cv1Hx_nx.js → search-C47TyRg0.js} +1 -1
  96. package/ui-dist/assets/{search-config-page-b283V-fo.js → search-config-page-BAIzd6gN.js} +1 -1
  97. package/ui-dist/assets/search-config-page-BAIzd6gN.js.gz +0 -0
  98. package/ui-dist/assets/{secrets-config-page-ss7x3BOo.js → secrets-config-page-Ddi-n43R.js} +1 -1
  99. package/ui-dist/assets/secrets-config-page-Ddi-n43R.js.gz +0 -0
  100. package/ui-dist/assets/security-config-C8MHnE1r.js +1 -0
  101. package/ui-dist/assets/{select-BVgD4EpG.js → select-BzQKfW1e.js} +2 -2
  102. package/ui-dist/assets/select-BzQKfW1e.js.gz +0 -0
  103. package/ui-dist/assets/skeleton-BXFAE9SE.js +1 -0
  104. package/ui-dist/assets/{tag-chip-DZ4ircUB.js → tag-chip-C3K814HZ.js} +1 -1
  105. package/ui-dist/assets/x-C1r9IXet.js +1 -0
  106. package/ui-dist/index.html +29 -21
  107. package/ui-dist/index.html.gz +0 -0
  108. package/ui-dist/themes/island/island-atmosphere.webp +0 -0
  109. package/dist/nextclaw-cli-app-B_ffeXOD.js.map +0 -1
  110. package/dist/nextclaw-distribution.utils-BneKclXo.js.map +0 -1
  111. package/ui-dist/assets/api-DNLQPHfQ.js.gz +0 -0
  112. package/ui-dist/assets/appearance-settings-page-BlAFMOGV.js.gz +0 -0
  113. package/ui-dist/assets/channels-list-page-Cfbgubyv.js.gz +0 -0
  114. package/ui-dist/assets/config-split-page-Dd8xG0Jk.js.gz +0 -0
  115. package/ui-dist/assets/confirm-dialog-CZIWjnJ6.js.gz +0 -0
  116. package/ui-dist/assets/desktop-capabilities-page-C6YxsHeV.js.gz +0 -0
  117. package/ui-dist/assets/desktop-update-config-BVN4QPJn.js.gz +0 -0
  118. package/ui-dist/assets/dist-BRloKr3S.js.gz +0 -0
  119. package/ui-dist/assets/doc-browser-Bzgqmmiu.js +0 -1
  120. package/ui-dist/assets/doc-browser-Bzgqmmiu.js.gz +0 -0
  121. package/ui-dist/assets/doc-browser-D0qI0Z_R.js +0 -1
  122. package/ui-dist/assets/doc-browser-context-3JGufiMA.js.gz +0 -0
  123. package/ui-dist/assets/extensions-Dedd2oDI.js.gz +0 -0
  124. package/ui-dist/assets/index-CXkHcESm.css +0 -1
  125. package/ui-dist/assets/index-CXkHcESm.css.gz +0 -0
  126. package/ui-dist/assets/index-D25tRg1p.js +0 -109
  127. package/ui-dist/assets/index-D25tRg1p.js.gz +0 -0
  128. package/ui-dist/assets/loader-circle-BoyCoFps.js +0 -1
  129. package/ui-dist/assets/mcp-marketplace-page-Cn-9CDh3.js +0 -1
  130. package/ui-dist/assets/mcp-marketplace-page-e9Rmfz-d.js.gz +0 -0
  131. package/ui-dist/assets/model-config-page-C173Ssil.js.gz +0 -0
  132. package/ui-dist/assets/navigation-link-DTPOvGGL.js.gz +0 -0
  133. package/ui-dist/assets/plus-D1R4Vipq.js +0 -1
  134. package/ui-dist/assets/privacy-settings-page-CRserBVF.js.gz +0 -0
  135. package/ui-dist/assets/providers-config-page-C8jUtnZb.js.gz +0 -0
  136. package/ui-dist/assets/react-BAzO6LZS.js.gz +0 -0
  137. package/ui-dist/assets/remote-6BHWP5zi.js +0 -1
  138. package/ui-dist/assets/runtime-config-page-aeQ1Dryz.js.gz +0 -0
  139. package/ui-dist/assets/search-config-page-b283V-fo.js.gz +0 -0
  140. package/ui-dist/assets/secrets-config-page-ss7x3BOo.js.gz +0 -0
  141. package/ui-dist/assets/security-config-HHz62oxH.js +0 -1
  142. package/ui-dist/assets/select-BVgD4EpG.js.gz +0 -0
  143. package/ui-dist/assets/skeleton-ChUmcyys.js +0 -1
  144. 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
+ }