@auraindustry/aurajs 0.1.3 → 0.1.5

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 (108) hide show
  1. package/README.md +7 -0
  2. package/benchmarks/perf-thresholds.json +27 -0
  3. package/package.json +6 -1
  4. package/src/ai-guidance.mjs +302 -0
  5. package/src/authored-project.mjs +498 -2
  6. package/src/build-contract/capabilities.mjs +87 -1
  7. package/src/build-contract/constants.mjs +1 -0
  8. package/src/build-contract.mjs +2 -0
  9. package/src/bundler.mjs +143 -13
  10. package/src/cli.mjs +681 -13
  11. package/src/commands/packs.mjs +741 -0
  12. package/src/commands/project-authoring.mjs +128 -1
  13. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
  14. package/src/conformance/cases/core-runtime-cases.mjs +6 -2
  15. package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
  16. package/src/conformance/cases/systems-and-gameplay-cases.mjs +265 -4
  17. package/src/conformance-mobile.mjs +166 -0
  18. package/src/conformance.mjs +89 -30
  19. package/src/evidence-bundle.mjs +242 -0
  20. package/src/headless-test/runtime-coordinator.mjs +186 -33
  21. package/src/headless-test.mjs +2 -0
  22. package/src/helpers/2d/index.mjs +183 -0
  23. package/src/helpers/index.mjs +26 -0
  24. package/src/helpers/starter-utils/adventure-objectives.js +102 -0
  25. package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
  26. package/src/helpers/starter-utils/animation-2d.js +337 -0
  27. package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
  28. package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
  29. package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
  30. package/src/helpers/starter-utils/avatar-3d.js +404 -0
  31. package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
  32. package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
  33. package/src/helpers/starter-utils/core.js +150 -0
  34. package/src/helpers/starter-utils/dialogue-2d.js +351 -0
  35. package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
  36. package/src/helpers/starter-utils/index.js +26 -0
  37. package/src/helpers/starter-utils/inventory-2d.js +268 -0
  38. package/src/helpers/starter-utils/journal-2d.js +267 -0
  39. package/src/helpers/starter-utils/platformer-3d.js +132 -0
  40. package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
  41. package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
  42. package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
  43. package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
  44. package/src/helpers/starter-utils/triggers.js +662 -0
  45. package/src/helpers/starter-utils/tween-2d.js +615 -0
  46. package/src/helpers/starter-utils/wave-director.js +101 -0
  47. package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
  48. package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
  49. package/src/mobile/android/build.mjs +606 -0
  50. package/src/mobile/android/host-artifact.mjs +280 -0
  51. package/src/mobile/ios/build.mjs +1323 -0
  52. package/src/mobile/ios/host-artifact.mjs +819 -0
  53. package/src/mobile/shared/capabilities.mjs +174 -0
  54. package/src/packs/catalog.mjs +259 -0
  55. package/src/perf-benchmark-runner.mjs +17 -12
  56. package/src/perf-benchmark.mjs +408 -4
  57. package/src/publish-command.mjs +303 -6
  58. package/src/replay-runtime.mjs +257 -0
  59. package/src/scaffold/config.mjs +2 -0
  60. package/src/scaffold/fs.mjs +8 -1
  61. package/src/scaffold/project-docs.mjs +43 -1
  62. package/src/scaffold.mjs +4 -0
  63. package/src/session-runtime.mjs +4 -3
  64. package/src/web-conformance.mjs +0 -36
  65. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
  66. package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
  67. package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
  68. package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
  69. package/templates/create/2d-adventure/docs/design/loop.md +4 -3
  70. package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
  71. package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
  72. package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
  73. package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
  74. package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
  75. package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
  76. package/templates/create/3d/scenes/gameplay.scene.js +30 -3
  77. package/templates/create/3d/src/runtime/capabilities.js +5 -0
  78. package/templates/create/3d/src/runtime/materials.js +10 -0
  79. package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
  80. package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
  81. package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
  82. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
  83. package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
  84. package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
  85. package/templates/create/shared/src/runtime/ui-forms.js +552 -0
  86. package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
  87. package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
  88. package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
  89. package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
  90. package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
  91. package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
  92. package/templates/create/shared/src/starter-utils/index.js +15 -1
  93. package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
  94. package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
  95. package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
  96. package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
  97. package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
  98. package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
  99. package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
  100. package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
  101. package/templates/create-bin/play.js +36 -7
  102. package/templates/skills/auramaxx/SKILL.md +46 -0
  103. package/templates/skills/auramaxx/project-requirements.md +68 -0
  104. package/templates/skills/auramaxx/starter-recipes.md +104 -0
  105. package/templates/skills/auramaxx/validation-checklist.md +49 -0
  106. package/templates/skills/aurajs/SKILL.md +0 -96
  107. package/templates/skills/aurajs/api-contract-3d.md +0 -7
  108. package/templates/skills/aurajs/api-contract.md +0 -7
