narraeon 0.1.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 (60) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +164 -0
  3. package/README.zh-CN.md +164 -0
  4. package/dist/node/cli/NarraeonCli.js +163 -0
  5. package/dist/node/cli/main.js +38 -0
  6. package/dist/node/protocol/appPreferences.js +5 -0
  7. package/dist/node/protocol/contentTree.js +14 -0
  8. package/dist/node/protocol/modelConnections.js +110 -0
  9. package/dist/node/protocol/playerViews.js +1 -0
  10. package/dist/node/protocol/v1.js +278 -0
  11. package/dist/node/runtime/V1Runtime.js +625 -0
  12. package/dist/node/runtime/artifact/FileNativeArtifactStore.js +1914 -0
  13. package/dist/node/runtime/config/AppPreferencesStore.js +92 -0
  14. package/dist/node/runtime/config/appPaths.js +19 -0
  15. package/dist/node/runtime/content/ContentLimits.js +1 -0
  16. package/dist/node/runtime/content/ContentTreeFile.js +1 -0
  17. package/dist/node/runtime/content/ContentWorkspace.js +601 -0
  18. package/dist/node/runtime/content/CurrentTreeContentLibrary.js +721 -0
  19. package/dist/node/runtime/content/FileNativeContentTree.js +512 -0
  20. package/dist/node/runtime/extension/FrontendExtensionBundle.js +229 -0
  21. package/dist/node/runtime/extension/PlayerViewPanelProjector.js +121 -0
  22. package/dist/node/runtime/model/AiFailureLog.js +332 -0
  23. package/dist/node/runtime/model/FileNativeModelAdapters.js +1246 -0
  24. package/dist/node/runtime/model/ModelConnectionStore.js +378 -0
  25. package/dist/node/runtime/model/ModelHost.js +89 -0
  26. package/dist/node/runtime/model/ModelHostStream.js +367 -0
  27. package/dist/node/runtime/model/ProviderExchangeState.js +1 -0
  28. package/dist/node/runtime/model/ProviderStream.js +90 -0
  29. package/dist/node/runtime/model/SettingAuthorStream.js +118 -0
  30. package/dist/node/runtime/play/FileNativeContinuityCorrection.js +304 -0
  31. package/dist/node/runtime/play/FileNativePlayPresetStore.js +1856 -0
  32. package/dist/node/runtime/play/PlayCallChain.js +1280 -0
  33. package/dist/node/runtime/play/PlayDocumentTools.js +1121 -0
  34. package/dist/node/runtime/play/PlayFollowupRequests.js +183 -0
  35. package/dist/node/runtime/play/PlayPresetWorkbench.js +185 -0
  36. package/dist/node/runtime/prompt/FileNativePromptCompiler.js +1425 -0
  37. package/dist/node/runtime/prompt/FileNativeToolRegistry.js +192 -0
  38. package/dist/node/runtime/prompt/MaterialSelection.js +95 -0
  39. package/dist/node/runtime/setting/DocumentCandidateSettingImprovement.js +1726 -0
  40. package/dist/node/runtime/world/FileNativeWorldStore.js +2028 -0
  41. package/dist/node/runtime/world/PlayerViewRenderer.js +322 -0
  42. package/dist/node/runtime/world/WorldDocumentStore.js +2808 -0
  43. package/dist/node/runtime/world/WorldOperationCoordinator.js +490 -0
  44. package/dist/node/runtime/world/WorldStateRevision.js +75 -0
  45. package/dist/node/server/createRuntime.js +6 -0
  46. package/dist/node/server/createServer.js +218 -0
  47. package/dist/node/server/main.js +25 -0
  48. package/dist/node/server/startWebServer.js +28 -0
  49. package/dist/node/shared/default-play-prompts.js +85 -0
  50. package/dist/node/shared/default-preset-host.js +538 -0
  51. package/dist/node/shared/default-setting-improvement-prompt.js +77 -0
  52. package/dist/node/shared/first-party-action-choices.js +164 -0
  53. package/dist/node/shared/first-party-generic-panels.js +105 -0
  54. package/dist/node/shared/first-party-play-preset-templates.js +41 -0
  55. package/dist/node/shared/first-party-player-view.js +179 -0
  56. package/dist/node/shared/play-preset-prompt-roles.js +4 -0
  57. package/dist/web/assets/index-CRGTZ9Ak.css +1 -0
  58. package/dist/web/assets/index-DDx1itzT.js +1192 -0
  59. package/dist/web/index.html +14 -0
  60. package/package.json +86 -0
