@unson/brainbase-mcp 0.41.11 → 0.42.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/README.md +1 -1
- package/contracts/judgment-dag/digest.json +2 -2
- package/dist/local-web-host.d.ts +6 -0
- package/dist/local-web-host.js +54 -3
- package/dist/personal-memory.d.ts +101 -0
- package/dist/personal-memory.js +204 -0
- package/dist/ssot.d.ts +3 -1
- package/dist/ssot.js +4 -0
- package/package.json +13 -2
- package/ui/area-nav.css +79 -0
- package/ui/area-nav.js +249 -0
- package/ui/foundation-overview.css +9 -0
- package/ui/foundation-overview.js +75 -5
- package/ui/graph-ontology-view.css +88 -0
- package/ui/graph-ontology-view.js +248 -0
- package/ui/graph-own-share.js +167 -0
- package/ui/graph-registry-view.css +272 -50
- package/ui/graph-registry-view.js +539 -456
- package/ui/graph-view-shared.css +32 -0
- package/ui/graph-view-shared.js +191 -12
- package/ui/judgment-history.js +74 -3
- package/ui/local-web-shell.css +1 -13
- package/ui/local-web-shell.js +119 -41
- package/ui/outcome-knowledge.js +6 -2
- package/ui/people-and-companies-view.css +166 -15
- package/ui/people-and-companies-view.js +307 -93
- package/ui/personal-memory-http-port.js +72 -0
- package/ui/personal-memory.css +254 -0
- package/ui/personal-memory.js +677 -0
- package/ui/world/world-canvas-notes.js +3 -2
- package/ui/world/world-view.css +19 -0
- package/ui/world/world-view.js +52 -10
- package/ui/world/world-work-rail.js +114 -8
package/ui/local-web-shell.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { createGraphProjectsView } from './graph-projects-view.js';
|
|
19
19
|
import { createGraphRegistryView } from './graph-registry-view.js';
|
|
20
|
+
import { createGraphOntologyView } from './graph-ontology-view.js';
|
|
20
21
|
import { createPeopleAndCompaniesView } from './people-and-companies-view.js';
|
|
21
22
|
import { createObjectiveEditorController } from './objective-editor.js';
|
|
22
23
|
import { createObjectiveEditorHttpPort } from './objective-editor-http-port.js';
|
|
@@ -25,8 +26,11 @@ import { createMeetingMinutesUI } from './meeting-minutes.js';
|
|
|
25
26
|
import { createMeetingMinutesStorageExtension } from './meeting-minutes-storage.js';
|
|
26
27
|
import { createMeetingMinutesLineageExtension } from './meeting-minutes-lineage-http.js';
|
|
27
28
|
import { createWorldModelView } from './world-model-view.js';
|
|
29
|
+
import { createPersonalMemoryUI, parsePersonalMemoryRoute, personalMemoryRouteTarget } from './personal-memory.js';
|
|
30
|
+
import { createPersonalMemoryHttpPort } from './personal-memory-http-port.js';
|
|
28
31
|
import { createGraphClient } from './graph-view-shared.js';
|
|
29
32
|
import { installNativeScrollbars } from './native-scrollbars.js';
|
|
33
|
+
import { browserAreaStorage, createAreaMemory, createAreaNav } from './area-nav.js';
|
|
30
34
|
|
|
31
35
|
export const LOCAL_WEB_SHELL_CONTRACT_VERSION = 'brainbase.local-web-shell.v1';
|
|
32
36
|
|
|
@@ -51,22 +55,32 @@ const NAV_ICON_PATHS = Object.freeze({
|
|
|
51
55
|
minutes: ['M6 3.5h9l3 3V20.5H6z', 'M15 3.5v4h3', 'M9 12h6m-6 3h6'],
|
|
52
56
|
// Two people (the organization edition's 人と会社 uses the same shape).
|
|
53
57
|
people: ['M9 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z', 'M2.5 20a6.5 6.5 0 0 1 13 0', 'M16 4.5a3.5 3.5 0 0 1 0 6.5', 'M18 14a6.5 6.5 0 0 1 3.5 6'],
|
|
58
|
+
// あなたの記憶 (ledger D15): one person and a lock, seen only by the owner.
|
|
59
|
+
memory: ['M10 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z', 'M3.5 20a6.5 6.5 0 0 1 10-5.5', 'M15 15h6v5h-6z', 'M16.5 15v-1.5a1.5 1.5 0 0 1 3 0V15'],
|
|
60
|
+
// Kinds of information and how they connect (settings, ledger D14): three linked boxes.
|
|
61
|
+
ontology: ['M4 4h6v6H4z', 'M14 14h6v6h-6z', 'M14 4h6v6h-6z', 'M10 7h4', 'M17 10v4'],
|
|
54
62
|
// World view (extension id `world`, ledger P17).
|
|
55
63
|
world: ['M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z', 'M3 12h18', 'M12 3c2.5 2.6 3.8 5.6 3.8 9s-1.3 6.4-3.8 9c-2.5-2.6-3.8-5.6-3.8-9S9.5 5.6 12 3z'],
|
|
64
|
+
// Areas whose screens have their own icons (ledger D10, stage 2): a briefcase, and
|
|
65
|
+
// the organization edition's gear.
|
|
66
|
+
'area:work': ['M4 8h16v11H4z', 'M9 8V5h6v3', 'M4 13h16'],
|
|
67
|
+
'area:settings': ['M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915', 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z'],
|
|
56
68
|
});
|
|
57
69
|
// Screens without their own icon (for example a private product's extension).
|
|
58
70
|
const DEFAULT_NAV_ICON_PATHS = Object.freeze(['M5 20V10m7 10V4m7 16v-7']);
|
|
59
71
|
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
60
72
|
|
|
61
|
-
// The menu's areas (brainbase-project ledger D10): the same order and
|
|
62
|
-
//
|
|
63
|
-
//
|
|
73
|
+
// The menu's areas (brainbase-project ledger D10): the same order and names as
|
|
74
|
+
// the organization edition. The left side lists only the areas; the screens of
|
|
75
|
+
// the current area are tabs above the page (stage 2). 目的と現状 is the only
|
|
76
|
+
// screen of its area and keeps its own name. Inside an area, `order` lists the
|
|
77
|
+
// screens first in that order; `defaultScreen` opens when nothing is remembered.
|
|
64
78
|
const NAV_AREAS = Object.freeze([
|
|
65
|
-
Object.freeze({ id: 'purpose', label: null, order: ['objectives'] }),
|
|
66
|
-
Object.freeze({ id: 'work', label: '仕事', order: ['projects', 'minutes'] }),
|
|
67
|
-
Object.freeze({ id: 'outcomes', label: '成果', order: ['value-proofs'] }),
|
|
68
|
-
Object.freeze({ id: 'information', label: '情報と関係', order: ['graph', 'people'] }),
|
|
69
|
-
Object.freeze({ id: 'settings', label: '設定', order: [] }),
|
|
79
|
+
Object.freeze({ id: 'purpose', label: null, order: ['objectives'], icon: 'objectives' }),
|
|
80
|
+
Object.freeze({ id: 'work', label: '仕事', order: ['projects', 'minutes'], defaultScreen: 'projects', icon: 'area:work' }),
|
|
81
|
+
Object.freeze({ id: 'outcomes', label: '成果', order: ['value-proofs'], icon: 'value-proofs' }),
|
|
82
|
+
Object.freeze({ id: 'information', label: '情報と関係', order: ['graph', 'people', 'memory'], defaultScreen: 'graph', icon: 'graph' }),
|
|
83
|
+
Object.freeze({ id: 'settings', label: '設定', order: ['ontology'], defaultScreen: 'ontology', icon: 'area:settings' }),
|
|
70
84
|
]);
|
|
71
85
|
|
|
72
86
|
// Menu order only: the screens' own order (and so the first screen) stays as given.
|
|
@@ -88,6 +102,23 @@ function navLayout(screens) {
|
|
|
88
102
|
return { leading, areas, trailing };
|
|
89
103
|
}
|
|
90
104
|
|
|
105
|
+
// Every menu line is an area: a screen without an area is an area of its own.
|
|
106
|
+
function navAreas(screens) {
|
|
107
|
+
const layout = navLayout(screens);
|
|
108
|
+
const own = (screen) => ({ id: screen.id, label: null, screenIds: [screen.id], icon: screen.id });
|
|
109
|
+
return [
|
|
110
|
+
...layout.leading.map(own),
|
|
111
|
+
...layout.areas.map(({ area, screens: members }) => ({
|
|
112
|
+
id: area.id,
|
|
113
|
+
label: area.label,
|
|
114
|
+
defaultScreen: area.defaultScreen,
|
|
115
|
+
screenIds: members.map((screen) => screen.id),
|
|
116
|
+
icon: area.icon,
|
|
117
|
+
})),
|
|
118
|
+
...layout.trailing.map(own),
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
|
|
91
122
|
function makeIcon(doc, id) {
|
|
92
123
|
const paths = NAV_ICON_PATHS[id] ?? DEFAULT_NAV_ICON_PATHS;
|
|
93
124
|
const create = (tag) => (typeof doc.createElementNS === 'function' ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag));
|
|
@@ -136,11 +167,14 @@ export function parseLocalWebTarget(target) {
|
|
|
136
167
|
// プロジェクトと関係者 one project (`project` = Graph project id); other screens ignore them.
|
|
137
168
|
const decisionId = screenId === 'value-proofs' ? params.get('decision')?.trim() || null : null;
|
|
138
169
|
const projectId = screenId === 'projects' ? params.get('project')?.trim() || null : null;
|
|
170
|
+
// あなたの記憶 keeps its filters and the opened memory (never the search words).
|
|
171
|
+
const memoryRoute = screenId === 'memory' ? parsePersonalMemoryRoute(params) : null;
|
|
139
172
|
return {
|
|
140
173
|
screenId,
|
|
141
174
|
entityId: screenId === 'graph' ? params.get('entity_id')?.trim() || null : null,
|
|
142
175
|
...(decisionId ? { decisionId } : {}),
|
|
143
176
|
...(projectId ? { projectId } : {}),
|
|
177
|
+
...(memoryRoute ? { memoryRoute } : {}),
|
|
144
178
|
};
|
|
145
179
|
}
|
|
146
180
|
|
|
@@ -201,6 +235,26 @@ function judgmentReferenceLabelReader(context) {
|
|
|
201
235
|
};
|
|
202
236
|
}
|
|
203
237
|
|
|
238
|
+
function personalMemoryPort(context) {
|
|
239
|
+
return createPersonalMemoryHttpPort({ fetcher: context.fetcher, token: context.token });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** The opening of each used memory, for 判断の履歴's 本人の考え; the local host has one owner. */
|
|
243
|
+
function personalMemoryResolver(context) {
|
|
244
|
+
const port = personalMemoryPort(context);
|
|
245
|
+
return async (refs) => {
|
|
246
|
+
const list = await port.listMemories({});
|
|
247
|
+
if (list.status !== 'ok') throw new Error(list.reason);
|
|
248
|
+
const wanted = new Set(refs);
|
|
249
|
+
const found = new Map();
|
|
250
|
+
for (const item of list.items) {
|
|
251
|
+
if (!wanted.has(item.id)) continue;
|
|
252
|
+
found.set(item.id, { excerpt: item.text, href: `#${personalMemoryRouteTarget({ memoryId: item.id })}` });
|
|
253
|
+
}
|
|
254
|
+
return found;
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
204
258
|
function mountToday(container, context) {
|
|
205
259
|
return createJudgmentHistoryUI({
|
|
206
260
|
root: makePage(container, context),
|
|
@@ -211,6 +265,23 @@ function mountToday(container, context) {
|
|
|
211
265
|
token: context.token,
|
|
212
266
|
basePath: '/api/judgment-history',
|
|
213
267
|
resolveReferenceLabels: judgmentReferenceLabelReader(context),
|
|
268
|
+
resolvePersonalMemory: personalMemoryResolver(context),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function mountPersonalMemory(container, context) {
|
|
273
|
+
return createPersonalMemoryUI({
|
|
274
|
+
root: makePage(container, context),
|
|
275
|
+
rail: context.rail,
|
|
276
|
+
page: context.page,
|
|
277
|
+
document: context.document,
|
|
278
|
+
port: personalMemoryPort(context),
|
|
279
|
+
judgmentHref: (decisionId) => `#value-proofs?decision=${encodeURIComponent(decisionId)}`,
|
|
280
|
+
candidatesCommand: 'brainbase onboard:candidates',
|
|
281
|
+
route: context.initialEntityId ?? undefined,
|
|
282
|
+
onRouteChange: context.navigate
|
|
283
|
+
? (route, options) => context.navigate(personalMemoryRouteTarget(route), options)
|
|
284
|
+
: undefined,
|
|
214
285
|
});
|
|
215
286
|
}
|
|
216
287
|
|
|
@@ -299,7 +370,7 @@ function mountGraphScreen(screenId, createView, extra = {}) {
|
|
|
299
370
|
fetcher: context.fetcher,
|
|
300
371
|
token: context.token,
|
|
301
372
|
initialEntityId: context.initialEntityId,
|
|
302
|
-
...extra,
|
|
373
|
+
...(typeof extra === 'function' ? extra(context) : extra),
|
|
303
374
|
});
|
|
304
375
|
};
|
|
305
376
|
}
|
|
@@ -309,8 +380,15 @@ export const LOCAL_WEB_SCREENS = Object.freeze([
|
|
|
309
380
|
Object.freeze({ id: 'minutes', label: '議事録', area: 'work', usesGraph: false, rail: true, source: '正本の議事録', mount: mountMeetingMinutes }),
|
|
310
381
|
Object.freeze({ id: 'objectives', label: '目的と現状', area: 'purpose', usesGraph: true, rail: true, source: '手元のGraph', mount: mountObjectives }),
|
|
311
382
|
Object.freeze({ id: 'projects', label: 'プロジェクトと関係者', area: 'work', usesGraph: true, rail: true, source: '手元のGraph', mount: mountGraphScreen('projects', createGraphProjectsView) }),
|
|
312
|
-
|
|
383
|
+
// A record of 情報と関係 opens as its own screen at `#graph?entity_id=…`; the list is `#graph`.
|
|
384
|
+
Object.freeze({ id: 'graph', label: '情報と関係', area: 'information', usesGraph: true, rail: false, source: '手元のGraph', mount: mountGraphScreen('graph', createGraphRegistryView, (context) => ({
|
|
385
|
+
onRouteChange: (entityId, options) => context.navigate?.(entityId ? `graph?entity_id=${encodeURIComponent(entityId)}` : 'graph', options),
|
|
386
|
+
})) }),
|
|
313
387
|
Object.freeze({ id: 'people', label: '人と会社', area: 'information', usesGraph: true, rail: true, source: '手元のGraph', mount: mountGraphScreen('people', createPeopleAndCompaniesView, { canCorrect: true }) }),
|
|
388
|
+
// Only the owner sees it (ledger D15); the local host has one owner, so it is always listed here.
|
|
389
|
+
Object.freeze({ id: 'memory', label: 'あなたの記憶', area: 'information', usesGraph: false, rail: true, ownerOnly: true, source: '手元の個人KG', mount: mountPersonalMemory }),
|
|
390
|
+
// The definitions behind 情報と関係: infrequent management work, so under settings (ledger D14).
|
|
391
|
+
Object.freeze({ id: 'ontology', label: '情報の種類とつながり方', area: 'settings', usesGraph: true, rail: false, source: '手元のGraph', mount: mountGraphScreen('ontology', createGraphOntologyView, { canCorrect: true }) }),
|
|
314
392
|
]);
|
|
315
393
|
|
|
316
394
|
function renderGraphGate(doc, slot, status, onRecheck, label) {
|
|
@@ -394,19 +472,26 @@ export function createLocalWebShell({
|
|
|
394
472
|
initialScreen,
|
|
395
473
|
meetingMinutesStorage = null,
|
|
396
474
|
meetingMinutesLineage = null,
|
|
475
|
+
areaMemory = createAreaMemory(browserAreaStorage()),
|
|
476
|
+
onNavigate,
|
|
477
|
+
// A host that serves more than one person passes false for anyone but the owner:
|
|
478
|
+
// owner-only screens (あなたの記憶) are then neither listed nor mounted.
|
|
479
|
+
isOwner = true,
|
|
397
480
|
} = {}) {
|
|
398
481
|
if (!root) throw new TypeError('root is required');
|
|
399
482
|
const doc = explicitDocument ?? (typeof document === 'undefined' ? null : document);
|
|
400
483
|
if (!doc || typeof doc.createElement !== 'function') throw new Error('document_unavailable');
|
|
484
|
+
screens = screens.filter((screen) => isOwner === true || screen.ownerOnly !== true);
|
|
401
485
|
const request = typeof fetcher === 'function'
|
|
402
486
|
? fetcher
|
|
403
487
|
: typeof globalThis.fetch === 'function' ? (path, init) => globalThis.fetch(path, init) : null;
|
|
404
|
-
|
|
488
|
+
// `navigate(target, { back })` lets a screen give its own state an address (the host's hash).
|
|
489
|
+
const navigate = typeof onNavigate === 'function' ? (target, options = {}) => onNavigate(target, options) : null;
|
|
490
|
+
const context = Object.freeze({ document: doc, fetcher: request, token, meetingMinutesStorage, meetingMinutesLineage, navigate });
|
|
405
491
|
|
|
406
492
|
const status = { phase: 'loading', data: null, error: null };
|
|
407
493
|
const mounted = new Map();
|
|
408
494
|
const slots = new Map();
|
|
409
|
-
const links = new Map();
|
|
410
495
|
const pendingEntityIds = new Map();
|
|
411
496
|
let active = null;
|
|
412
497
|
|
|
@@ -427,37 +512,27 @@ export function createLocalWebShell({
|
|
|
427
512
|
};
|
|
428
513
|
menuButton.addEventListener('click', () => setMenuOpen(!sidebar.className.includes('is-open')));
|
|
429
514
|
backdrop.addEventListener('click', () => setMenuOpen(false));
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
515
|
+
const areaList = navAreas(screens);
|
|
516
|
+
const areaNav = createAreaNav({
|
|
517
|
+
document: doc,
|
|
518
|
+
areas: areaList,
|
|
519
|
+
screens: screens.map((screen) => ({ id: screen.id, label: screen.label })),
|
|
520
|
+
memory: areaMemory,
|
|
521
|
+
linkClassName: 'bb-shell-nav-link',
|
|
522
|
+
iconFor: (area) => makeIcon(doc, areaList.find((entry) => entry.id === area.id)?.icon ?? area.id),
|
|
523
|
+
onSelect: (screenId) => {
|
|
435
524
|
setMenuOpen(false);
|
|
436
|
-
controller.show(
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
};
|
|
441
|
-
const layout = navLayout(screens);
|
|
442
|
-
for (const screen of layout.leading) nav.append(navLink(screen));
|
|
443
|
-
for (const { area, screens: members } of layout.areas) {
|
|
444
|
-
if (!area.label) {
|
|
445
|
-
for (const screen of members) nav.append(navLink(screen));
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
const headId = `bb-shell-nav-area-${area.id}`;
|
|
449
|
-
const group = makeElement(doc, 'div', { className: 'bb-shell-nav-group', attrs: { role: 'group', 'aria-labelledby': headId } });
|
|
450
|
-
group.append(makeElement(doc, 'div', { className: 'bb-shell-nav-group-head', text: area.label, attrs: { id: headId } }));
|
|
451
|
-
for (const screen of members) group.append(navLink(screen));
|
|
452
|
-
nav.append(group);
|
|
453
|
-
}
|
|
454
|
-
for (const screen of layout.trailing) nav.append(navLink(screen));
|
|
525
|
+
controller.show(screenId);
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
nav.append(areaNav.nav);
|
|
455
529
|
const source = makeElement(doc, 'section', { className: 'bb-shell-source', attrs: { 'aria-label': '出典' } });
|
|
456
530
|
sidebar.append(brand, nav, source);
|
|
457
531
|
const workspace = makeElement(doc, 'div', { className: 'bb-shell-workspace' });
|
|
458
532
|
const mobileBar = makeElement(doc, 'div', { className: 'bb-shell-mobile-bar' });
|
|
459
533
|
mobileBar.append(makeElement(doc, 'strong', { text: 'Brainbase' }), menuButton);
|
|
460
534
|
const main = makeElement(doc, 'main', { className: 'bb-shell-main' });
|
|
535
|
+
main.append(areaNav.tabs);
|
|
461
536
|
for (const screen of screens) {
|
|
462
537
|
const slot = makeElement(doc, 'section', { className: 'bb-shell-screen', attrs: { 'data-screen': screen.id, 'aria-label': screen.label } });
|
|
463
538
|
slot.hidden = true;
|
|
@@ -493,8 +568,11 @@ export function createLocalWebShell({
|
|
|
493
568
|
if (mounted.has(screen.id)) {
|
|
494
569
|
const view = mounted.get(screen.id);
|
|
495
570
|
if (entityId && screen.id === 'graph' && typeof view?.openEntity === 'function') void view.openEntity(entityId);
|
|
571
|
+
// `#graph` without a record is the list: a record still open there closes (the browser's back).
|
|
572
|
+
if (!entityId && screen.id === 'graph' && view?.state?.detail && typeof view.closeEntity === 'function') view.closeEntity();
|
|
496
573
|
if (entityId && screen.id === 'value-proofs' && typeof view?.openDecision === 'function') view.openDecision(entityId);
|
|
497
574
|
if (entityId && screen.id === 'projects' && typeof view?.openProject === 'function') void view.openProject(entityId);
|
|
575
|
+
if (entityId && screen.id === 'memory' && typeof view?.applyRoute === 'function') view.applyRoute(entityId);
|
|
498
576
|
return;
|
|
499
577
|
}
|
|
500
578
|
const slot = slots.get(screen.id);
|
|
@@ -508,6 +586,7 @@ export function createLocalWebShell({
|
|
|
508
586
|
const page = Object.freeze({ crumbs: Object.freeze(['あなたのBrainbase', screen.label]), source: screen.source ?? null });
|
|
509
587
|
const view = screen.mount(slot, Object.freeze({ ...context, rail, page, initialEntityId: entityId })) ?? true;
|
|
510
588
|
mounted.set(screen.id, view);
|
|
589
|
+
if (screen.id === 'memory' && typeof view?.load === 'function') void view.load();
|
|
511
590
|
if (entityId && screen.id === 'value-proofs' && typeof view?.openDecision === 'function') view.openDecision(entityId);
|
|
512
591
|
if (entityId && screen.id === 'projects' && typeof view?.openProject === 'function') void view.openProject(entityId);
|
|
513
592
|
updateRail();
|
|
@@ -523,13 +602,12 @@ export function createLocalWebShell({
|
|
|
523
602
|
const screen = screens.find((candidate) => candidate.id === parsed.screenId) ?? screens[0];
|
|
524
603
|
pendingEntityIds.set(screen.id, screen.id === 'graph' ? parsed.entityId
|
|
525
604
|
: screen.id === 'value-proofs' ? parsed.decisionId ?? null
|
|
526
|
-
: screen.id === 'projects' ? parsed.projectId ?? null
|
|
605
|
+
: screen.id === 'projects' ? parsed.projectId ?? null
|
|
606
|
+
// あなたの記憶 receives its whole route (filters and the opened memory).
|
|
607
|
+
: screen.id === 'memory' ? parsed.memoryRoute ?? null : null);
|
|
527
608
|
active = screen.id;
|
|
528
609
|
for (const [screenId, slot] of slots) slot.hidden = screenId !== screen.id;
|
|
529
|
-
|
|
530
|
-
if (screenId === screen.id) link.setAttribute('aria-current', 'page');
|
|
531
|
-
else link.removeAttribute('aria-current');
|
|
532
|
-
}
|
|
610
|
+
areaNav.show(screen.id);
|
|
533
611
|
ensureMounted(screen, pendingEntityIds.get(screen.id));
|
|
534
612
|
updateRail();
|
|
535
613
|
return controller;
|
package/ui/outcome-knowledge.js
CHANGED
|
@@ -84,6 +84,9 @@ const SCOPE_LABELS = Object.freeze({
|
|
|
84
84
|
|
|
85
85
|
const LIFECYCLE_STATUS_LABELS = Object.freeze({
|
|
86
86
|
active: '有効',
|
|
87
|
+
decided: '決定済み',
|
|
88
|
+
active_reference: '参照用',
|
|
89
|
+
unknown: UNKNOWN,
|
|
87
90
|
inactive: '無効',
|
|
88
91
|
retired: '失効',
|
|
89
92
|
superseded: '置換済み',
|
|
@@ -1480,7 +1483,8 @@ export function createKnowledgeOverviewController(options = {}) {
|
|
|
1480
1483
|
const { root, api, onSelectProject } = options;
|
|
1481
1484
|
if (!root || typeof root.replaceChildren !== 'function') throw new Error('knowledge_root_unavailable');
|
|
1482
1485
|
const labels = resolveKnowledgeUiLabels(options.labels);
|
|
1483
|
-
|
|
1486
|
+
// A host path may already carry conditions (for example `?limit=100`); keep them and add the filters.
|
|
1487
|
+
const [itemsBase, itemsBaseQuery = ''] = (optionalText(options.itemsPath).trim() || `/api/knowledge/${KNOWLEDGE_ROUTES.items}`).split(/\?(.*)/s);
|
|
1484
1488
|
const state = {
|
|
1485
1489
|
filters: { q: '', scope: 'all', status: 'active' },
|
|
1486
1490
|
list: { state: 'loading', records: null, absence_confirmed: false, searched_scope: null },
|
|
@@ -1491,7 +1495,7 @@ export function createKnowledgeOverviewController(options = {}) {
|
|
|
1491
1495
|
const projectName = (code) => projectOptions(options.projects).find((option) => option.code === code)?.name ?? null;
|
|
1492
1496
|
|
|
1493
1497
|
function itemsPath() {
|
|
1494
|
-
const query = new URLSearchParams();
|
|
1498
|
+
const query = new URLSearchParams(itemsBaseQuery);
|
|
1495
1499
|
if (state.filters.q) query.set('q', state.filters.q);
|
|
1496
1500
|
if (state.filters.scope) query.set('scope', state.filters.scope);
|
|
1497
1501
|
if (state.filters.status) query.set('status', state.filters.status);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* 「人と会社」. Load after workspace-kit.css and graph-view-shared.css.
|
|
3
3
|
* Column templates of the people and company ledgers, the group titles,
|
|
4
|
-
* the
|
|
5
|
-
*
|
|
4
|
+
* the add buttons, the recorded company note, and a record's page (the way
|
|
5
|
+
* back, its head, the actions gathered under it and its relations), with a
|
|
6
|
+
* 760px variant.
|
|
6
7
|
* Uses only the shared tokens in brainbase-tokens.css.
|
|
7
8
|
*/
|
|
8
9
|
.bb-pc-people,
|
|
@@ -67,25 +68,12 @@
|
|
|
67
68
|
font-size: 12px;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
.bb-graph-rail .bb-pc-fix {
|
|
71
|
-
display: grid;
|
|
72
|
-
gap: 6px;
|
|
73
|
-
margin: 16px 0 0;
|
|
74
|
-
color: var(--bb-color-muted);
|
|
75
|
-
font-size: 12px;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.bb-pc-actions,
|
|
79
71
|
.bb-pc-relation-actions {
|
|
80
72
|
display: flex;
|
|
81
73
|
flex-wrap: wrap;
|
|
82
74
|
gap: 8px;
|
|
83
75
|
}
|
|
84
76
|
|
|
85
|
-
.bb-pc-actions {
|
|
86
|
-
margin: 0 0 12px;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
77
|
.bb-pc-company-cell {
|
|
90
78
|
display: grid;
|
|
91
79
|
gap: 2px;
|
|
@@ -102,6 +90,11 @@
|
|
|
102
90
|
}
|
|
103
91
|
|
|
104
92
|
@media (max-width: 760px) {
|
|
93
|
+
/* Below a host's top bar on a phone. */
|
|
94
|
+
.bb-pc-detail {
|
|
95
|
+
scroll-margin-top: 72px;
|
|
96
|
+
}
|
|
97
|
+
|
|
105
98
|
.bb-pc-people-ledger .bb-ws-ledger-row {
|
|
106
99
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 84px;
|
|
107
100
|
}
|
|
@@ -262,3 +255,161 @@
|
|
|
262
255
|
align-items: center;
|
|
263
256
|
min-height: 46px;
|
|
264
257
|
}
|
|
258
|
+
|
|
259
|
+
/* A record's page (2026-10-16). Choosing a row turns the workspace into the
|
|
260
|
+
record's page, as a CRM does: the way back with the record's place, the
|
|
261
|
+
head, then what can be done to it, gathered in one place with what the page
|
|
262
|
+
says about it (nothing is deleted; why an action is disabled). */
|
|
263
|
+
.bb-pc-action-group {
|
|
264
|
+
display: grid;
|
|
265
|
+
gap: 8px;
|
|
266
|
+
margin: 0 0 20px;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.bb-pc-action-row {
|
|
270
|
+
display: flex;
|
|
271
|
+
flex-wrap: wrap;
|
|
272
|
+
gap: 8px;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.bb-pc-action-row .bb-ws-button.is-primary {
|
|
276
|
+
border-color: var(--bb-color-action);
|
|
277
|
+
background: var(--bb-color-action);
|
|
278
|
+
color: var(--bb-color-action-text);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.bb-pc-action-row .bb-ws-button.is-primary:hover:not(:disabled) {
|
|
282
|
+
border-color: var(--bb-color-action-hover);
|
|
283
|
+
background: var(--bb-color-action-hover);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.bb-pc-action-row .bb-ws-button[data-adds]::before {
|
|
287
|
+
content: '+';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.bb-pc-action-note,
|
|
291
|
+
.bb-pc-action-reason {
|
|
292
|
+
margin: 0;
|
|
293
|
+
color: var(--bb-color-muted);
|
|
294
|
+
font-size: 12px;
|
|
295
|
+
line-height: 1.6;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.bb-pc-action-reason {
|
|
299
|
+
justify-self: start;
|
|
300
|
+
padding: 4px 10px;
|
|
301
|
+
border-radius: 6px;
|
|
302
|
+
background: var(--bb-color-warning-soft);
|
|
303
|
+
color: var(--bb-color-warning);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.bb-pc-action-note a {
|
|
307
|
+
color: var(--bb-color-accent-strong);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.bb-pc-add {
|
|
311
|
+
margin-top: -4px;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.bb-pc-detail {
|
|
315
|
+
display: grid;
|
|
316
|
+
gap: 0;
|
|
317
|
+
scroll-margin-top: 16px;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.bb-pc-detail-nav {
|
|
321
|
+
display: flex;
|
|
322
|
+
flex-wrap: wrap;
|
|
323
|
+
align-items: center;
|
|
324
|
+
gap: 6px;
|
|
325
|
+
margin: 0 0 20px;
|
|
326
|
+
padding-bottom: 12px;
|
|
327
|
+
border-bottom: 1px solid var(--bb-color-border);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.bb-pc-detail-nav .bb-pc-back {
|
|
331
|
+
margin-right: auto;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.bb-pc-detail-place {
|
|
335
|
+
display: inline-flex;
|
|
336
|
+
align-items: center;
|
|
337
|
+
gap: 6px;
|
|
338
|
+
margin-right: 4px;
|
|
339
|
+
color: var(--bb-color-muted);
|
|
340
|
+
font-size: 12px;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.bb-pc-detail-nav .bb-ws-button[aria-label='前へ'],
|
|
344
|
+
.bb-pc-detail-nav .bb-ws-button[aria-label='次へ'] {
|
|
345
|
+
min-width: 32px;
|
|
346
|
+
padding-inline: 8px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.bb-pc-detail-head {
|
|
350
|
+
display: flex;
|
|
351
|
+
align-items: flex-start;
|
|
352
|
+
gap: 14px;
|
|
353
|
+
margin: 0 0 18px;
|
|
354
|
+
scroll-margin-top: 16px;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.bb-pc-mark.bb-pc-detail-mark {
|
|
358
|
+
width: 44px;
|
|
359
|
+
height: 44px;
|
|
360
|
+
margin: 0;
|
|
361
|
+
font-size: 18px;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.bb-pc-company-mark.bb-pc-detail-mark {
|
|
365
|
+
border-radius: 10px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.bb-pc-detail-copy {
|
|
369
|
+
display: grid;
|
|
370
|
+
gap: 4px;
|
|
371
|
+
min-width: 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.bb-pc-detail-copy h2 {
|
|
375
|
+
margin: 0;
|
|
376
|
+
color: var(--bb-color-ink);
|
|
377
|
+
font-size: 22px;
|
|
378
|
+
line-height: 1.3;
|
|
379
|
+
overflow-wrap: anywhere;
|
|
380
|
+
scroll-margin-top: 16px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.bb-pc-detail-copy h2:focus {
|
|
384
|
+
outline: none;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.bb-pc-detail-sub {
|
|
388
|
+
margin: 0;
|
|
389
|
+
color: var(--bb-color-muted);
|
|
390
|
+
font-size: 13px;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.bb-pc-detail-lead {
|
|
394
|
+
margin: 4px 0 0;
|
|
395
|
+
color: var(--bb-color-ink);
|
|
396
|
+
font-size: 13px;
|
|
397
|
+
line-height: 1.6;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.bb-pc-detail-actions {
|
|
401
|
+
padding: 14px 16px;
|
|
402
|
+
border: 1px solid var(--bb-color-border);
|
|
403
|
+
border-radius: 10px;
|
|
404
|
+
background: var(--bb-color-canvas);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.bb-pc-detail-block {
|
|
408
|
+
margin: 0 0 22px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.bb-pc-detail-block h3 {
|
|
412
|
+
margin: 0 0 6px;
|
|
413
|
+
color: var(--bb-color-ink);
|
|
414
|
+
font-size: 13px;
|
|
415
|
+
}
|