@@ -74,6 +74,34 @@ export function createGameplayScene(context = {}) {
74
74
  let playerVisual = null;
75
75
  let sceneState = createCollectathonRunState();
76
76
 
77
+ function configureSceneLook() {
78
+ aura.light.ambient({ r: 0.94, g: 0.97, b: 1.0 }, 0.16);
79
+ aura.light.hemisphere(
80
+ { r: 0.46, g: 0.6, b: 0.82 },
81
+ { r: 0.13, g: 0.1, b: 0.08 },
82
+ 0.32,
83
+ { x: 0, y: 1, z: 0 },
84
+ );
85
+ aura.light.directional({ x: 0.52, y: -1.0, z: -0.28 }, { r: 1.0, g: 0.97, b: 0.9 }, 1.08);
86
+ aura.light.spot(
87
+ { x: -7.5, y: 7.0, z: 9.0 },
88
+ { x: 0.38, y: -1.0, z: -0.34 },
89
+ { r: 1.0, g: 0.84, b: 0.56 },
90
+ 1.45,
91
+ 30,
92
+ 0.76,
93
+ );
94
+ aura.draw3d.setFog({
95
+ mode: 'exp2',
96
+ color: { r: 0.05, g: 0.08, b: 0.11 },
97
+ density: 0.017,
98
+ });
99
+ aura.draw3d.setOcclusionCulling({ enabled: true, conservative: true });
100
+ aura.draw3d.setPostFXPass('bloom', { strength: 0.32, radius: 0.52, threshold: 0.72 });
101
+ aura.draw3d.setPostFXPass('vignette', { strength: 0.14, radius: 0.93 });
102
+ aura.draw3d.setPostFXPass('fxaa', { strength: 1.0 });
103
+ }
104
+
77
105
  function syncSessionState() {
78
106
  collectathonSession.lastSceneId = 'gameplay';
79
107
  collectathonSession.bestScore = Math.max(Number(collectathonSession.bestScore || 0), sceneState.score);
@@ -137,8 +165,7 @@ export function createGameplayScene(context = {}) {
137
165
  materials = createCollectathonMaterials();
138
166
  playerVisual = loadPlayerAvatarVisual();
139
167
 
140
- aura.light.ambient({ r: 1, g: 1, b: 1 }, 0.23);
141
- aura.light.directional({ x: 0.55, y: -1.0, z: -0.35 }, { r: 1, g: 1, b: 1 }, 1.0);
168
+ configureSceneLook();
142
169
  aura.camera3d.perspective(62, 0.1, 150);
143
170
 
144
171
  resetRun();
@@ -174,7 +201,7 @@ export function createGameplayScene(context = {}) {
174
201
  syncSessionState();
175
202
  },
176
203
  draw() {
177
- aura.draw3d.clear3d({ r: 0.06, g: 0.08, b: 0.12 });
204
+ aura.draw3d.clear3d({ r: 0.04, g: 0.06, b: 0.085 });
178
205
  drawCollectathonCourse({
179
206
  mesh: boxMesh,
180
207
  floorMaterial: materials.floor,
@@ -11,7 +11,9 @@ export function assertRuntimeCapabilities() {
11
11
  if (!hasMethod(aura.mesh, 'createBox')) missing.push('aura.mesh.createBox');
12
12
  if (!hasMethod(aura.material, 'create')) missing.push('aura.material.create');
13
13
  if (!hasMethod(aura.light, 'ambient')) missing.push('aura.light.ambient');
14
+ if (!hasMethod(aura.light, 'hemisphere')) missing.push('aura.light.hemisphere');
14
15
  if (!hasMethod(aura.light, 'directional')) missing.push('aura.light.directional');
16
+ if (!hasMethod(aura.light, 'spot')) missing.push('aura.light.spot');
15
17
  if (!hasMethod(aura.camera3d, 'perspective')) missing.push('aura.camera3d.perspective');
16
18
  if (!hasMethod(aura.camera3d, 'setPosition')) missing.push('aura.camera3d.setPosition');
17
19
  if (!hasMethod(aura.camera3d, 'lookAt')) missing.push('aura.camera3d.lookAt');
@@ -33,6 +35,9 @@ export function assertRuntimeCapabilities() {
33
35
  if (!hasMethod(aura.scene3d, 'submitRenderBindings')) missing.push('aura.scene3d.submitRenderBindings');
34
36
  if (!hasMethod(aura.draw3d, 'clear3d')) missing.push('aura.draw3d.clear3d');
35
37
  if (!hasMethod(aura.draw3d, 'drawMesh')) missing.push('aura.draw3d.drawMesh');
38
+ if (!hasMethod(aura.draw3d, 'setFog')) missing.push('aura.draw3d.setFog');
39
+ if (!hasMethod(aura.draw3d, 'setOcclusionCulling')) missing.push('aura.draw3d.setOcclusionCulling');
40
+ if (!hasMethod(aura.draw3d, 'setPostFXPass')) missing.push('aura.draw3d.setPostFXPass');
36
41
  if (!hasMethod(aura.draw2d, 'text')) missing.push('aura.draw2d.text');
37
42
  if (!hasMethod(aura.draw2d, 'measureText')) missing.push('aura.draw2d.measureText');
38
43
  if (typeof aura.rgb !== 'function') missing.push('aura.rgb');
@@ -12,23 +12,33 @@ export function createCollectathonMaterials() {
12
12
  }),
13
13
  player: aura.material.create({
14
14
  color: { r: 0.98, g: 0.55, b: 0.26, a: 1.0 },
15
+ emissive: { r: 0.09, g: 0.03, b: 0.01 },
15
16
  metallic: 0.28,
16
17
  roughness: 0.32,
18
+ sheenColor: { r: 0.16, g: 0.1, b: 0.08 },
19
+ sheenRoughness: 0.44,
17
20
  }),
18
21
  collectible: aura.material.create({
19
22
  color: { r: 0.98, g: 0.92, b: 0.38, a: 1.0 },
23
+ emissive: { r: 0.48, g: 0.38, b: 0.1 },
20
24
  metallic: 0.7,
21
25
  roughness: 0.25,
22
26
  }),
23
27
  checkpoint: aura.material.create({
24
28
  color: { r: 0.34, g: 0.9, b: 1.0, a: 1.0 },
29
+ emissive: { r: 0.05, g: 0.24, b: 0.28 },
25
30
  metallic: 0.45,
26
31
  roughness: 0.3,
32
+ sheenColor: { r: 0.12, g: 0.18, b: 0.22 },
33
+ sheenRoughness: 0.24,
27
34
  }),
28
35
  goal: aura.material.create({
29
36
  color: { r: 0.54, g: 1.0, b: 0.7, a: 1.0 },
37
+ emissive: { r: 0.08, g: 0.26, b: 0.14 },
30
38
  metallic: 0.38,
31
39
  roughness: 0.32,
40
+ sheenColor: { r: 0.14, g: 0.2, b: 0.16 },
41
+ sheenRoughness: 0.28,
32
42
  }),
33
43
  };
34
44
  }
@@ -34,6 +34,89 @@ function normalizeMenuOptions(options) {
34
34
  : [];
35
35
  }
36
36
 
37
+ function normalizeCollectionItems(items) {
38
+ return Array.isArray(items)
39
+ ? items
40
+ .filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry))
41
+ .map((entry) => ({
42
+ id: typeof entry.id === 'string' ? entry.id.trim() : '',
43
+ label: typeof entry.label === 'string' && entry.label.trim().length > 0 ? entry.label : String(entry.id || ''),
44
+ description: typeof entry.description === 'string' ? entry.description : null,
45
+ detail: typeof entry.detail === 'string' ? entry.detail : null,
46
+ badge: typeof entry.badge === 'string' && entry.badge.trim().length > 0 ? entry.badge.trim() : null,
47
+ quantity: Number.isFinite(Number(entry.quantity)) ? Math.max(0, Number(entry.quantity)) : 0,
48
+ disabled: entry.disabled === true,
49
+ }))
50
+ .filter((entry) => entry.id.length > 0)
51
+ : [];
52
+ }
53
+
54
+ function normalizeDialogueChoices(choices) {
55
+ return normalizeMenuOptions(choices);
56
+ }
57
+
58
+ function normalizeJournalEntries(entries) {
59
+ return Array.isArray(entries)
60
+ ? entries
61
+ .filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry))
62
+ .map((entry, index) => {
63
+ const id = typeof entry.id === 'string' && entry.id.trim().length > 0
64
+ ? entry.id.trim()
65
+ : `entry-${index + 1}`;
66
+ const label = typeof entry.label === 'string' && entry.label.trim().length > 0
67
+ ? entry.label.trim()
68
+ : (typeof entry.title === 'string' && entry.title.trim().length > 0
69
+ ? entry.title.trim()
70
+ : id);
71
+ return {
72
+ id,
73
+ label,
74
+ description: typeof entry.description === 'string'
75
+ ? entry.description
76
+ : (typeof entry.summary === 'string' ? entry.summary : null),
77
+ detail: typeof entry.detail === 'string'
78
+ ? entry.detail
79
+ : (typeof entry.body === 'string' ? entry.body : null),
80
+ badge: typeof entry.badge === 'string' && entry.badge.trim().length > 0
81
+ ? entry.badge.trim()
82
+ : (typeof entry.status === 'string' && entry.status.trim().length > 0
83
+ ? entry.status.trim()
84
+ : null),
85
+ meta: typeof entry.meta === 'string' && entry.meta.trim().length > 0
86
+ ? entry.meta.trim()
87
+ : (typeof entry.updatedAt === 'string' && entry.updatedAt.trim().length > 0
88
+ ? entry.updatedAt.trim()
89
+ : null),
90
+ disabled: entry.disabled === true,
91
+ };
92
+ })
93
+ .filter((entry) => entry.id.length > 0)
94
+ : [];
95
+ }
96
+
97
+ function buttonStateFromView(rootId, optionId) {
98
+ return readViewState(`${rootId}-${optionId}`) || null;
99
+ }
100
+
101
+ function selectionState(option, state, selected) {
102
+ return {
103
+ id: option.id,
104
+ label: option.label,
105
+ selected,
106
+ clicked: state?.clicked === true,
107
+ focused: state?.focused === true,
108
+ disabled: state?.disabled === true || option.disabled === true,
109
+ };
110
+ }
111
+
112
+ function collectionSummary(item) {
113
+ if (!item) return null;
114
+ if (item.badge && item.quantity > 1) return `${item.badge} · x${item.quantity}`;
115
+ if (item.badge) return item.badge;
116
+ if (item.quantity > 1) return `x${item.quantity}`;
117
+ return null;
118
+ }
119
+
37
120
  function renderInfoText(id, label = null, description = null) {
38
121
  if (label) {
39
122
  globalThis.aura.ui.text(`${id}-label`, {
@@ -207,3 +290,472 @@ export function renderMenuList({
207
290
  states: cloneValue(states),
208
291
  };
209
292
  }
293
+
294
+ export function renderTabBar({
295
+ id,
296
+ label = null,
297
+ description = null,
298
+ value = null,
299
+ tabs = [],
300
+ } = {}) {
301
+ if (!hasUiMethod('beginColumn') || !hasUiMethod('beginRow') || !hasUiMethod('button')) {
302
+ return {
303
+ ok: false,
304
+ reasonCode: 'ui_tab_bar_runtime_unavailable',
305
+ value,
306
+ actionId: null,
307
+ };
308
+ }
309
+ const rootId = typeof id === 'string' ? id.trim() : '';
310
+ const normalizedTabs = normalizeMenuOptions(tabs);
311
+ if (!rootId || normalizedTabs.length <= 0) {
312
+ return {
313
+ ok: false,
314
+ reasonCode: 'ui_tab_bar_invalid_options',
315
+ value,
316
+ actionId: null,
317
+ };
318
+ }
319
+
320
+ const theme = activeTheme();
321
+ globalThis.aura.ui.beginColumn({
322
+ id: `${rootId}-group`,
323
+ gap: 8,
324
+ width: 'fill',
325
+ });
326
+ renderInfoText(rootId, label, description);
327
+ globalThis.aura.ui.beginRow({
328
+ id: `${rootId}-tabs`,
329
+ gap: 8,
330
+ width: 'fill',
331
+ });
332
+ for (const tab of normalizedTabs) {
333
+ const selected = tab.id === value;
334
+ globalThis.aura.ui.button(`${rootId}-${tab.id}`, {
335
+ label: tab.label,
336
+ disabled: tab.disabled === true,
337
+ width: 'hug',
338
+ background: selected ? (theme?.buttonFocusColor || '#1e5b97') : (theme?.buttonColor || '#132132'),
339
+ borderColor: selected ? (theme?.buttonTextColor || '#ffffff') : (theme?.panelBorderColor || '#314e68'),
340
+ });
341
+ }
342
+ globalThis.aura.ui.endRow();
343
+ globalThis.aura.ui.endColumn();
344
+
345
+ let nextValue = value;
346
+ let actionId = null;
347
+ const states = [];
348
+ for (const tab of normalizedTabs) {
349
+ const state = buttonStateFromView(rootId, tab.id);
350
+ if (state?.clicked === true || state?.activated === true) {
351
+ nextValue = tab.id;
352
+ actionId = tab.id;
353
+ }
354
+ states.push(selectionState(tab, state, tab.id === nextValue));
355
+ }
356
+
357
+ return {
358
+ ok: true,
359
+ reasonCode: 'ui_tab_bar_rendered',
360
+ value: nextValue,
361
+ changed: nextValue !== value,
362
+ actionId,
363
+ states: cloneValue(states),
364
+ };
365
+ }
366
+
367
+ export function renderCollectionList({
368
+ id,
369
+ label = null,
370
+ description = null,
371
+ value = null,
372
+ items = [],
373
+ emptyText = 'Nothing here yet.',
374
+ } = {}) {
375
+ if (!hasUiMethod('beginColumn') || !hasUiMethod('button')) {
376
+ return {
377
+ ok: false,
378
+ reasonCode: 'ui_collection_list_runtime_unavailable',
379
+ value,
380
+ actionId: null,
381
+ states: [],
382
+ };
383
+ }
384
+ const rootId = typeof id === 'string' ? id.trim() : '';
385
+ if (!rootId) {
386
+ return {
387
+ ok: false,
388
+ reasonCode: 'ui_collection_list_invalid_id',
389
+ value,
390
+ actionId: null,
391
+ states: [],
392
+ };
393
+ }
394
+
395
+ const normalizedItems = normalizeCollectionItems(items);
396
+ const theme = activeTheme();
397
+ globalThis.aura.ui.beginColumn({
398
+ id: `${rootId}-group`,
399
+ gap: 8,
400
+ width: 'fill',
401
+ });
402
+ renderInfoText(rootId, label, description);
403
+
404
+ if (normalizedItems.length <= 0) {
405
+ globalThis.aura.ui.text(`${rootId}-empty`, {
406
+ text: emptyText,
407
+ size: 13,
408
+ color: theme?.mutedTextColor || '#9db2cb',
409
+ });
410
+ globalThis.aura.ui.endColumn();
411
+ return {
412
+ ok: true,
413
+ reasonCode: 'ui_collection_list_empty',
414
+ value,
415
+ changed: false,
416
+ actionId: null,
417
+ states: [],
418
+ selectedItem: null,
419
+ };
420
+ }
421
+
422
+ for (const item of normalizedItems) {
423
+ const selected = item.id === value;
424
+ const summary = collectionSummary(item);
425
+ const buttonLabel = summary ? `${item.label} (${summary})` : item.label;
426
+ globalThis.aura.ui.button(`${rootId}-${item.id}`, {
427
+ label: buttonLabel,
428
+ width: 'fill',
429
+ disabled: item.disabled === true,
430
+ background: selected ? (theme?.buttonFocusColor || '#1e5b97') : (theme?.buttonColor || '#132132'),
431
+ borderColor: selected ? (theme?.buttonTextColor || '#ffffff') : (theme?.panelBorderColor || '#314e68'),
432
+ });
433
+ if (item.description) {
434
+ globalThis.aura.ui.text(`${rootId}-${item.id}-description`, {
435
+ text: item.description,
436
+ size: 12,
437
+ color: theme?.mutedTextColor || '#9db2cb',
438
+ });
439
+ }
440
+ if (item.detail) {
441
+ globalThis.aura.ui.text(`${rootId}-${item.id}-detail`, {
442
+ text: item.detail,
443
+ size: 12,
444
+ color: theme?.mutedTextColor || '#9db2cb',
445
+ });
446
+ }
447
+ }
448
+ globalThis.aura.ui.endColumn();
449
+
450
+ let nextValue = value;
451
+ let actionId = null;
452
+ const states = [];
453
+ for (const item of normalizedItems) {
454
+ const state = buttonStateFromView(rootId, item.id);
455
+ if (state?.clicked === true || state?.activated === true) {
456
+ nextValue = item.id;
457
+ actionId = item.id;
458
+ }
459
+ states.push({
460
+ ...selectionState(item, state, item.id === nextValue),
461
+ badge: item.badge,
462
+ quantity: item.quantity,
463
+ });
464
+ }
465
+
466
+ const selectedItem = normalizedItems.find((entry) => entry.id === nextValue) || null;
467
+ return {
468
+ ok: true,
469
+ reasonCode: 'ui_collection_list_rendered',
470
+ value: nextValue,
471
+ changed: nextValue !== value,
472
+ actionId,
473
+ states: cloneValue(states),
474
+ selectedItem: cloneValue(selectedItem),
475
+ };
476
+ }
477
+
478
+ export function renderDialoguePanel({
479
+ id,
480
+ label = null,
481
+ speaker = null,
482
+ title = null,
483
+ text = '',
484
+ note = null,
485
+ value = null,
486
+ choices = [],
487
+ emptyText = 'No response options available.',
488
+ } = {}) {
489
+ if (!hasUiMethod('beginColumn') || !hasUiMethod('button') || !hasUiMethod('text')) {
490
+ return {
491
+ ok: false,
492
+ reasonCode: 'ui_dialogue_panel_runtime_unavailable',
493
+ value,
494
+ actionId: null,
495
+ states: [],
496
+ selectedChoice: null,
497
+ };
498
+ }
499
+ const rootId = typeof id === 'string' ? id.trim() : '';
500
+ if (!rootId) {
501
+ return {
502
+ ok: false,
503
+ reasonCode: 'ui_dialogue_panel_invalid_id',
504
+ value,
505
+ actionId: null,
506
+ states: [],
507
+ selectedChoice: null,
508
+ };
509
+ }
510
+
511
+ const normalizedChoices = normalizeDialogueChoices(choices);
512
+ const theme = activeTheme();
513
+
514
+ globalThis.aura.ui.beginColumn({
515
+ id: `${rootId}-group`,
516
+ gap: 8,
517
+ width: 'fill',
518
+ });
519
+ if (label) {
520
+ globalThis.aura.ui.text(`${rootId}-label`, {
521
+ text: label,
522
+ size: 11,
523
+ color: theme?.mutedTextColor || '#9db2cb',
524
+ });
525
+ }
526
+ if (speaker) {
527
+ globalThis.aura.ui.text(`${rootId}-speaker`, {
528
+ text: speaker,
529
+ size: 13,
530
+ color: '#84c3ef',
531
+ });
532
+ }
533
+ if (title) {
534
+ globalThis.aura.ui.text(`${rootId}-title`, {
535
+ text: title,
536
+ size: 20,
537
+ color: '#eef7ff',
538
+ });
539
+ }
540
+ globalThis.aura.ui.text(`${rootId}-text`, {
541
+ text,
542
+ size: 13,
543
+ color: '#d5e1ee',
544
+ });
545
+ if (note) {
546
+ globalThis.aura.ui.text(`${rootId}-note`, {
547
+ text: note,
548
+ size: 12,
549
+ color: theme?.mutedTextColor || '#9db2cb',
550
+ });
551
+ }
552
+
553
+ if (normalizedChoices.length <= 0) {
554
+ globalThis.aura.ui.text(`${rootId}-empty`, {
555
+ text: emptyText,
556
+ size: 12,
557
+ color: theme?.mutedTextColor || '#9db2cb',
558
+ });
559
+ globalThis.aura.ui.endColumn();
560
+ return {
561
+ ok: true,
562
+ reasonCode: 'ui_dialogue_panel_empty_choices',
563
+ value,
564
+ changed: false,
565
+ actionId: null,
566
+ states: [],
567
+ selectedChoice: null,
568
+ };
569
+ }
570
+
571
+ for (const choice of normalizedChoices) {
572
+ const selected = choice.id === value;
573
+ globalThis.aura.ui.button(`${rootId}-${choice.id}`, {
574
+ label: choice.label,
575
+ width: 'fill',
576
+ disabled: choice.disabled === true,
577
+ background: selected ? (theme?.buttonFocusColor || '#1e5b97') : (theme?.buttonColor || '#132132'),
578
+ borderColor: selected ? (theme?.buttonTextColor || '#ffffff') : (theme?.panelBorderColor || '#314e68'),
579
+ });
580
+ if (choice.description) {
581
+ globalThis.aura.ui.text(`${rootId}-${choice.id}-description`, {
582
+ text: choice.description,
583
+ size: 12,
584
+ color: theme?.mutedTextColor || '#9db2cb',
585
+ });
586
+ }
587
+ }
588
+ globalThis.aura.ui.endColumn();
589
+
590
+ let nextValue = value;
591
+ let actionId = null;
592
+ const states = [];
593
+ for (const choice of normalizedChoices) {
594
+ const state = buttonStateFromView(rootId, choice.id);
595
+ if (state?.clicked === true || state?.activated === true) {
596
+ nextValue = choice.id;
597
+ actionId = choice.id;
598
+ }
599
+ states.push(selectionState(choice, state, choice.id === nextValue));
600
+ }
601
+
602
+ const selectedChoice = normalizedChoices.find((entry) => entry.id === nextValue) || null;
603
+ return {
604
+ ok: true,
605
+ reasonCode: 'ui_dialogue_panel_rendered',
606
+ value: nextValue,
607
+ changed: nextValue !== value,
608
+ actionId,
609
+ states: cloneValue(states),
610
+ selectedChoice: cloneValue(selectedChoice),
611
+ };
612
+ }
613
+
614
+ export function renderJournalPane({
615
+ id,
616
+ label = null,
617
+ description = null,
618
+ value = null,
619
+ entries = [],
620
+ emptyText = 'No journal entries yet.',
621
+ detailEmptyText = 'Select an entry to inspect the current route record.',
622
+ } = {}) {
623
+ if (!hasUiMethod('beginColumn') || !hasUiMethod('beginRow') || !hasUiMethod('button') || !hasUiMethod('text')) {
624
+ return {
625
+ ok: false,
626
+ reasonCode: 'ui_journal_pane_runtime_unavailable',
627
+ value,
628
+ actionId: null,
629
+ states: [],
630
+ selectedEntry: null,
631
+ };
632
+ }
633
+ const rootId = typeof id === 'string' ? id.trim() : '';
634
+ if (!rootId) {
635
+ return {
636
+ ok: false,
637
+ reasonCode: 'ui_journal_pane_invalid_id',
638
+ value,
639
+ actionId: null,
640
+ states: [],
641
+ selectedEntry: null,
642
+ };
643
+ }
644
+
645
+ const normalizedEntries = normalizeJournalEntries(entries);
646
+ const theme = activeTheme();
647
+
648
+ globalThis.aura.ui.beginColumn({
649
+ id: `${rootId}-group`,
650
+ gap: 8,
651
+ width: 'fill',
652
+ });
653
+ renderInfoText(rootId, label, description);
654
+ globalThis.aura.ui.beginRow({
655
+ id: `${rootId}-layout`,
656
+ gap: 12,
657
+ width: 'fill',
658
+ });
659
+
660
+ globalThis.aura.ui.beginColumn({
661
+ id: `${rootId}-list`,
662
+ width: 220,
663
+ gap: 8,
664
+ });
665
+ if (normalizedEntries.length <= 0) {
666
+ globalThis.aura.ui.text(`${rootId}-empty`, {
667
+ text: emptyText,
668
+ size: 13,
669
+ color: theme?.mutedTextColor || '#9db2cb',
670
+ });
671
+ } else {
672
+ for (const entry of normalizedEntries) {
673
+ const selected = entry.id === value;
674
+ const summary = entry.badge ? `${entry.label} (${entry.badge})` : entry.label;
675
+ globalThis.aura.ui.button(`${rootId}-${entry.id}`, {
676
+ label: summary,
677
+ width: 'fill',
678
+ disabled: entry.disabled === true,
679
+ background: selected ? (theme?.buttonFocusColor || '#1e5b97') : (theme?.buttonColor || '#132132'),
680
+ borderColor: selected ? (theme?.buttonTextColor || '#ffffff') : (theme?.panelBorderColor || '#314e68'),
681
+ });
682
+ if (entry.description) {
683
+ globalThis.aura.ui.text(`${rootId}-${entry.id}-description`, {
684
+ text: entry.description,
685
+ size: 12,
686
+ color: theme?.mutedTextColor || '#9db2cb',
687
+ });
688
+ }
689
+ }
690
+ }
691
+ globalThis.aura.ui.endColumn();
692
+
693
+ const fallbackEntry = normalizedEntries[0] || null;
694
+ const activeEntry = normalizedEntries.find((entry) => entry.id === value) || fallbackEntry;
695
+
696
+ globalThis.aura.ui.beginColumn({
697
+ id: `${rootId}-detail`,
698
+ width: 'fill',
699
+ gap: 8,
700
+ });
701
+ globalThis.aura.ui.text(`${rootId}-detail-label`, {
702
+ text: 'ENTRY',
703
+ size: 11,
704
+ color: '#84c3ef',
705
+ });
706
+ globalThis.aura.ui.text(`${rootId}-detail-title`, {
707
+ text: activeEntry?.label || 'No entry selected',
708
+ size: 18,
709
+ color: '#eef7ff',
710
+ });
711
+ globalThis.aura.ui.text(`${rootId}-detail-badge`, {
712
+ text: activeEntry?.badge || 'Journal',
713
+ size: 12,
714
+ color: '#f0cf79',
715
+ });
716
+ globalThis.aura.ui.text(`${rootId}-detail-copy`, {
717
+ text: activeEntry?.detail || activeEntry?.description || detailEmptyText,
718
+ size: 13,
719
+ color: '#d5e1ee',
720
+ });
721
+ if (activeEntry?.meta) {
722
+ globalThis.aura.ui.text(`${rootId}-detail-meta`, {
723
+ text: activeEntry.meta,
724
+ size: 12,
725
+ color: theme?.mutedTextColor || '#9db2cb',
726
+ });
727
+ }
728
+ globalThis.aura.ui.endColumn();
729
+
730
+ globalThis.aura.ui.endRow();
731
+ globalThis.aura.ui.endColumn();
732
+
733
+ let nextValue = value;
734
+ let actionId = null;
735
+ const states = [];
736
+ for (const entry of normalizedEntries) {
737
+ const state = buttonStateFromView(rootId, entry.id);
738
+ if (state?.clicked === true || state?.activated === true) {
739
+ nextValue = entry.id;
740
+ actionId = entry.id;
741
+ }
742
+ states.push({
743
+ ...selectionState(entry, state, entry.id === nextValue),
744
+ badge: entry.badge,
745
+ meta: entry.meta,
746
+ });
747
+ }
748
+
749
+ const selectedEntry = normalizedEntries.find((entry) => entry.id === nextValue)
750
+ || normalizedEntries[0]
751
+ || null;
752
+ return {
753
+ ok: true,
754
+ reasonCode: normalizedEntries.length > 0 ? 'ui_journal_pane_rendered' : 'ui_journal_pane_empty',
755
+ value: selectedEntry?.id || value || null,
756
+ changed: Boolean(selectedEntry?.id && selectedEntry.id !== value),
757
+ actionId,
758
+ states: cloneValue(states),
759
+ selectedEntry: cloneValue(selectedEntry),
760
+ };
761
+ }