prts-terrarchive 0.2.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/GAME_ASSETS.md +26 -0
- package/LICENSE +21 -0
- package/README.en.md +274 -0
- package/README.md +423 -0
- package/THIRD_PARTY_NOTICES.md +61 -0
- package/bin/bake-rhine-details.mjs +34 -0
- package/bin/install.js +273 -0
- package/bin/pack-map-assets.mjs +110 -0
- package/bin/preview-rhine.mjs +71 -0
- package/bin/rhine-detail-baker.ts +84 -0
- package/contracts/cloud_tools_v1.schema.json +243 -0
- package/contracts/corpus_i18n_v1.schema.json +179 -0
- package/contracts/corpus_tools_v1.schema.json +1801 -0
- package/cordis.patch.yml +32 -0
- package/docs/endfield-official-i18n.md +103 -0
- package/lib/client.js +3912 -0
- package/lib/endfield-map/map.js.br +0 -0
- package/lib/endfield-map/map.js.gz +0 -0
- package/lib/endfield-map/resources/map-00b0d0744a1b4404.json.br +0 -0
- package/lib/endfield-map/resources/map-00b0d0744a1b4404.json.gz +0 -0
- package/lib/endfield-map/resources/map-01616f534aaaeca1.png +0 -0
- package/lib/endfield-map/resources/map-1562e0dff4be3d23.png +0 -0
- package/lib/endfield-map/resources/map-160fe040f080f6a3.png +0 -0
- package/lib/endfield-map/resources/map-17b9d8be25dc2b39.json.br +0 -0
- package/lib/endfield-map/resources/map-17b9d8be25dc2b39.json.gz +0 -0
- package/lib/endfield-map/resources/map-1c4245c4e8b80c43.json.br +0 -0
- package/lib/endfield-map/resources/map-1c4245c4e8b80c43.json.gz +0 -0
- package/lib/endfield-map/resources/map-1d30baaaef3e29cc.png +0 -0
- package/lib/endfield-map/resources/map-21a61386af79cd12.json.br +0 -0
- package/lib/endfield-map/resources/map-21a61386af79cd12.json.gz +0 -0
- package/lib/endfield-map/resources/map-24b920b8dab49b44.json.br +0 -0
- package/lib/endfield-map/resources/map-24b920b8dab49b44.json.gz +0 -0
- package/lib/endfield-map/resources/map-26caf8d71247abb7.png +0 -0
- package/lib/endfield-map/resources/map-2729942c89545e73.json.br +0 -0
- package/lib/endfield-map/resources/map-2729942c89545e73.json.gz +0 -0
- package/lib/endfield-map/resources/map-29063a9802f67014.json.br +0 -0
- package/lib/endfield-map/resources/map-29063a9802f67014.json.gz +0 -0
- package/lib/endfield-map/resources/map-2f35d09a3ab60a63.json.br +0 -0
- package/lib/endfield-map/resources/map-2f35d09a3ab60a63.json.gz +0 -0
- package/lib/endfield-map/resources/map-2f98a873da81425e.json.br +0 -0
- package/lib/endfield-map/resources/map-2f98a873da81425e.json.gz +0 -0
- package/lib/endfield-map/resources/map-3ab978ae44190f80.json.br +0 -0
- package/lib/endfield-map/resources/map-3ab978ae44190f80.json.gz +0 -0
- package/lib/endfield-map/resources/map-3b62c80d14208902.json.br +0 -0
- package/lib/endfield-map/resources/map-3b62c80d14208902.json.gz +0 -0
- package/lib/endfield-map/resources/map-3d486e887ae121ac.png +0 -0
- package/lib/endfield-map/resources/map-418dc4865608fce5.png +0 -0
- package/lib/endfield-map/resources/map-4366b84607335f76.json.br +0 -0
- package/lib/endfield-map/resources/map-4366b84607335f76.json.gz +0 -0
- package/lib/endfield-map/resources/map-4371259d3ab7aac7.png +0 -0
- package/lib/endfield-map/resources/map-48dcc0fd55e13f7f.png +0 -0
- package/lib/endfield-map/resources/map-4aabda0e41197570.json.br +0 -0
- package/lib/endfield-map/resources/map-4aabda0e41197570.json.gz +0 -0
- package/lib/endfield-map/resources/map-505f5687b287043c.json.br +0 -0
- package/lib/endfield-map/resources/map-505f5687b287043c.json.gz +0 -0
- package/lib/endfield-map/resources/map-5177e9d1d46f47e2.png +0 -0
- package/lib/endfield-map/resources/map-51baeb59b7f8c28d.png +0 -0
- package/lib/endfield-map/resources/map-549561b46e621874.png +0 -0
- package/lib/endfield-map/resources/map-56c6a0bef799c8dc.json.br +0 -0
- package/lib/endfield-map/resources/map-56c6a0bef799c8dc.json.gz +0 -0
- package/lib/endfield-map/resources/map-5a6963909423b674.png +0 -0
- package/lib/endfield-map/resources/map-5a9c533c0552d7b7.png +0 -0
- package/lib/endfield-map/resources/map-5d20ae9ffa9a02b0.png +0 -0
- package/lib/endfield-map/resources/map-626c8847393f60bf.png +0 -0
- package/lib/endfield-map/resources/map-63d43cbec3bed3b8.png +0 -0
- package/lib/endfield-map/resources/map-661b6ec56e722574.png +0 -0
- package/lib/endfield-map/resources/map-6ddd384eca190828.png +0 -0
- package/lib/endfield-map/resources/map-6fc1aa9144f8156c.json.br +0 -0
- package/lib/endfield-map/resources/map-6fc1aa9144f8156c.json.gz +0 -0
- package/lib/endfield-map/resources/map-738ebaa002c33cfc.png +0 -0
- package/lib/endfield-map/resources/map-758c13be6085ec07.json.br +0 -0
- package/lib/endfield-map/resources/map-758c13be6085ec07.json.gz +0 -0
- package/lib/endfield-map/resources/map-78929f26d6844509.png +0 -0
- package/lib/endfield-map/resources/map-7f09c142950a0f74.png +0 -0
- package/lib/endfield-map/resources/map-8385e11f39e1a51c.json.br +0 -0
- package/lib/endfield-map/resources/map-8385e11f39e1a51c.json.gz +0 -0
- package/lib/endfield-map/resources/map-881343926a568f27.json.br +0 -0
- package/lib/endfield-map/resources/map-881343926a568f27.json.gz +0 -0
- package/lib/endfield-map/resources/map-8d1ade3f01d952b0.png +0 -0
- package/lib/endfield-map/resources/map-8fef3495bb7ed60f.png +0 -0
- package/lib/endfield-map/resources/map-907a7ecf8a0ce352.png +0 -0
- package/lib/endfield-map/resources/map-9113d5951c4db903.json.br +0 -0
- package/lib/endfield-map/resources/map-9113d5951c4db903.json.gz +0 -0
- package/lib/endfield-map/resources/map-94ad6634436224e3.json.br +0 -0
- package/lib/endfield-map/resources/map-94ad6634436224e3.json.gz +0 -0
- package/lib/endfield-map/resources/map-99722e61da5f3e53.png +0 -0
- package/lib/endfield-map/resources/map-9be9596cc059abe3.png +0 -0
- package/lib/endfield-map/resources/map-a4db9a4763e47fbf.png +0 -0
- package/lib/endfield-map/resources/map-a4e8ef07159997e7.json.br +0 -0
- package/lib/endfield-map/resources/map-a4e8ef07159997e7.json.gz +0 -0
- package/lib/endfield-map/resources/map-ab35d06875ee418f.png +0 -0
- package/lib/endfield-map/resources/map-ac51bb3e7623cd8b.png +0 -0
- package/lib/endfield-map/resources/map-ac830a66296bc83a.png +0 -0
- package/lib/endfield-map/resources/map-b3fd04209718e55e.json.br +0 -0
- package/lib/endfield-map/resources/map-b3fd04209718e55e.json.gz +0 -0
- package/lib/endfield-map/resources/map-b8164f1af7c5d895.png +0 -0
- package/lib/endfield-map/resources/map-b8cde93b965384d8.json.br +0 -0
- package/lib/endfield-map/resources/map-b8cde93b965384d8.json.gz +0 -0
- package/lib/endfield-map/resources/map-b8e48511502e0e8d.json.br +0 -0
- package/lib/endfield-map/resources/map-b8e48511502e0e8d.json.gz +0 -0
- package/lib/endfield-map/resources/map-bfc58277d5ba649d.png +0 -0
- package/lib/endfield-map/resources/map-c013f82048c6891a.png +0 -0
- package/lib/endfield-map/resources/map-c49c947841ae4ab5.json.br +0 -0
- package/lib/endfield-map/resources/map-c49c947841ae4ab5.json.gz +0 -0
- package/lib/endfield-map/resources/map-c4d1b1c6d6706c81.json.br +0 -0
- package/lib/endfield-map/resources/map-c4d1b1c6d6706c81.json.gz +0 -0
- package/lib/endfield-map/resources/map-c607f8b4c8ad8967.png +0 -0
- package/lib/endfield-map/resources/map-c6f20276b8781326.json.br +0 -0
- package/lib/endfield-map/resources/map-c6f20276b8781326.json.gz +0 -0
- package/lib/endfield-map/resources/map-d51adf5952af1e8e.png +0 -0
- package/lib/endfield-map/resources/map-d695eface22f8b74.json.br +0 -0
- package/lib/endfield-map/resources/map-d695eface22f8b74.json.gz +0 -0
- package/lib/endfield-map/resources/map-db11d394456a7465.png +0 -0
- package/lib/endfield-map/resources/map-dcc4d218581e3dc6.png +0 -0
- package/lib/endfield-map/resources/map-ddd43dbb8ab8a4e3.json.br +0 -0
- package/lib/endfield-map/resources/map-ddd43dbb8ab8a4e3.json.gz +0 -0
- package/lib/endfield-map/resources/map-defe5db7dabf748a.png +0 -0
- package/lib/endfield-map/resources/map-e1aeb833e22520aa.json.br +0 -0
- package/lib/endfield-map/resources/map-e1aeb833e22520aa.json.gz +0 -0
- package/lib/endfield-map/resources/map-e25ee9af6e493500.png +0 -0
- package/lib/endfield-map/resources/map-e3824082cd6f747d.json.br +0 -0
- package/lib/endfield-map/resources/map-e3824082cd6f747d.json.gz +0 -0
- package/lib/endfield-map/resources/map-e694a91e630b1bd6.png +0 -0
- package/lib/endfield-map/resources/map-eaa93af79d12a87d.png +0 -0
- package/lib/endfield-map/resources/map-ec9575ee65d7766f.json.br +0 -0
- package/lib/endfield-map/resources/map-ec9575ee65d7766f.json.gz +0 -0
- package/lib/endfield-map/resources/map-ede421a329dc78a4.png +0 -0
- package/lib/endfield-map/resources/map-eff3bce8f6888fc5.json.br +0 -0
- package/lib/endfield-map/resources/map-eff3bce8f6888fc5.json.gz +0 -0
- package/lib/endfield-map/resources/map-fb5f4a59a181452c.png +0 -0
- package/lib/endfield-map/resources/map-fdeaa17eeba90d8e.json.br +0 -0
- package/lib/endfield-map/resources/map-fdeaa17eeba90d8e.json.gz +0 -0
- package/lib/rhine/assets/archive-assembly.glb +0 -0
- package/lib/rhine/assets/archive-cassette.glb +0 -0
- package/lib/rhine/fonts/MiSans-Bold.woff2 +0 -0
- package/lib/rhine/fonts/MiSans-Demibold.woff2 +0 -0
- package/lib/rhine/fonts/MiSans-Light.woff2 +0 -0
- package/lib/rhine/fonts/MiSans-Regular.woff2 +0 -0
- package/lib/rhine/fonts/MiSans-license.pdf +0 -0
- package/lib/rhine/fonts/NOTICE.txt +6 -0
- package/lib/rhine/licenses/RhineLabUI-MIT.txt +21 -0
- package/lib/rhine/licenses/entities-BSD-2-Clause.txt +11 -0
- package/lib/rhine/licenses/linkify-it-MIT.txt +22 -0
- package/lib/rhine/licenses/markdown-it-MIT.txt +22 -0
- package/lib/rhine/licenses/mdurl-MIT.txt +45 -0
- package/lib/rhine/licenses/punycode.js-MIT.txt +20 -0
- package/lib/rhine/licenses/rolling-number-MIT.txt +21 -0
- package/lib/rhine/licenses/three-MIT.txt +21 -0
- package/lib/rhine/licenses/uc.micro-MIT.txt +20 -0
- package/lib/rhine/rhine.css +1 -0
- package/lib/rhine/rhine.js +5106 -0
- package/lib/skins/common.css +31 -0
- package/lib/skins/endfield-aic.css +141 -0
- package/lib/skins/prts-agent.css +136 -0
- package/lib/skins/rhine-lab.css +70 -0
- package/package.json +112 -0
- package/presets/definition.js +26 -0
- package/presets/prts/agent.cordis.yml +25 -0
- package/presets/prts/preset.yml +3 -0
- package/presets/register.js +86 -0
- package/resources/entity-routing.json +21 -0
- package/skills/prts-investigation/SKILL.md +121 -0
- package/skills/prts-retrieval/SKILL.md +35 -0
- package/skills/prts-retrieval/references/cloud-tools.md +13 -0
- package/skills/prts-retrieval/references/diagnostics.md +15 -0
- package/skills/prts-retrieval/references/local-tools.md +24 -0
- package/skills/prts-retrieval/references/module-arknights.md +23 -0
- package/skills/prts-retrieval/references/module-dual.md +15 -0
- package/skills/prts-retrieval/references/module-endfield.md +21 -0
- package/skills/prts-retrieval/references/retrieval-process.md +44 -0
- package/skills/prts-retrieval/references/retrieval-recipes-dual.md +34 -0
- package/skills/prts-retrieval/references/retrieval-recipes-endfield.md +37 -0
- package/skills/prts-retrieval/references/retrieval-recipes.md +39 -0
- package/skills/prts-retrieval/references/tools-runtime.md +48 -0
- package/skills/prts-retrieval/references/wiki-schema.md +38 -0
- package/src/archive-presentation.js +287 -0
- package/src/cloud-projection.js +124 -0
- package/src/cloud.js +498 -0
- package/src/entity-recognizer.js +311 -0
- package/src/entity-routing.js +82 -0
- package/src/evidence-state.js +332 -0
- package/src/i18n.js +365 -0
- package/src/index.js +1261 -0
- package/src/installer.js +1362 -0
- package/src/investigation-review.js +87 -0
- package/src/investigation-store.js +579 -0
- package/src/investigation-tools.js +113 -0
- package/src/localization-format.js +23 -0
- package/src/manual-search.js +103 -0
- package/src/read.js +1097 -0
- package/src/release-compatibility.js +73 -0
- package/src/release-watch.js +40 -0
- package/src/search-projection.js +139 -0
- package/src/search.js +1522 -0
- package/src/skill.js +98 -0
- package/src/source-map.js +348 -0
- package/src/state.js +314 -0
- package/src/store.js +1569 -0
- package/src/timeline.js +386 -0
- package/src/ui.js +1353 -0
- package/src/wiki.js +111 -0
- package/tsconfig.rhine.json +15 -0
- package/ui/rhine/archive-navigation-limit.ts +31 -0
- package/ui/rhine/archive-rack.ts +83 -0
- package/ui/rhine/assets/evidence-observatory.webp +0 -0
- package/ui/rhine/board-plane-transform.ts +24 -0
- package/ui/rhine/catalogue.ts +72 -0
- package/ui/rhine/deferred-preparation.ts +146 -0
- package/ui/rhine/evidence-board-controls.css +22 -0
- package/ui/rhine/evidence-board-history.ts +73 -0
- package/ui/rhine/evidence-board-model.ts +189 -0
- package/ui/rhine/evidence-board-panel.ts +762 -0
- package/ui/rhine/evidence-board-scene.ts +548 -0
- package/ui/rhine/evidence-board.css +191 -0
- package/ui/rhine/evidence-card-art.ts +470 -0
- package/ui/rhine/evidence-card-style.ts +17 -0
- package/ui/rhine/evidence-inbox-scene.ts +43 -0
- package/ui/rhine/evidence-inbox.css +29 -0
- package/ui/rhine/evidence-inbox.ts +104 -0
- package/ui/rhine/index.ts +12 -0
- package/ui/rhine/interaction.css +20 -0
- package/ui/rhine/investigation-board-plane.ts +5 -0
- package/ui/rhine/investigation-board.css +34 -0
- package/ui/rhine/investigation-board.ts +348 -0
- package/ui/rhine/investigation-edits.ts +46 -0
- package/ui/rhine/investigation-layout.ts +29 -0
- package/ui/rhine/label-prefetch.ts +34 -0
- package/ui/rhine/looping-title.ts +40 -0
- package/ui/rhine/manual-search.css +41 -0
- package/ui/rhine/manual-search.ts +89 -0
- package/ui/rhine/original/appearance.ts +180 -0
- package/ui/rhine/original/archive-label.ts +84 -0
- package/ui/rhine/original/archive-lighting.ts +39 -0
- package/ui/rhine/original/archive-loop.ts +82 -0
- package/ui/rhine/original/archive-refill.ts +64 -0
- package/ui/rhine/original/array-detail-data.ts +2 -0
- package/ui/rhine/original/array-detail.ts +41 -0
- package/ui/rhine/original/array-interior.ts +411 -0
- package/ui/rhine/original/array-occlusion.ts +166 -0
- package/ui/rhine/original/array-shell.ts +38 -0
- package/ui/rhine/original/array-visibility.ts +309 -0
- package/ui/rhine/original/asset-url.ts +4 -0
- package/ui/rhine/original/brand.ts +12 -0
- package/ui/rhine/original/cassette-lod.ts +81 -0
- package/ui/rhine/original/data.ts +21 -0
- package/ui/rhine/original/decryption.ts +185 -0
- package/ui/rhine/original/detail-signature.ts +18 -0
- package/ui/rhine/original/glass-reveal.ts +45 -0
- package/ui/rhine/original/internal-optics.ts +70 -0
- package/ui/rhine/original/label-renderer.ts +119 -0
- package/ui/rhine/original/label-textures.ts +117 -0
- package/ui/rhine/original/model-viewer.ts +658 -0
- package/ui/rhine/original/motion.ts +116 -0
- package/ui/rhine/original/opaque-backfaces.ts +64 -0
- package/ui/rhine/original/program-preparation.ts +111 -0
- package/ui/rhine/original/quality-renderer.ts +97 -0
- package/ui/rhine/original/quality-settings.ts +111 -0
- package/ui/rhine/original/render-quality.ts +163 -0
- package/ui/rhine/original/scene.ts +2051 -0
- package/ui/rhine/original/shelf-interior.ts +142 -0
- package/ui/rhine/original/ui-transitions.ts +111 -0
- package/ui/rhine/original/viewer-camera.ts +92 -0
- package/ui/rhine/original/viewport-layout.ts +41 -0
- package/ui/rhine/original-ui.css +2334 -0
- package/ui/rhine/readability.css +46 -0
- package/ui/rhine/reading-object.ts +96 -0
- package/ui/rhine/reading-origin.ts +28 -0
- package/ui/rhine/reading-room.css +55 -0
- package/ui/rhine/report-markdown.ts +98 -0
- package/ui/rhine/scene-edge-navigation.ts +84 -0
- package/ui/rhine/scene.ts +1726 -0
- package/ui/rhine/shelf-layout.ts +38 -0
- package/ui/rhine/snapshot-update-gate.ts +22 -0
- package/ui/rhine/source-reading.ts +15 -0
- package/ui/rhine/source-selection.css +164 -0
- package/ui/rhine/temporary-performance-observers.ts +163 -0
- package/ui/rhine/temporary-performance-panel.ts +124 -0
- package/ui/rhine/temporary-performance.css +19 -0
- package/ui/rhine/temporary-performance.ts +552 -0
- package/ui/rhine/temporary-render-diagnostics.ts +105 -0
- package/ui/rhine/tool-activity.ts +69 -0
- package/ui/rhine/types.ts +219 -0
- package/ui/rhine/visible-scene-hit.ts +13 -0
- package/ui/rhine/workbench.css +818 -0
- package/ui/rhine/workbench.ts +2245 -0
- package/ui/rhine/workspace-travel.ts +32 -0
- package/vite.rhine.config.ts +19 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
export type EvidenceTemplate = 'note' | 'question' | 'source' | 'tag';
|
|
2
|
+
export type EvidenceBoardTool = { mode: 'select' }
|
|
3
|
+
| { mode: 'place'; template: EvidenceTemplate }
|
|
4
|
+
| { mode: 'connect'; fromId?: string };
|
|
5
|
+
|
|
6
|
+
export type EvidenceCard = {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
body: string;
|
|
10
|
+
stage: 0 | 1 | 2;
|
|
11
|
+
kind: 'note' | 'source' | 'question';
|
|
12
|
+
clueKind?: 'excerpt' | 'finding' | 'time' | 'relation' | 'question' | 'contrast' | 'report';
|
|
13
|
+
summary?: string;
|
|
14
|
+
contentRevision?: number;
|
|
15
|
+
layoutRevision?: number;
|
|
16
|
+
variant?: number;
|
|
17
|
+
evidenceLabel?: string;
|
|
18
|
+
sourceLabel?: string;
|
|
19
|
+
relationKinds?: Record<string, string>;
|
|
20
|
+
sourceTitle?: string;
|
|
21
|
+
sourceId?: string;
|
|
22
|
+
position?: { x: number; y: number };
|
|
23
|
+
rotation?: number;
|
|
24
|
+
scale?: number;
|
|
25
|
+
links?: string[];
|
|
26
|
+
visual?: 'observatory' | 'schematic' | 'signal';
|
|
27
|
+
presentation?: 'compact' | 'tag';
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const EVIDENCE_STAGES = ['线索收集', '交叉核验', '阶段结论'] as const;
|
|
31
|
+
export const EVIDENCE_CARD_CAPACITY = 12;
|
|
32
|
+
export const EVIDENCE_TITLE_LIMIT = 120;
|
|
33
|
+
export const EVIDENCE_BODY_LIMIT = 4000;
|
|
34
|
+
export const EVIDENCE_STORAGE_LIMIT = 100000;
|
|
35
|
+
export const EVIDENCE_WRITING_BOUNDS = { left: -7.55, right: 7.55, bottom: -3.82, top: 3.9 } as const;
|
|
36
|
+
export const EVIDENCE_SCALE_MIN = 0.5;
|
|
37
|
+
export const EVIDENCE_SCALE_MAX = 2;
|
|
38
|
+
|
|
39
|
+
/** Do not coerce browser storage or form values into scene dimensions. */
|
|
40
|
+
export function normalizeEvidenceScale(value: unknown): number {
|
|
41
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
42
|
+
? Math.max(EVIDENCE_SCALE_MIN, Math.min(EVIDENCE_SCALE_MAX, value)) : 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function evidenceStorageKey(sessionId: string) {
|
|
46
|
+
return `prts-rhine-evidence-board:v1:${encodeURIComponent(sessionId || 'default')}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createEvidenceId() {
|
|
50
|
+
return `card:${globalThis.crypto?.randomUUID?.() || `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Creating a template never stores a card; placement is confirmed by the caller. */
|
|
54
|
+
export function createEvidenceTemplate(template: EvidenceTemplate): EvidenceCard {
|
|
55
|
+
const titles = { note: '新的观察', question: '待解的问题', source: '资料摘录', tag: '关键词' };
|
|
56
|
+
return { id: createEvidenceId(), title: titles[template], body: '', stage: 0,
|
|
57
|
+
kind: template === 'tag' ? 'note' : template, rotation: 0,
|
|
58
|
+
...(template === 'tag' ? { presentation: 'tag' as const }
|
|
59
|
+
: template === 'source' ? {} : { presentation: 'compact' as const }) };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const POSITIONS = [
|
|
63
|
+
[0, 2.5, -2], [-5.35, 2.15, 7], [5.25, 2.25, -6], [-2.8, -0.1, -4],
|
|
64
|
+
[2.6, -0.35, 5], [-5.1, -2.2, -5], [-0.05, -2.2, 3], [5.1, -2.2, -4],
|
|
65
|
+
[-2.7, 2.95, 2], [2.85, 2.95, -3], [-6, -0.35, 3], [6, -0.35, -2],
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
function basePaperSize(card: EvidenceCard) {
|
|
69
|
+
if (card.clueKind) {
|
|
70
|
+
const sizes = { report: [3.85, 2.75], excerpt: [2.55, 1.62], finding: [2.50, 1.86],
|
|
71
|
+
time: [2.60, 1.22], relation: [2.55, 1.58], question: [2.22, 1.70], contrast: [2.55, 1.88] };
|
|
72
|
+
const [width, height] = sizes[card.clueKind]; return { width, height };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const [width, height] = card.presentation === 'tag' ? [3, 0.7]
|
|
76
|
+
: card.presentation === 'compact' ? card.kind === 'question' ? [3.25, 1.55] : [3.35, 1.7]
|
|
77
|
+
: card.visual === 'observatory' ? [4.6, 3.25]
|
|
78
|
+
: card.visual === 'schematic' ? [5.35, 3.65] : card.visual === 'signal' ? [4.2, 1.95]
|
|
79
|
+
: card.kind === 'source' ? [3.4, 3.8] : card.kind === 'question' ? [3.8, 1.7] : [3.5, 2.25];
|
|
80
|
+
return { width: width * 0.8, height: height * 0.8 };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The rotated sheet must fit on the writing surface even at maximum size. */
|
|
84
|
+
export function evidenceCardScale(card: EvidenceCard, index = 0): number {
|
|
85
|
+
const { width, height } = basePaperSize(card);
|
|
86
|
+
const rotation = card.rotation ?? POSITIONS[index % POSITIONS.length][2];
|
|
87
|
+
const angle = rotation * Math.PI / 180;
|
|
88
|
+
const cosine = Math.abs(Math.cos(angle)), sine = Math.abs(Math.sin(angle));
|
|
89
|
+
const availableWidth = EVIDENCE_WRITING_BOUNDS.right - EVIDENCE_WRITING_BOUNDS.left;
|
|
90
|
+
const availableHeight = EVIDENCE_WRITING_BOUNDS.top - EVIDENCE_WRITING_BOUNDS.bottom;
|
|
91
|
+
return Math.min(normalizeEvidenceScale(card.scale),
|
|
92
|
+
availableWidth / (width * cosine + height * sine),
|
|
93
|
+
availableHeight / (width * sine + height * cosine));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function evidenceCardLayout(card: EvidenceCard, index: number) {
|
|
97
|
+
const [x, y, rotation] = POSITIONS[index % POSITIONS.length];
|
|
98
|
+
const { width, height } = basePaperSize(card), scale = evidenceCardScale(card, index);
|
|
99
|
+
const layout = { x: card.position?.x ?? x, y: card.position?.y ?? y,
|
|
100
|
+
rotation: card.rotation ?? rotation, width: width * scale, height: height * scale, scale };
|
|
101
|
+
return { ...layout, ...clampPaperPosition(layout, layout.x, layout.y) };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function clampPaperPosition(layout: { rotation: number; width: number; height: number }, x: number, y: number) {
|
|
105
|
+
const angle = layout.rotation * Math.PI / 180;
|
|
106
|
+
const halfWidth = (Math.abs(Math.cos(angle)) * layout.width + Math.abs(Math.sin(angle)) * layout.height) / 2;
|
|
107
|
+
const halfHeight = (Math.abs(Math.sin(angle)) * layout.width + Math.abs(Math.cos(angle)) * layout.height) / 2;
|
|
108
|
+
return { x: Math.max(EVIDENCE_WRITING_BOUNDS.left + halfWidth, Math.min(EVIDENCE_WRITING_BOUNDS.right - halfWidth, x)),
|
|
109
|
+
y: Math.max(EVIDENCE_WRITING_BOUNDS.bottom + halfHeight, Math.min(EVIDENCE_WRITING_BOUNDS.top - halfHeight, y)) };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Keep all four paper corners inside the usable writing surface. */
|
|
113
|
+
export function clampEvidencePosition(card: EvidenceCard, index: number, x: number, y: number) {
|
|
114
|
+
return clampPaperPosition(evidenceCardLayout(card, index), x, y);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Browser storage is untrusted; accept only bounded cards that fit the board. */
|
|
118
|
+
export function parseEvidenceCards(value: unknown): EvidenceCard[] {
|
|
119
|
+
if (!Array.isArray(value)) return [];
|
|
120
|
+
const cards: EvidenceCard[] = [], ids = new Set<string>();
|
|
121
|
+
for (const raw of value.slice(0, 64)) {
|
|
122
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) continue;
|
|
123
|
+
const card = raw as Record<string, unknown>;
|
|
124
|
+
if (typeof card.id !== 'string' || !card.id || card.id.length > 128 || ids.has(card.id)
|
|
125
|
+
|| typeof card.title !== 'string' || !card.title.trim() || typeof card.body !== 'string'
|
|
126
|
+
|| card.stage !== 0 && card.stage !== 1 && card.stage !== 2
|
|
127
|
+
|| card.kind !== 'note' && card.kind !== 'source' && card.kind !== 'question') continue;
|
|
128
|
+
cards.push({ id: card.id, title: card.title.trim().slice(0, EVIDENCE_TITLE_LIMIT),
|
|
129
|
+
body: card.body.slice(0, EVIDENCE_BODY_LIMIT), stage: card.stage, kind: card.kind,
|
|
130
|
+
...(card.visual === 'observatory' || card.visual === 'schematic' || card.visual === 'signal'
|
|
131
|
+
? { visual: card.visual as EvidenceCard['visual'] } : {}),
|
|
132
|
+
...(card.presentation === 'compact' || card.presentation === 'tag'
|
|
133
|
+
? { presentation: card.presentation } : {}),
|
|
134
|
+
...(typeof card.sourceTitle === 'string' && card.sourceTitle.trim()
|
|
135
|
+
? { sourceTitle: card.sourceTitle.trim().slice(0, 512) } : {}),
|
|
136
|
+
...(typeof card.sourceId === 'string' && card.sourceId
|
|
137
|
+
? { sourceId: card.sourceId.slice(0, 1024) } : {}),
|
|
138
|
+
...(typeof card.rotation === 'number' && Number.isFinite(card.rotation)
|
|
139
|
+
? { rotation: Math.max(-15, Math.min(15, card.rotation)) } : {}),
|
|
140
|
+
...(typeof card.scale === 'number' && Number.isFinite(card.scale)
|
|
141
|
+
? { scale: normalizeEvidenceScale(card.scale) } : {}),
|
|
142
|
+
...(Array.isArray(card.links) ? { links: [...new Set(card.links.slice(0, 64).filter((id): id is string =>
|
|
143
|
+
typeof id === 'string' && id.length > 0 && id.length <= 128 && id !== card.id))] } : {}),
|
|
144
|
+
});
|
|
145
|
+
const position = card.position as { x?: unknown; y?: unknown } | undefined;
|
|
146
|
+
if (position && typeof position.x === 'number' && Number.isFinite(position.x)
|
|
147
|
+
&& typeof position.y === 'number' && Number.isFinite(position.y)) {
|
|
148
|
+
cards[cards.length - 1].position = clampEvidencePosition(cards[cards.length - 1], cards.length - 1, position.x, position.y);
|
|
149
|
+
}
|
|
150
|
+
ids.add(card.id);
|
|
151
|
+
if (cards.length === EVIDENCE_CARD_CAPACITY) break;
|
|
152
|
+
}
|
|
153
|
+
return cards.map(card => card.links ? { ...card, links: card.links.filter(id => ids.has(id)).slice(0, EVIDENCE_CARD_CAPACITY - 1) } : card);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function isPreviewCard(card: EvidenceCard) {
|
|
157
|
+
return card.id.startsWith('preview:');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Reviewed story paraphrases for the scene preview, not Agent research records.
|
|
161
|
+
* Corpus revision: e501261a50770f281a1cab5ed5a57d97c0d36ce73f7da768b27f592516117d33.
|
|
162
|
+
* Sources: CW-10 before L70–89 / L94–118; CW-ST-4 L81–92 / L109–110 / L136–159;
|
|
163
|
+
* CW-8 after L129–148; CW-7 before L158–167 and after L53. */
|
|
164
|
+
export function createPreviewCards(): EvidenceCard[] {
|
|
165
|
+
return [
|
|
166
|
+
{ id: 'preview:question', title: '星荚之外有什么', stage: 0, kind: 'question',
|
|
167
|
+
body: '克丽斯腾不愿止步于天空。阻隔层之外,泰拉又身处何方?',
|
|
168
|
+
sourceTitle: '《孤星》CW-10 行动前 · 研究提问', position: { x: -0.35, y: 2.65 }, rotation: -3,
|
|
169
|
+
links: ['preview:collect', 'preview:connect'] },
|
|
170
|
+
{ id: 'preview:collect', title: '克丽斯腾的群星', stage: 0, kind: 'source', visual: 'observatory',
|
|
171
|
+
body: '万星园越过星荚。克丽斯腾留下观测记录,在群星之间准备进入休眠。',
|
|
172
|
+
sourceTitle: '《孤星》CW-ST-4 · 剧情改写', position: { x: -4.7, y: 1 }, rotation: 4,
|
|
173
|
+
links: ['preview:another'] },
|
|
174
|
+
{ id: 'preview:verify', title: '保存者最后的选择', stage: 1, kind: 'source',
|
|
175
|
+
body: '保存者亲手关闭旧计划,将最后的能源交给克丽斯腾。\n他选择让年轻的文明迈出下一步。',
|
|
176
|
+
sourceTitle: '《孤星》CW-8 行动后 · 剧情改写', position: { x: 4.65, y: 0.95 }, rotation: -3,
|
|
177
|
+
links: ['preview:connect', 'preview:conclude'] },
|
|
178
|
+
{ id: 'preview:connect', title: '万星园的真正航向', stage: 0, kind: 'source', visual: 'schematic',
|
|
179
|
+
body: '弧光一号是对军方的幌子。万星园的目标,在星荚之外。',
|
|
180
|
+
sourceTitle: '《孤星》CW-10 行动前 · 剧情改写', position: { x: -0.05, y: -0.25 }, rotation: -2.2,
|
|
181
|
+
links: ['preview:another', 'preview:conclude'] },
|
|
182
|
+
{ id: 'preview:another', title: '无法停止的蓄能', stage: 1, kind: 'note', visual: 'signal',
|
|
183
|
+
body: '能量井读数持续升高,军方无法关闭。聚焦发生器已抵达上空。',
|
|
184
|
+
sourceTitle: '《孤星》CW-7 行动前后 · 剧情改写', position: { x: -4.1, y: -2.05 }, rotation: -5 },
|
|
185
|
+
{ id: 'preview:conclude', title: '科学应当看向谁', stage: 2, kind: 'question',
|
|
186
|
+
body: '赫默发起《特里蒙科学伦理联合宣言》。飞向天空,也要回应留在大地上的人。',
|
|
187
|
+
sourceTitle: '《孤星》CW-ST-4 · 剧情改写', position: { x: 4.05, y: -2.15 }, rotation: 5 },
|
|
188
|
+
];
|
|
189
|
+
}
|