@unson/brainbase-mcp 0.44.1 → 0.44.2
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/package.json +2 -2
- package/ui/workspace-kit.css +33 -1
- package/ui/workspace-kit.js +13 -2
- package/ui/world/world-canvas-ui.css +4 -49
- package/ui/world/world-canvas-ui.js +26 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unson/brainbase-mcp",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.2",
|
|
4
4
|
"description": "Local-first Brainbase judgment OS that lets supported AI agents reuse a user's approved goals, priorities, decisions, and judgment criteria.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -608,5 +608,5 @@
|
|
|
608
608
|
"url": "https://github.com/Unson-LLC/brainbase/issues"
|
|
609
609
|
},
|
|
610
610
|
"homepage": "https://github.com/Unson-LLC/brainbase#readme",
|
|
611
|
-
"gitHead": "
|
|
611
|
+
"gitHead": "108ed1e1c5066b088e6c61b9d6ff87149a9f2ce6"
|
|
612
612
|
}
|
package/ui/workspace-kit.css
CHANGED
|
@@ -517,7 +517,39 @@
|
|
|
517
517
|
.bb-ws-error-stack { position: absolute; z-index: 55; top: 0; right: 0; width: min(300px,100%); max-height: none; padding-bottom: 64px; pointer-events: none; box-sizing: border-box; overflow: visible; }
|
|
518
518
|
.bb-ws-error-list { display: grid; gap: 6px; }
|
|
519
519
|
.bb-ws-error-count { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
|
|
520
|
-
.bb-ws-
|
|
520
|
+
.bb-ws-notification-card {
|
|
521
|
+
--bb-notification-accent: var(--bb-color-notice-control-border);
|
|
522
|
+
--bb-notification-symbol-border: var(--bb-color-notice-control-border);
|
|
523
|
+
--bb-notification-symbol-text: var(--bb-color-notice-text);
|
|
524
|
+
box-sizing: border-box;
|
|
525
|
+
padding: 8px 10px;
|
|
526
|
+
border: 1px solid var(--bb-color-notice-border);
|
|
527
|
+
border-left: 3px solid var(--bb-notification-accent);
|
|
528
|
+
border-radius: 4px;
|
|
529
|
+
background: var(--bb-color-notice-background);
|
|
530
|
+
backdrop-filter: blur(5px);
|
|
531
|
+
color: var(--bb-color-notice-text);
|
|
532
|
+
font-size: 12px;
|
|
533
|
+
line-height: 1.5;
|
|
534
|
+
}
|
|
535
|
+
.bb-ws-notification-card.is-warning {
|
|
536
|
+
--bb-notification-accent: var(--bb-color-notice-warning);
|
|
537
|
+
--bb-notification-symbol-border: var(--bb-color-notice-warning-symbol-border);
|
|
538
|
+
--bb-notification-symbol-text: var(--bb-color-notice-warning-text);
|
|
539
|
+
}
|
|
540
|
+
.bb-ws-notification-card.is-danger {
|
|
541
|
+
--bb-notification-accent: var(--bb-color-notice-danger);
|
|
542
|
+
--bb-notification-symbol-border: var(--bb-color-notice-danger-symbol-border);
|
|
543
|
+
--bb-notification-symbol-text: var(--bb-color-notice-danger-text);
|
|
544
|
+
}
|
|
545
|
+
.bb-ws-notification-symbol {
|
|
546
|
+
display: grid; place-items: center; flex: none;
|
|
547
|
+
width: 18px; height: 18px; border-radius: 3px;
|
|
548
|
+
border: 1px solid var(--bb-notification-symbol-border);
|
|
549
|
+
color: var(--bb-notification-symbol-text);
|
|
550
|
+
font-size: 11px; font-weight: 700;
|
|
551
|
+
}
|
|
552
|
+
.bb-ws-error-card { display: flex; align-items: start; gap: 8px; width: 100%; pointer-events: auto; }
|
|
521
553
|
.bb-ws-error-card.is-warning { border-left-color: var(--bb-color-notice-warning); }
|
|
522
554
|
.bb-ws-error-card > details { flex: 1; min-width: 0; }
|
|
523
555
|
.bb-ws-error-card summary { display: flex; align-items: center; gap: 6px; list-style: none; cursor: pointer; }
|
package/ui/workspace-kit.js
CHANGED
|
@@ -371,9 +371,20 @@ export function workspaceFilterPopover(doc, { content, open = false, onToggle, t
|
|
|
371
371
|
} };
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
+
/** Reusable notification surface. Hosts own content, placement and lifecycle.
|
|
375
|
+
* Tones use shared semantic tokens; unknown values stay informational.
|
|
376
|
+
*/
|
|
377
|
+
export function workspaceNotificationCard(doc, { tone = 'info', content, tag = 'section', className = '', role } = {}) {
|
|
378
|
+
const severity = tone === 'attention' ? 'warning' : ['info', 'warning', 'danger'].includes(tone) ? tone : 'info';
|
|
379
|
+
const card = el(doc, tag, { className: `bb-ws-notification-card is-${severity} ${className}`.trim(), attrs: { role } });
|
|
380
|
+
appendContent(card, doc, content);
|
|
381
|
+
return card;
|
|
382
|
+
}
|
|
383
|
+
|
|
374
384
|
/** Compact notification with a fixed-center, purely visual spinner. */
|
|
375
385
|
export function workspaceLoadingCard(doc, { title = 'ロード中' } = {}) {
|
|
376
|
-
const card =
|
|
386
|
+
const card = workspaceNotificationCard(doc, { className: 'bb-ws-error-card bb-ws-loading-card', role: 'status' });
|
|
387
|
+
card.setAttribute('aria-live', 'polite');
|
|
377
388
|
card.append(el(doc, 'span', { className: 'bb-ws-loading-spinner', attrs: { 'aria-hidden': 'true' } }), el(doc, 'span', { text: title }));
|
|
378
389
|
return card;
|
|
379
390
|
}
|
|
@@ -404,7 +415,7 @@ export function workspaceDismissNotification(card, onDismiss) {
|
|
|
404
415
|
|
|
405
416
|
/** Persistent, initially closed notification. The caller owns its lifecycle. */
|
|
406
417
|
export function workspaceErrorCard(doc, { title, message, tone = 'danger', onRetry } = {}) {
|
|
407
|
-
const card =
|
|
418
|
+
const card = workspaceNotificationCard(doc, { tone, className: 'bb-ws-error-card', role: 'listitem' });
|
|
408
419
|
const details = el(doc, 'details');
|
|
409
420
|
const summary = el(doc, 'summary', { attrs: { 'aria-label': `${title}の通知` } });
|
|
410
421
|
summary.append(el(doc, 'span', { className: 'bb-ws-error-symbol', text: '!', attrs: { 'aria-hidden': 'true' } }), el(doc, 'span', { text: title }));
|
|
@@ -103,31 +103,7 @@
|
|
|
103
103
|
.bb-world-canvas-ui .bb-world-canvas-city:disabled { cursor: default; opacity: .65; }
|
|
104
104
|
.bb-world-canvas-ui .bb-world-canvas-sky { display: grid; place-items: center; padding: 0; }
|
|
105
105
|
.bb-world-canvas-ui .bb-world-canvas-sky-icon { font-size: 22px; line-height: 1; }
|
|
106
|
-
.bb-world-canvas-ui .bb-world-canvas-sky-
|
|
107
|
-
position: absolute;
|
|
108
|
-
top: calc(var(--bb-world-toolbar-top) + 48px);
|
|
109
|
-
right: var(--bb-world-right-inset);
|
|
110
|
-
min-width: 150px;
|
|
111
|
-
padding: 5px;
|
|
112
|
-
border: 1px solid rgb(13 25 46 / 18%);
|
|
113
|
-
border-radius: 9px;
|
|
114
|
-
background: #fff;
|
|
115
|
-
box-shadow: 0 8px 25px #0d192e22;
|
|
116
|
-
pointer-events: auto;
|
|
117
|
-
}
|
|
118
|
-
.bb-world-canvas-ui .bb-world-canvas-sky-option {
|
|
119
|
-
display: block;
|
|
120
|
-
width: 100%;
|
|
121
|
-
padding: 8px 10px;
|
|
122
|
-
border: 0;
|
|
123
|
-
border-radius: 5px;
|
|
124
|
-
background: transparent;
|
|
125
|
-
color: inherit;
|
|
126
|
-
font-size: 12px;
|
|
127
|
-
text-align: left;
|
|
128
|
-
}
|
|
129
|
-
.bb-world-canvas-ui .bb-world-canvas-sky-option:hover,
|
|
130
|
-
.bb-world-canvas-ui .bb-world-canvas-sky-option[aria-checked="true"] { background: #e5f8e1; }
|
|
106
|
+
.bb-world-canvas-ui .bb-world-canvas-sky-icon svg { display: block; width: 23px; height: 23px; }
|
|
131
107
|
|
|
132
108
|
.bb-world-canvas-ui .bb-world-canvas-quests {
|
|
133
109
|
position: absolute;
|
|
@@ -147,28 +123,7 @@
|
|
|
147
123
|
min-width: 0;
|
|
148
124
|
margin: 0;
|
|
149
125
|
padding: 7px 10px 7px 8px;
|
|
150
|
-
border: 1px solid rgb(255 255 255 / 18%);
|
|
151
|
-
border-left: 3px solid #9be891;
|
|
152
|
-
border-radius: 4px;
|
|
153
|
-
background: linear-gradient(110deg, rgb(13 25 46 / 88%), rgb(13 25 46 / 72%));
|
|
154
|
-
color: #fff;
|
|
155
|
-
font-size: 12px;
|
|
156
|
-
line-height: 18px;
|
|
157
126
|
box-shadow: 0 3px 12px #0d192e18;
|
|
158
|
-
backdrop-filter: blur(5px);
|
|
159
|
-
}
|
|
160
|
-
.bb-world-canvas-ui .bb-world-canvas-quest-symbol {
|
|
161
|
-
display: grid;
|
|
162
|
-
place-items: center;
|
|
163
|
-
flex: none;
|
|
164
|
-
width: 18px;
|
|
165
|
-
height: 18px;
|
|
166
|
-
border: 1px solid #9be89170;
|
|
167
|
-
border-radius: 3px;
|
|
168
|
-
background: #9be89112;
|
|
169
|
-
color: #b5f2ab;
|
|
170
|
-
font-size: 11px;
|
|
171
|
-
font-weight: 700;
|
|
172
127
|
}
|
|
173
128
|
.bb-world-canvas-ui .bb-world-canvas-quest-text { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
174
129
|
/* A quest with a way down (候補の確認, 区画の入口) is pressable as a whole row even though the quest list lets
|
|
@@ -181,7 +136,7 @@
|
|
|
181
136
|
}
|
|
182
137
|
.bb-world-canvas-ui .bb-world-canvas-quest.has-way:hover,
|
|
183
138
|
.bb-world-canvas-ui .bb-world-canvas-quest.has-way:focus-within {
|
|
184
|
-
border-color:
|
|
139
|
+
border-color: var(--bb-notification-accent);
|
|
185
140
|
background: linear-gradient(110deg, rgb(20 44 62 / 94%), rgb(20 44 62 / 84%));
|
|
186
141
|
}
|
|
187
142
|
.bb-world-canvas-ui .bb-world-canvas-quest a,
|
|
@@ -191,7 +146,7 @@
|
|
|
191
146
|
padding: 0;
|
|
192
147
|
border: 0;
|
|
193
148
|
background: none;
|
|
194
|
-
color:
|
|
149
|
+
color: var(--bb-notification-symbol-text);
|
|
195
150
|
font: inherit;
|
|
196
151
|
text-decoration: underline;
|
|
197
152
|
text-underline-offset: 2px;
|
|
@@ -205,7 +160,7 @@
|
|
|
205
160
|
.bb-world-canvas-ui .bb-world-canvas-quest a:focus-visible,
|
|
206
161
|
.bb-world-canvas-ui .bb-world-canvas-quest-action:focus-visible { outline: none; }
|
|
207
162
|
.bb-world-canvas-ui .bb-world-canvas-quest a:focus-visible::before,
|
|
208
|
-
.bb-world-canvas-ui .bb-world-canvas-quest-action:focus-visible::before { outline: 2px solid
|
|
163
|
+
.bb-world-canvas-ui .bb-world-canvas-quest-action:focus-visible::before { outline: 2px solid var(--bb-notification-symbol-text); outline-offset: 1px; }
|
|
209
164
|
/* A quest with nowhere to go: flat and dashed, so it reads as a sign, not a button, with its reason. */
|
|
210
165
|
.bb-world-canvas-ui .bb-world-canvas-quest.is-static {
|
|
211
166
|
border-style: dashed;
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* unanchored:true omits the pointer for a real record without a drawable lot.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { workspaceNotificationCard } from '../../workspace-kit.js';
|
|
9
|
+
|
|
8
10
|
const SKY_MODES = Object.freeze([
|
|
9
|
-
{ value: 'auto', label: '
|
|
10
|
-
{ value: 'day', label: '昼', icon: '
|
|
11
|
-
{ value: 'dusk', label: '夕方', icon: '
|
|
12
|
-
{ value: 'night', label: '夜', icon: '
|
|
11
|
+
{ value: 'auto', label: '自動(現在時刻)', icon: '<circle cx="12" cy="12" r="8.5"/><path d="M12 7.5v5l3 1.8"/>' },
|
|
12
|
+
{ value: 'day', label: '昼', icon: '<circle cx="12" cy="12" r="5" fill="currentColor" fill-opacity=".12"/><path d="M12 2v2m0 16v2M2 12h2m16 0h2M4.9 4.9l1.4 1.4m11.4 11.4 1.4 1.4M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/><path d="M10 12h.01M14 12h.01m-4 2.5q2 1.6 4 0"/>' },
|
|
13
|
+
{ value: 'dusk', label: '夕方', icon: '<path d="M5.5 13a6.5 6.5 0 0 1 13 0" fill="currentColor" fill-opacity=".12"/><path d="M12 2v2M3.5 5.5 5 7m15.5-1.5L19 7M2 13h20M3 17q3-2 6 0t6 0 6 0M6 21q3-2 6 0t6 0"/>' },
|
|
14
|
+
{ value: 'night', label: '夜', icon: '<path d="M19.5 15.5A8.5 8.5 0 0 1 8.5 4.5a8.5 8.5 0 1 0 11 11Z" fill="currentColor" fill-opacity=".12"/><path d="m18 3 .7 2.3L21 6l-2.3.7L18 9l-.7-2.3L15 6l2.3-.7ZM22 11h.01"/>' },
|
|
13
15
|
]);
|
|
14
16
|
const CLOSE_DELAY = 220;
|
|
15
17
|
let instanceId = 0;
|
|
@@ -57,7 +59,6 @@ export function createWorldCanvasUI({
|
|
|
57
59
|
let taskOpen = false;
|
|
58
60
|
let taskAnchor = null;
|
|
59
61
|
let helpOpen = false;
|
|
60
|
-
let skyOpen = false;
|
|
61
62
|
let skyMode = 'auto';
|
|
62
63
|
let businesses = new Set();
|
|
63
64
|
let overlayOrder = [];
|
|
@@ -138,10 +139,7 @@ export function createWorldCanvasUI({
|
|
|
138
139
|
const reset = button('bb-world-canvas-control bb-world-canvas-reset', '世界全体に戻る', '全体に戻る');
|
|
139
140
|
const city = element('select', 'bb-world-canvas-control bb-world-canvas-city', null, { 'aria-label': '都市を選ぶ' });
|
|
140
141
|
const sky = button('bb-world-canvas-control bb-world-canvas-sky', '空:時刻に合わせる', '');
|
|
141
|
-
|
|
142
|
-
sky.setAttribute('aria-expanded', 'false');
|
|
143
|
-
sky.setAttribute('aria-controls', `${id}-sky`);
|
|
144
|
-
const skyIcon = element('span', 'bb-world-canvas-sky-icon', SKY_MODES[0].icon, { 'aria-hidden': 'true' });
|
|
142
|
+
const skyIcon = element('span', 'bb-world-canvas-sky-icon', '', { 'aria-hidden': 'true' });
|
|
145
143
|
sky.append(skyIcon);
|
|
146
144
|
toolbar.append(reset, city, sky);
|
|
147
145
|
const contextRoot = element('div', 'bb-world-canvas-context', null, {
|
|
@@ -153,22 +151,7 @@ export function createWorldCanvasUI({
|
|
|
153
151
|
const contextNextAction = element('p', 'bb-world-canvas-context-next-action', worldContext.unavailableCity, {
|
|
154
152
|
'data-world-context': 'next-action',
|
|
155
153
|
});
|
|
156
|
-
contextRoot.append(
|
|
157
|
-
const skyMenu = element('div', 'bb-world-canvas-sky-menu', null, { id: `${id}-sky`, role: 'menu', 'aria-label': '空の時間帯' });
|
|
158
|
-
skyMenu.hidden = true;
|
|
159
|
-
const skyOptions = SKY_MODES.map((mode) => {
|
|
160
|
-
const option = button('bb-world-canvas-sky-option', mode.label);
|
|
161
|
-
option.setAttribute('role', 'menuitemradio');
|
|
162
|
-
option.setAttribute('aria-checked', String(mode.value === skyMode));
|
|
163
|
-
option.setAttribute('tabindex', '-1');
|
|
164
|
-
listen(option, 'click', () => {
|
|
165
|
-
setSky(mode.value);
|
|
166
|
-
closeSky(true);
|
|
167
|
-
onSky?.(mode.value);
|
|
168
|
-
});
|
|
169
|
-
skyMenu.append(option);
|
|
170
|
-
return option;
|
|
171
|
-
});
|
|
154
|
+
contextRoot.append(contextNextAction);
|
|
172
155
|
const quests = element('div', 'bb-world-canvas-quests', null, { 'aria-live': 'polite', 'aria-label': '確認が必要なこと' });
|
|
173
156
|
|
|
174
157
|
const edge = button('bb-world-canvas-edge', '都市の詳細を開く', '');
|
|
@@ -203,7 +186,7 @@ export function createWorldCanvasUI({
|
|
|
203
186
|
const notes = element('div', 'bb-world-canvas-notes');
|
|
204
187
|
helpBody.append(helpContent, notes);
|
|
205
188
|
help.append(helpTop, helpBody);
|
|
206
|
-
root.append(toolbar, contextRoot,
|
|
189
|
+
root.append(toolbar, contextRoot, quests, edge, rail, task, helpButton, help);
|
|
207
190
|
stage.append(root);
|
|
208
191
|
|
|
209
192
|
function setCities(items = [], selectedCode = null) {
|
|
@@ -222,6 +205,7 @@ export function createWorldCanvasUI({
|
|
|
222
205
|
}
|
|
223
206
|
city.replaceChildren(...options);
|
|
224
207
|
city.disabled = businesses.size === 0;
|
|
208
|
+
contextRoot.hidden = businesses.size > 0;
|
|
225
209
|
contextNextAction.textContent = businesses.size === 0
|
|
226
210
|
? worldContext.unavailableCity
|
|
227
211
|
: worldContext.knownCityAction;
|
|
@@ -236,27 +220,12 @@ export function createWorldCanvasUI({
|
|
|
236
220
|
const match = SKY_MODES.find((entry) => entry.value === mode);
|
|
237
221
|
if (!match) return;
|
|
238
222
|
skyMode = mode;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (disposed) return;
|
|
246
|
-
skyOpen = true;
|
|
247
|
-
skyMenu.hidden = false;
|
|
248
|
-
sky.setAttribute('aria-expanded', 'true');
|
|
249
|
-
activate('sky', skyMenu);
|
|
250
|
-
focus(skyOptions[SKY_MODES.findIndex((entry) => entry.value === skyMode)]);
|
|
251
|
-
}
|
|
252
|
-
function closeSky(restoreFocus = false) {
|
|
253
|
-
if (!skyOpen) return;
|
|
254
|
-
const shouldRestore = restoreFocus && focusedWithin(skyMenu);
|
|
255
|
-
skyOpen = false;
|
|
256
|
-
skyMenu.hidden = true;
|
|
257
|
-
sky.setAttribute('aria-expanded', 'false');
|
|
258
|
-
forget('sky');
|
|
259
|
-
if (shouldRestore) focus(sky);
|
|
223
|
+
// Artwork is fixed code, never host data. Native button activation covers Enter/Space.
|
|
224
|
+
skyIcon.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${match.icon}</svg>`;
|
|
225
|
+
const next = SKY_MODES[(SKY_MODES.indexOf(match) + 1) % SKY_MODES.length];
|
|
226
|
+
const label = `空:${match.label}。次は${next.label}`;
|
|
227
|
+
sky.setAttribute('aria-label', label);
|
|
228
|
+
sky.setAttribute('title', label);
|
|
260
229
|
}
|
|
261
230
|
function cancelRailTimer() {
|
|
262
231
|
if (railTimer != null) clearTimeout(railTimer);
|
|
@@ -397,10 +366,10 @@ export function createWorldCanvasUI({
|
|
|
397
366
|
// A quest is a way down (a link already checked by the caller, or an action in this page) and then the
|
|
398
367
|
// whole row is pressable; without one it is drawn flat, so it never looks pressable (story-world-clickable-honesty-v1).
|
|
399
368
|
const way = link?.href ? 'link' : typeof action?.onClick === 'function' ? 'action' : null;
|
|
400
|
-
const quest =
|
|
401
|
-
const icon =
|
|
369
|
+
const quest = workspaceNotificationCard(doc, { tag: 'p', tone, className: `bb-world-canvas-quest ${way ? 'has-way' : 'is-static'}` });
|
|
370
|
+
const icon = ['attention', 'warning', 'danger'].includes(tone) ? '!' : 'i';
|
|
402
371
|
quest.append(
|
|
403
|
-
element('span', 'bb-world-canvas-quest-symbol', icon, { 'aria-hidden': 'true' }),
|
|
372
|
+
element('span', 'bb-world-canvas-quest-symbol bb-ws-notification-symbol', icon, { 'aria-hidden': 'true' }),
|
|
404
373
|
element('span', 'bb-world-canvas-quest-text', summary.trim()),
|
|
405
374
|
);
|
|
406
375
|
// The accessible name contains the unabridged summary even when visual text
|
|
@@ -428,38 +397,23 @@ export function createWorldCanvasUI({
|
|
|
428
397
|
// A selected task outside the camera is not a visible overlay to dismiss.
|
|
429
398
|
const name = [...overlayOrder].reverse().find((entry) => entry !== 'task' || !task.hidden);
|
|
430
399
|
if (!name) return false;
|
|
431
|
-
if (name === '
|
|
432
|
-
else if (name === 'help') closeHelp();
|
|
400
|
+
if (name === 'help') closeHelp();
|
|
433
401
|
else if (name === 'rail') closeRail();
|
|
434
402
|
else if (name === 'task') dismissTask();
|
|
435
403
|
return true;
|
|
436
404
|
}
|
|
437
405
|
|
|
438
406
|
listen(reset, 'click', () => {
|
|
439
|
-
closeSky();
|
|
440
407
|
closeHelp();
|
|
441
408
|
closeRail();
|
|
442
409
|
closeTask();
|
|
443
410
|
onReset?.();
|
|
444
411
|
});
|
|
445
412
|
listen(city, 'change', () => { if (businesses.has(city.value)) onCity?.(city.value); });
|
|
446
|
-
listen(sky, 'click', () => {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
listen(skyMenu, 'keydown', (event) => {
|
|
451
|
-
let index = skyOptions.indexOf(doc.activeElement);
|
|
452
|
-
if (event.key === 'ArrowDown') index = (index + 1) % skyOptions.length;
|
|
453
|
-
else if (event.key === 'ArrowUp') index = (index - 1 + skyOptions.length) % skyOptions.length;
|
|
454
|
-
else if (event.key === 'Home') index = 0;
|
|
455
|
-
else if (event.key === 'End') index = skyOptions.length - 1;
|
|
456
|
-
else if (event.key === 'Tab') { closeSky(true); return; }
|
|
457
|
-
else return;
|
|
458
|
-
event.preventDefault();
|
|
459
|
-
focus(skyOptions[index]);
|
|
460
|
-
});
|
|
461
|
-
listen(skyMenu, 'focusout', (event) => {
|
|
462
|
-
if (!skyMenu.contains(event.relatedTarget) && event.relatedTarget !== sky) closeSky();
|
|
413
|
+
listen(sky, 'click', () => {
|
|
414
|
+
const next = SKY_MODES[(SKY_MODES.findIndex(entry => entry.value === skyMode) + 1) % SKY_MODES.length];
|
|
415
|
+
setSky(next.value);
|
|
416
|
+
onSky?.(next.value);
|
|
463
417
|
});
|
|
464
418
|
for (const target of [edge, rail]) {
|
|
465
419
|
listen(target, 'pointerenter', railEnter);
|
|
@@ -493,10 +447,11 @@ export function createWorldCanvasUI({
|
|
|
493
447
|
observer?.observe(stage);
|
|
494
448
|
observer?.observe(taskBody);
|
|
495
449
|
setCities([]);
|
|
450
|
+
setSky(skyMode);
|
|
496
451
|
|
|
497
452
|
return {
|
|
498
453
|
setCities, setCity, setSky, showRail, openRail, showTask, closeTask, setTaskAnchor,
|
|
499
|
-
setHelp(nodes) { if (!disposed) helpContent.replaceChildren(...nodeList(nodes)); },
|
|
454
|
+
setHelp(nodes) { if (!disposed) helpContent.replaceChildren(contextPurpose, element('p', '', worldContext.knownCityAction), ...nodeList(nodes)); },
|
|
500
455
|
addNote, clearNotes, dismissOverlay,
|
|
501
456
|
dispose() {
|
|
502
457
|
if (disposed) return;
|