@@ -0,0 +1,164 @@
1
+ import { defaultNarrationPromptForLocale } from "./default-play-prompts.js";
2
+ import { defaultPresetHostFilesForLocale } from "./default-preset-host.js";
3
+ import { defaultSettingImprovementPromptForLocale, defaultSettingImprovementPromptPath, } from "./default-setting-improvement-prompt.js";
4
+ /**
5
+ * An ordinary, portable play-preset asset. Runtime treats this exactly like
6
+ * any author-supplied file map. The export is only a convenient workbench
7
+ * starting point and may be copied, edited, exported, or deleted.
8
+ */
9
+ export function firstPartyActionChoicesPresetFilesForLocale(locale) {
10
+ const displayName = locale === "zh-CN" ? "下一步建议" : "Next-step ideas";
11
+ return {
12
+ "preset.yaml": `format: narraeon.play-preset/v1
13
+ name: action-choices-recommended
14
+ callChain: call-chain.yaml
15
+ settingImprovement:
16
+ markdown: prompts/setting-improvement.md
17
+ mounts:
18
+ player.options: composer_below
19
+ extensions:
20
+ - renderers/player-options.html
21
+ - scripts/player-options.js
22
+ - assets/player-options.css
23
+ `,
24
+ "call-chain.yaml": `format: narraeon.play-call-chain/v1
25
+ narrative:
26
+ - markdown: prompts/narrate.md
27
+ followups:
28
+ - id: player_options
29
+ displayName: ${displayName}
30
+ prompt:
31
+ role: author_instruction
32
+ markdown: prompts/options.md
33
+ maxArtifactBytes: 32768
34
+ artifacts:
35
+ - name: player_options
36
+ channel: player.options
37
+ strategy: replace
38
+ contentType: application/json
39
+ renderer: renderers/player-options.html
40
+ rendererRevision: v1
41
+ rendererMode: app
42
+ scripts:
43
+ - scripts/player-options.js
44
+ assets:
45
+ - assets/player-options.css
46
+ save: commit
47
+ invalidation: new_operation
48
+ required: true
49
+ maxEmits: 1
50
+ payloadContract:
51
+ type: array
52
+ minItems: 4
53
+ maxItems: 4
54
+ uniqueBy: id
55
+ items:
56
+ type: object
57
+ additionalProperties: false
58
+ required:
59
+ - id
60
+ - label
61
+ - prompt
62
+ properties:
63
+ id:
64
+ type: string
65
+ minLength: 1
66
+ maxLength: 64
67
+ label:
68
+ type: string
69
+ minLength: 1
70
+ maxLength: 80
71
+ description:
72
+ type: string
73
+ maxLength: 240
74
+ prompt:
75
+ type: string
76
+ minLength: 1
77
+ maxLength: 1000
78
+ `,
79
+ ...defaultPresetHostFilesForLocale(locale),
80
+ "prompts/narrate.md": defaultNarrationPromptForLocale(locale),
81
+ [defaultSettingImprovementPromptPath]: defaultSettingImprovementPromptForLocale(locale),
82
+ "prompts/options.md": locale === "zh-CN"
83
+ ? `# 提交后下一步建议
84
+
85
+ 根据玩家刚刚看到的决策点,给出四种短而具体、彼此有实质差异的下一步建议。差异应体现在目标、方法、风险或态度上,而不是只替换近义词。
86
+
87
+ 建议只能使用玩家已经能够知道的信息,并且必须是玩家角色此刻有条件尝试的行动。每项建议只是可以填入输入框的草稿:表达玩家准备做什么,不宣称行动已经发生,不预先写定结果,也不替玩家选择或发送。字段、数量和提交方式服从当前后置请求的 Runtime 产物说明。
88
+ `
89
+ : `# Next-step ideas after the commit
90
+
91
+ At the decision point the player has just seen, provide four short, concrete next-step ideas that differ meaningfully from one another. Vary the goal, method, risk, or attitude rather than swapping synonyms.
92
+
93
+ Use only information the player can already know, and suggest only actions the player character could attempt now. Each idea is a draft that may be placed in the input box: it expresses what the player is preparing to do, never claims the action has happened, predetermines an outcome, chooses for the player, or sends the input. Follow the current follow-up request's Runtime artifact contract for fields, count, and submission.
94
+ `,
95
+ "renderers/player-options.html": `<!doctype html>
96
+ <html><head><meta charset="utf-8"><title>${displayName}</title></head><body>
97
+ <main id="player-options-root" aria-label="${displayName}"></main>
98
+ </body></html>
99
+ `,
100
+ "scripts/player-options.js": `(function () {
101
+ var root = document.getElementById("player-options-root");
102
+ var pending = new Map();
103
+ var styleSource = window.__NARRAEON_ASSETS__ && window.__NARRAEON_ASSETS__["assets/player-options.css"];
104
+ if (typeof styleSource === "string") {
105
+ var style = document.createElement("style");
106
+ style.textContent = styleSource;
107
+ document.head.append(style);
108
+ }
109
+ function post(command, payload) {
110
+ var requestId = "draft-" + Math.random().toString(36).slice(2);
111
+ parent.postMessage({
112
+ namespace: "narraeon.extension.v1",
113
+ command: command,
114
+ instanceId: document.documentElement.dataset.narraeonInstance,
115
+ nonce: document.documentElement.dataset.narraeonNonce,
116
+ requestId: requestId,
117
+ payload: payload
118
+ }, "*");
119
+ pending.set(requestId, true);
120
+ }
121
+ function draw(raw, disabled) {
122
+ root.replaceChildren();
123
+ var options;
124
+ try { options = JSON.parse(raw); } catch (_) { return; }
125
+ if (!Array.isArray(options)) return;
126
+ options.forEach(function (option) {
127
+ var button = document.createElement("button");
128
+ button.type = "button";
129
+ button.className = "player-option";
130
+ button.dataset.optionId = option.id;
131
+ button.disabled = Boolean(disabled);
132
+ var label = document.createElement("strong");
133
+ label.textContent = option.label;
134
+ button.append(label);
135
+ if (option.description) {
136
+ var detail = document.createElement("small");
137
+ detail.textContent = option.description;
138
+ button.append(detail);
139
+ }
140
+ button.addEventListener("click", function () {
141
+ post("composer.set_draft", { text: option.prompt });
142
+ });
143
+ root.append(button);
144
+ });
145
+ }
146
+ window.addEventListener("message", function (event) {
147
+ if (!event.data || event.data.namespace !== "narraeon.extension.v1") return;
148
+ if (event.data.type === "render.update") {
149
+ draw(event.data.payload && event.data.payload.content, event.data.payload && event.data.payload.interactionDisabled);
150
+ return;
151
+ }
152
+ if (event.data.type === "bridge.response") pending.delete(event.data.requestId);
153
+ });
154
+ }());
155
+ `,
156
+ "assets/player-options.css": `body { margin: 0; font: 14px/1.45 system-ui, sans-serif; color: #202020; }
157
+ #player-options-root { display: grid; gap: .5rem; }
158
+ .player-option { display: grid; gap: .15rem; padding: .65rem .75rem; border: 1px solid #c9c0b1; border-radius: .65rem; background: #fffaf1; color: inherit; text-align: left; cursor: pointer; }
159
+ .player-option:hover { border-color: #a66a42; }
160
+ .player-option small { color: #6c6259; }
161
+ `,
162
+ };
163
+ }
164
+ export const firstPartyActionChoicesPresetFiles = firstPartyActionChoicesPresetFilesForLocale("en");
@@ -0,0 +1,105 @@
1
+ import { defaultNarrationPromptForLocale } from "./default-play-prompts.js";
2
+ import { defaultPresetHostFilesForLocale } from "./default-preset-host.js";
3
+ import { defaultSettingImprovementPromptForLocale, defaultSettingImprovementPromptPath, } from "./default-setting-improvement-prompt.js";
4
+ /**
5
+ * Ordinary copyable examples for the generic artifact seam. Runtime does not
6
+ * privilege these output names or channels; authors may replace every file.
7
+ */
8
+ export function firstPartyGenericPanelsPresetFilesForLocale(locale) {
9
+ const displayName = locale === "zh-CN" ? "发布通用面板" : "Publish generic panels";
10
+ const panelTitle = locale === "zh-CN" ? "通用 HTML 面板" : "Generic HTML panel";
11
+ return {
12
+ "preset.yaml": `format: narraeon.play-preset/v1
13
+ name: generic-panels-recommended
14
+ callChain: call-chain.yaml
15
+ settingImprovement:
16
+ markdown: prompts/setting-improvement.md
17
+ mounts:
18
+ generic.markdown: story
19
+ generic.html: sidebar
20
+ generic.debug: debug
21
+ extensions:
22
+ - renderers/generic-html.html
23
+ - scripts/generic-html.js
24
+ - assets/generic-html.css
25
+ `,
26
+ "call-chain.yaml": `format: narraeon.play-call-chain/v1
27
+ narrative:
28
+ - markdown: prompts/narrate.md
29
+ followups:
30
+ - id: publish_panels
31
+ displayName: ${displayName}
32
+ prompt:
33
+ role: author_instruction
34
+ markdown: prompts/panels.md
35
+ maxArtifactBytes: 65536
36
+ artifacts:
37
+ - name: markdown_panel
38
+ channel: generic.markdown
39
+ strategy: replace
40
+ contentType: text/markdown
41
+ save: commit
42
+ invalidation: new_operation
43
+ required: false
44
+ maxEmits: 1
45
+ - name: html_panel
46
+ channel: generic.html
47
+ strategy: replace
48
+ contentType: text/html
49
+ renderer: renderers/generic-html.html
50
+ rendererRevision: v1
51
+ rendererMode: app
52
+ scripts:
53
+ - scripts/generic-html.js
54
+ assets:
55
+ - assets/generic-html.css
56
+ save: commit
57
+ invalidation: new_operation
58
+ required: false
59
+ maxEmits: 1
60
+ - name: debug_panel
61
+ channel: generic.debug
62
+ strategy: append
63
+ contentType: text/markdown
64
+ save: operation
65
+ invalidation: operation_end
66
+ required: false
67
+ maxEmits: 8
68
+ `,
69
+ ...defaultPresetHostFilesForLocale(locale),
70
+ "prompts/narrate.md": defaultNarrationPromptForLocale(locale),
71
+ [defaultSettingImprovementPromptPath]: defaultSettingImprovementPromptForLocale(locale),
72
+ "prompts/panels.md": locale === "zh-CN"
73
+ ? `# 通用面板
74
+
75
+ 核心叙事已经提交。只有面板能为当前局面提供额外价值时才生成它;没有合适内容时可以不生成。
76
+
77
+ 面板只能整理已经成立且适合玩家看见的信息,不得发明世界事实、替玩家作决定、偷偷保存状态,或把未向玩家揭示的信息当作提示。它是非权威的界面补充,不应复制或改写由玩家视图负责的固定状态展示。各输出的格式、保存方式和提交方法服从当前后置请求的 Runtime 产物说明。
78
+ `
79
+ : `# Generic panels
80
+
81
+ The core narrative has already been committed. Generate a panel only when it adds value to the current situation; emit nothing when there is no suitable content.
82
+
83
+ A panel may organize only established information that is appropriate for the player to see. It must not invent world facts, decide for the player, save state covertly, or turn unrevealed information into hints. It is a non-authoritative interface supplement and must not duplicate or rewrite fixed state display owned by player views. Follow the current follow-up request's Runtime artifact contract for each output's format, retention, and submission.
84
+ `,
85
+ "renderers/generic-html.html": `<!doctype html>
86
+ <html><head><meta charset="utf-8"><title>${panelTitle}</title></head><body>
87
+ <main id="generic-html-root" aria-label="${panelTitle}"></main>
88
+ </body></html>
89
+ `,
90
+ "scripts/generic-html.js": `(function () {
91
+ var root = document.getElementById("generic-html-root");
92
+ window.addEventListener("message", function (event) {
93
+ if (!event.data || event.data.namespace !== "narraeon.extension.v1") return;
94
+ if (event.data.type !== "render.update") return;
95
+ var payload = event.data.payload || {};
96
+ root.innerHTML = typeof payload.content === "string" ? payload.content : "";
97
+ });
98
+ }());
99
+ `,
100
+ "assets/generic-html.css": `body { margin: 0; font: 14px/1.45 system-ui, sans-serif; color: #202020; }
101
+ #generic-html-root { padding: .75rem; }
102
+ `,
103
+ };
104
+ }
105
+ export const firstPartyGenericPanelsPresetFiles = firstPartyGenericPanelsPresetFilesForLocale("en");
@@ -0,0 +1,41 @@
1
+ import { firstPartyActionChoicesPresetFilesForLocale } from "./first-party-action-choices.js";
2
+ import { firstPartyGenericPanelsPresetFilesForLocale } from "./first-party-generic-panels.js";
3
+ import { firstPartyStatusPanelPresetFilesForLocale } from "./first-party-player-view.js";
4
+ /** Optional workbench registry; each entry is ordinary portable file content. */
5
+ export function firstPartyPlayPresetTemplatesForLocale(locale) {
6
+ return [
7
+ {
8
+ id: "action-choices",
9
+ label: locale === "zh-CN" ? "行动选项" : "Action choices",
10
+ name: locale === "zh-CN"
11
+ ? "下一步建议(可编辑副本)"
12
+ : "Next-step ideas (editable copy)",
13
+ files: firstPartyActionChoicesPresetFilesForLocale(locale),
14
+ },
15
+ {
16
+ id: "status-panel",
17
+ label: locale === "zh-CN" ? "状态栏" : "Status panel",
18
+ name: locale === "zh-CN"
19
+ ? "状态栏(可编辑副本)"
20
+ : "Status panel (editable copy)",
21
+ files: firstPartyStatusPanelPresetFilesForLocale(locale),
22
+ },
23
+ {
24
+ id: "generic-panels",
25
+ label: locale === "zh-CN" ? "Markdown / HTML 面板" : "Markdown / HTML panels",
26
+ name: locale === "zh-CN"
27
+ ? "通用面板(可编辑副本)"
28
+ : "Generic panels (editable copy)",
29
+ files: firstPartyGenericPanelsPresetFilesForLocale(locale),
30
+ },
31
+ {
32
+ id: "artifact-debugger",
33
+ label: locale === "zh-CN" ? "产物调试器" : "Artifact debugger",
34
+ name: locale === "zh-CN"
35
+ ? "产物调试器(可编辑副本)"
36
+ : "Artifact debugger (editable copy)",
37
+ files: firstPartyGenericPanelsPresetFilesForLocale(locale),
38
+ },
39
+ ];
40
+ }
41
+ export const firstPartyPlayPresetTemplates = firstPartyPlayPresetTemplatesForLocale("en");
@@ -0,0 +1,179 @@
1
+ import { defaultNarrationPromptForLocale } from "./default-play-prompts.js";
2
+ import { defaultPresetHostFilesForLocale } from "./default-preset-host.js";
3
+ import { defaultSettingImprovementPromptForLocale, defaultSettingImprovementPromptPath, } from "./default-setting-improvement-prompt.js";
4
+ /**
5
+ * Ordinary, copyable player-view panel assets. Runtime understands only the
6
+ * generic playerViewPanels contract; this is not a privileged status system.
7
+ */
8
+ export function firstPartyStatusPanelPresetFilesForLocale(locale) {
9
+ const currentStatus = locale === "zh-CN" ? "当前状态" : "Current status";
10
+ const playerView = locale === "zh-CN" ? "玩家视图" : "Player view";
11
+ const emptyMessage = locale === "zh-CN" ? "当前没有可显示内容。" : "Nothing to display yet.";
12
+ const emptyPrefix = locale === "zh-CN" ? "暂无项目:" : "No items: ";
13
+ const diagnosticsSuffix = locale === "zh-CN" ? " 项视图诊断" : " player-view diagnostics";
14
+ return {
15
+ "preset.yaml": `format: narraeon.play-preset/v1
16
+ name: status-panel-recommended
17
+ callChain: call-chain.yaml
18
+ settingImprovement:
19
+ markdown: prompts/setting-improvement.md
20
+ mounts: []
21
+ playerViewPanels:
22
+ - id: current_view
23
+ source:
24
+ kind: player_view
25
+ view: status
26
+ channel: player.view.current
27
+ key: current
28
+ mount: sidebar
29
+ renderer: renderers/player-view-status.html
30
+ rendererRevision: v1
31
+ rendererMode: app
32
+ scripts:
33
+ - scripts/player-view-status.js
34
+ assets:
35
+ - assets/player-view-status.css
36
+ config:
37
+ title: ${currentStatus}
38
+ layout: stack
39
+ theme: parchment
40
+ empty: message
41
+ emptyMessage: ${emptyMessage}
42
+ groups: []
43
+ extensions:
44
+ - renderers/player-view-status.html
45
+ - scripts/player-view-status.js
46
+ - assets/player-view-status.css
47
+ `,
48
+ "call-chain.yaml": `format: narraeon.play-call-chain/v1
49
+ narrative:
50
+ - markdown: prompts/narrate.md
51
+ followups: []
52
+ `,
53
+ ...defaultPresetHostFilesForLocale(locale),
54
+ "prompts/narrate.md": defaultNarrationPromptForLocale(locale),
55
+ [defaultSettingImprovementPromptPath]: defaultSettingImprovementPromptForLocale(locale),
56
+ "renderers/player-view-status.html": `<!doctype html>
57
+ <html><head><meta charset="utf-8"><title>${currentStatus}</title></head><body>
58
+ <main id="player-view-panel-root" aria-label="${currentStatus}"></main>
59
+ </body></html>
60
+ `,
61
+ "scripts/player-view-status.js": `(function () {
62
+ var root = document.getElementById("player-view-panel-root");
63
+ var styleSource = window.__NARRAEON_ASSETS__ && window.__NARRAEON_ASSETS__["assets/player-view-status.css"];
64
+ if (typeof styleSource === "string") {
65
+ var style = document.createElement("style");
66
+ style.textContent = styleSource;
67
+ document.head.append(style);
68
+ }
69
+ function valueNode(value) {
70
+ if (value === null || value === undefined) return document.createTextNode("—");
71
+ if (typeof value === "object") {
72
+ var wrapper = document.createElement(Array.isArray(value) ? "ul" : "dl");
73
+ Object.keys(value).forEach(function (key, index) {
74
+ var entry = document.createElement(Array.isArray(value) ? "li" : "div");
75
+ if (!Array.isArray(value)) {
76
+ var term = document.createElement("dt");
77
+ term.textContent = key;
78
+ entry.append(term);
79
+ }
80
+ entry.append(valueNode(Array.isArray(value) ? value[index] : value[key]));
81
+ wrapper.append(entry);
82
+ });
83
+ return wrapper;
84
+ }
85
+ return document.createTextNode(String(value));
86
+ }
87
+ function draw(raw) {
88
+ root.replaceChildren();
89
+ var payload;
90
+ try { payload = JSON.parse(raw || "{}"); } catch (_) { return; }
91
+ var config = payload.config || {};
92
+ root.dataset.theme = config.theme || "default";
93
+ root.dataset.layout = config.layout || "stack";
94
+ root.dataset.emptyState = config.empty || "message";
95
+ var heading = document.createElement("h3");
96
+ heading.textContent = payload.title || payload.viewId || ${JSON.stringify(playerView)};
97
+ root.append(heading);
98
+ var items = Array.isArray(payload.items) ? payload.items : [];
99
+ if (items.length === 0) {
100
+ if (config.empty === "message" || config.empty === "show") {
101
+ var empty = document.createElement("p");
102
+ empty.className = config.empty === "show"
103
+ ? "player-view-empty player-view-empty-value"
104
+ : "player-view-empty player-view-empty-message";
105
+ empty.textContent = config.empty === "show"
106
+ ? ${JSON.stringify(emptyPrefix)} + (config.emptyMessage || ${JSON.stringify(emptyMessage)})
107
+ : (config.emptyMessage || ${JSON.stringify(emptyMessage)});
108
+ root.append(empty);
109
+ }
110
+ } else {
111
+ var byId = new Map(items.map(function (item) { return [item.id, item]; }));
112
+ var groups = Array.isArray(config.groups) ? config.groups : [];
113
+ var renderItems = function (list, selected) {
114
+ selected.forEach(function (item) {
115
+ var row = document.createElement("div");
116
+ var label = document.createElement("dt");
117
+ label.textContent = item.label || item.id;
118
+ var value = document.createElement("dd");
119
+ value.append(valueNode(item.value));
120
+ row.append(label, value);
121
+ list.append(row);
122
+ });
123
+ };
124
+ if (groups.length === 0) {
125
+ var list = document.createElement("dl");
126
+ renderItems(list, items);
127
+ root.append(list);
128
+ } else {
129
+ groups.forEach(function (group) {
130
+ var selected = (Array.isArray(group.itemIds) ? group.itemIds : [])
131
+ .map(function (id) { return byId.get(id); })
132
+ .filter(Boolean);
133
+ if (selected.length === 0) return;
134
+ var section = document.createElement("section");
135
+ var groupHeading = document.createElement("h4");
136
+ groupHeading.textContent = group.label || group.id;
137
+ var groupList = document.createElement("dl");
138
+ renderItems(groupList, selected);
139
+ section.append(groupHeading, groupList);
140
+ root.append(section);
141
+ });
142
+ }
143
+ }
144
+ var diagnostics = Array.isArray(payload.diagnostics) ? payload.diagnostics : [];
145
+ if (diagnostics.length > 0) {
146
+ var details = document.createElement("details");
147
+ var summary = document.createElement("summary");
148
+ summary.textContent = diagnostics.length + ${JSON.stringify(diagnosticsSuffix)};
149
+ details.append(summary);
150
+ var diagnosticList = document.createElement("ul");
151
+ diagnostics.forEach(function (diagnostic) {
152
+ var item = document.createElement("li");
153
+ item.textContent = diagnostic.message;
154
+ diagnosticList.append(item);
155
+ });
156
+ details.append(diagnosticList);
157
+ root.append(details);
158
+ }
159
+ }
160
+ window.addEventListener("message", function (event) {
161
+ if (!event.data || event.data.namespace !== "narraeon.extension.v1") return;
162
+ if (event.data.type === "render.update") draw(event.data.payload && event.data.payload.content);
163
+ });
164
+ }());
165
+ `,
166
+ "assets/player-view-status.css": `body { margin: 0; font: 14px/1.45 system-ui, sans-serif; color: #202020; }
167
+ #player-view-panel-root { display: grid; gap: .5rem; }
168
+ #player-view-panel-root[data-theme="parchment"] { color: #4a382b; background: #fffaf1; padding: .75rem; border-radius: .65rem; }
169
+ #player-view-panel-root dl { display: grid; gap: .45rem; margin: 0; }
170
+ #player-view-panel-root[data-layout="grid"] dl { grid-template-columns: repeat(2, minmax(0, 1fr)); }
171
+ #player-view-panel-root dl > div { display: grid; gap: .15rem; }
172
+ #player-view-panel-root dt { font-weight: 650; }
173
+ #player-view-panel-root dd { margin: 0; }
174
+ #player-view-panel-root ul, #player-view-panel-root dl dl { margin: .25rem 0 0 1rem; padding-left: 1rem; }
175
+ .player-view-empty { color: #6c6259; }
176
+ `,
177
+ };
178
+ }
179
+ export const firstPartyStatusPanelPresetFiles = firstPartyStatusPanelPresetFilesForLocale("en");
@@ -0,0 +1,4 @@
1
+ export const playPresetPromptRoles = ["author_instruction"];
2
+ export function isPlayPresetPromptRole(value) {
3
+ return playPresetPromptRoles.some((role) => role === value);
4
+ }