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.
- package/LICENSE +201 -0
- package/README.md +164 -0
- package/README.zh-CN.md +164 -0
- package/dist/node/cli/NarraeonCli.js +163 -0
- package/dist/node/cli/main.js +38 -0
- package/dist/node/protocol/appPreferences.js +5 -0
- package/dist/node/protocol/contentTree.js +14 -0
- package/dist/node/protocol/modelConnections.js +110 -0
- package/dist/node/protocol/playerViews.js +1 -0
- package/dist/node/protocol/v1.js +278 -0
- package/dist/node/runtime/V1Runtime.js +625 -0
- package/dist/node/runtime/artifact/FileNativeArtifactStore.js +1914 -0
- package/dist/node/runtime/config/AppPreferencesStore.js +92 -0
- package/dist/node/runtime/config/appPaths.js +19 -0
- package/dist/node/runtime/content/ContentLimits.js +1 -0
- package/dist/node/runtime/content/ContentTreeFile.js +1 -0
- package/dist/node/runtime/content/ContentWorkspace.js +601 -0
- package/dist/node/runtime/content/CurrentTreeContentLibrary.js +721 -0
- package/dist/node/runtime/content/FileNativeContentTree.js +512 -0
- package/dist/node/runtime/extension/FrontendExtensionBundle.js +229 -0
- package/dist/node/runtime/extension/PlayerViewPanelProjector.js +121 -0
- package/dist/node/runtime/model/AiFailureLog.js +332 -0
- package/dist/node/runtime/model/FileNativeModelAdapters.js +1246 -0
- package/dist/node/runtime/model/ModelConnectionStore.js +378 -0
- package/dist/node/runtime/model/ModelHost.js +89 -0
- package/dist/node/runtime/model/ModelHostStream.js +367 -0
- package/dist/node/runtime/model/ProviderExchangeState.js +1 -0
- package/dist/node/runtime/model/ProviderStream.js +90 -0
- package/dist/node/runtime/model/SettingAuthorStream.js +118 -0
- package/dist/node/runtime/play/FileNativeContinuityCorrection.js +304 -0
- package/dist/node/runtime/play/FileNativePlayPresetStore.js +1856 -0
- package/dist/node/runtime/play/PlayCallChain.js +1280 -0
- package/dist/node/runtime/play/PlayDocumentTools.js +1121 -0
- package/dist/node/runtime/play/PlayFollowupRequests.js +183 -0
- package/dist/node/runtime/play/PlayPresetWorkbench.js +185 -0
- package/dist/node/runtime/prompt/FileNativePromptCompiler.js +1425 -0
- package/dist/node/runtime/prompt/FileNativeToolRegistry.js +192 -0
- package/dist/node/runtime/prompt/MaterialSelection.js +95 -0
- package/dist/node/runtime/setting/DocumentCandidateSettingImprovement.js +1726 -0
- package/dist/node/runtime/world/FileNativeWorldStore.js +2028 -0
- package/dist/node/runtime/world/PlayerViewRenderer.js +322 -0
- package/dist/node/runtime/world/WorldDocumentStore.js +2808 -0
- package/dist/node/runtime/world/WorldOperationCoordinator.js +490 -0
- package/dist/node/runtime/world/WorldStateRevision.js +75 -0
- package/dist/node/server/createRuntime.js +6 -0
- package/dist/node/server/createServer.js +218 -0
- package/dist/node/server/main.js +25 -0
- package/dist/node/server/startWebServer.js +28 -0
- package/dist/node/shared/default-play-prompts.js +85 -0
- package/dist/node/shared/default-preset-host.js +538 -0
- package/dist/node/shared/default-setting-improvement-prompt.js +77 -0
- package/dist/node/shared/first-party-action-choices.js +164 -0
- package/dist/node/shared/first-party-generic-panels.js +105 -0
- package/dist/node/shared/first-party-play-preset-templates.js +41 -0
- package/dist/node/shared/first-party-player-view.js +179 -0
- package/dist/node/shared/play-preset-prompt-roles.js +4 -0
- package/dist/web/assets/index-CRGTZ9Ak.css +1 -0
- package/dist/web/assets/index-DDx1itzT.js +1192 -0
- package/dist/web/index.html +14 -0
- 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");
|