@unson/brainbase-mcp 0.40.0 → 0.41.1
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/dist/foundation-http.d.ts +12 -2
- package/dist/foundation-http.js +183 -8
- package/dist/graph-web.d.ts +2 -0
- package/dist/graph-web.js +10 -1
- package/dist/judgment-history.js +3 -1
- package/dist/local-web-host.js +1 -1
- package/dist/portable-graph.d.ts +2 -2
- package/dist/portable-graph.js +4 -3
- package/dist/types.d.ts +32 -1
- package/package.json +2 -2
- package/ui/foundation-overview.js +17 -1
- package/ui/graph-own-share.js +4 -2
- package/ui/graph-projects-view.js +167 -11
- package/ui/graph-registry-view.js +135 -5
- package/ui/graph-view-shared.js +6 -1
- package/ui/judgment-history.js +5 -1
- package/ui/meeting-minutes.css +19 -0
- package/ui/meeting-minutes.js +124 -6
- package/ui/objective-editor-http-port.js +21 -8
- package/ui/objective-editor.js +186 -24
- package/ui/objective-readback-contract.js +177 -0
- package/ui/outcome-knowledge.css +18 -1
- package/ui/outcome-knowledge.js +523 -55
- package/ui/project-workspace.js +177 -4
- package/ui/world/world-canvas-ui.css +32 -0
- package/ui/world/world-canvas-ui.js +30 -2
- package/ui/world/world-view.js +5 -0
package/ui/project-workspace.js
CHANGED
|
@@ -655,6 +655,145 @@ function todayActionItems(items, now = new Date()) {
|
|
|
655
655
|
}));
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
+
const DENSE_PARTICIPANT_STATUS_VALUES = new Set(['available', 'empty', 'partial', 'unavailable', 'unknown', 'stale', 'failed']);
|
|
659
|
+
const DENSE_PARTICIPANT_FRESHNESS_VALUES = new Set(['fresh', 'stale', 'unknown']);
|
|
660
|
+
|
|
661
|
+
function denseProjectPeople(payload) {
|
|
662
|
+
const people = payload?.project?.people;
|
|
663
|
+
if (!Array.isArray(people)) return [];
|
|
664
|
+
return people.map((person) => {
|
|
665
|
+
const id = textOrNull(person?.id);
|
|
666
|
+
const name = textOrNull(person?.name) ?? textOrNull(person?.title);
|
|
667
|
+
if (!id || !name || typeof person?.accountable !== 'boolean') return null;
|
|
668
|
+
return { ...person, id, name };
|
|
669
|
+
}).filter(Boolean);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function denseParticipantStatus(payload) {
|
|
673
|
+
const raw = [payload?.participantStatus, payload?.participants_status, payload?.participantsStatus]
|
|
674
|
+
.find((value) => value !== undefined && value !== null);
|
|
675
|
+
if (raw === 'ready') return 'available';
|
|
676
|
+
if (typeof raw === 'string' && DENSE_PARTICIPANT_STATUS_VALUES.has(raw)) return raw;
|
|
677
|
+
return (Array.isArray(payload?.participants) && payload.participants.length > 0) || denseProjectPeople(payload).length > 0
|
|
678
|
+
? 'available'
|
|
679
|
+
: 'unknown';
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function denseParticipantFreshness(payload) {
|
|
683
|
+
const raw = [payload?.participantFreshness, payload?.participants_freshness, payload?.participantsFreshness]
|
|
684
|
+
.find((value) => value !== undefined && value !== null);
|
|
685
|
+
return typeof raw === 'string' && DENSE_PARTICIPANT_FRESHNESS_VALUES.has(raw) ? raw : null;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function denseParticipantAbsenceConfirmed(payload) {
|
|
689
|
+
return payload?.participantAbsenceConfirmed === true
|
|
690
|
+
|| payload?.participants_absence_confirmed === true
|
|
691
|
+
|| payload?.participantsAbsenceConfirmed === true;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function denseParticipantAsOf(payload) {
|
|
695
|
+
const raw = [
|
|
696
|
+
payload?.participantsAsOf,
|
|
697
|
+
payload?.participantAsOf,
|
|
698
|
+
payload?.participants_as_of,
|
|
699
|
+
payload?.participant_as_of,
|
|
700
|
+
payload?.participantsCheckedAt,
|
|
701
|
+
payload?.participants_checked_at,
|
|
702
|
+
payload?.participantCheckedAt,
|
|
703
|
+
payload?.participant_checked_at,
|
|
704
|
+
].map(textOrNull).find(Boolean);
|
|
705
|
+
if (!raw || Number.isNaN(Date.parse(raw))) return null;
|
|
706
|
+
return formatDateTime(raw);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function denseParticipantReason(payload, state, freshness) {
|
|
710
|
+
const explicit = textOrNull(payload?.participantUnavailableReason)
|
|
711
|
+
?? textOrNull(payload?.participants_unavailable_reason)
|
|
712
|
+
?? textOrNull(payload?.participantsUnavailableReason);
|
|
713
|
+
if (explicit) return explicit;
|
|
714
|
+
if (Array.isArray(payload?.warnings)) {
|
|
715
|
+
const warning = payload.warnings
|
|
716
|
+
.map((item) => textOrNull(item) ?? textOrNull(item?.reason) ?? textOrNull(item?.message))
|
|
717
|
+
.find(Boolean);
|
|
718
|
+
if (warning) return warning;
|
|
719
|
+
}
|
|
720
|
+
if (state === 'failed') return '関係者の詳細を取得できませんでした。';
|
|
721
|
+
if (state === 'stale' || freshness === 'stale') {
|
|
722
|
+
const checkedAt = denseParticipantAsOf(payload);
|
|
723
|
+
return checkedAt
|
|
724
|
+
? `以前の情報です(確認時点: ${checkedAt})。現在の関係者の詳細を未確認です。`
|
|
725
|
+
: '以前の情報です。確認時点は未確認です。現在の関係者の詳細を未確認です。';
|
|
726
|
+
}
|
|
727
|
+
if (state === 'partial') return '一部の関係者だけを読み取っています。';
|
|
728
|
+
return '関係者の詳細を未確認です。登録なしとは判断できません。';
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function denseParticipantCountsZero(payload) {
|
|
732
|
+
const project = payload?.project ?? {};
|
|
733
|
+
return Number.isInteger(project.participantCount)
|
|
734
|
+
&& Number.isInteger(project.accountableCount)
|
|
735
|
+
&& project.participantCount === 0
|
|
736
|
+
&& project.accountableCount === 0;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function denseParticipantStrictZero(payload) {
|
|
740
|
+
return denseParticipantCountsZero(payload)
|
|
741
|
+
&& Array.isArray(payload?.participants)
|
|
742
|
+
&& payload.participants.length === 0;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function denseParticipantSnapshot(payload) {
|
|
746
|
+
const state = denseParticipantStatus(payload);
|
|
747
|
+
const freshness = denseParticipantFreshness(payload);
|
|
748
|
+
const absenceConfirmed = denseParticipantAbsenceConfirmed(payload);
|
|
749
|
+
const confirmedEmpty = absenceConfirmed
|
|
750
|
+
&& (state === 'available' || state === 'empty')
|
|
751
|
+
&& freshness === 'fresh'
|
|
752
|
+
&& denseParticipantStrictZero(payload);
|
|
753
|
+
const uncertain = !confirmedEmpty && (state === 'unknown' || state === 'unavailable' || state === 'partial' || state === 'stale' || state === 'failed' || freshness === 'unknown' || freshness === 'stale');
|
|
754
|
+
return { state, freshness, absenceConfirmed, confirmedEmpty, uncertain, reason: denseParticipantReason(payload, state, freshness) };
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function denseParticipantCountSummary(payload, snapshot) {
|
|
758
|
+
const project = payload?.project ?? {};
|
|
759
|
+
const counts = [];
|
|
760
|
+
if (Number.isInteger(project.participantCount) && project.participantCount > 0) counts.push(`関係者${project.participantCount}人`);
|
|
761
|
+
if (Number.isInteger(project.accountableCount) && project.accountableCount > 0) counts.push(`責任者${project.accountableCount}人`);
|
|
762
|
+
if (counts.length === 0) {
|
|
763
|
+
if (snapshot.confirmedEmpty && denseParticipantStrictZero(payload)) return '確認済みの件数: 関係者0人、責任者0人。';
|
|
764
|
+
return '件数は未確認です。';
|
|
765
|
+
}
|
|
766
|
+
if (snapshot.uncertain) return `既知の件数(現在未確認): ${counts.join('、')}。`;
|
|
767
|
+
return `確認済みの件数: ${counts.join('、')}。`;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function denseParticipantMetricValue(payload, participantEdges, snapshot) {
|
|
771
|
+
const project = payload?.project ?? {};
|
|
772
|
+
if (Number.isInteger(project.participantCount) && project.participantCount > 0) return `${project.participantCount}人`;
|
|
773
|
+
if (snapshot.confirmedEmpty && denseParticipantStrictZero(payload)) return '0人';
|
|
774
|
+
if (participantEdges.length > 0) return `${participantEdges.length}人`;
|
|
775
|
+
return '未確認';
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function denseParticipantCountMismatch(payload, participantEdges, snapshot) {
|
|
779
|
+
if (snapshot.uncertain) return false;
|
|
780
|
+
const project = payload?.project ?? {};
|
|
781
|
+
const declared = [project.participantCount, project.accountableCount]
|
|
782
|
+
.some((value) => Number.isInteger(value) && value > 0);
|
|
783
|
+
const namedPeople = denseProjectPeople(payload);
|
|
784
|
+
if (denseParticipantCountsZero(payload) && participantEdges.length > 0) return true;
|
|
785
|
+
if (participantEdges.length === 0 && namedPeople.length > 0) {
|
|
786
|
+
const participantMismatch = Number.isInteger(project.participantCount)
|
|
787
|
+
&& project.participantCount >= 0
|
|
788
|
+
&& project.participantCount !== namedPeople.length;
|
|
789
|
+
const accountableMismatch = Number.isInteger(project.accountableCount)
|
|
790
|
+
&& project.accountableCount >= 0
|
|
791
|
+
&& project.accountableCount !== namedPeople.filter((person) => person.accountable).length;
|
|
792
|
+
return participantMismatch || accountableMismatch;
|
|
793
|
+
}
|
|
794
|
+
return participantEdges.length === 0 && declared;
|
|
795
|
+
}
|
|
796
|
+
|
|
658
797
|
function renderDenseOverview(doc, detail, context, entities, actions = {}) {
|
|
659
798
|
const payload = projectDetailPayload(detail);
|
|
660
799
|
const taskSection = contextTaskSection(context);
|
|
@@ -664,7 +803,18 @@ function renderDenseOverview(doc, detail, context, entities, actions = {}) {
|
|
|
664
803
|
const participantEdges = [...new Map((payload?.participants ?? [])
|
|
665
804
|
.filter((edge) => edge?.counterpart?.id && (edge.counterpart.name || edge.counterpart.title))
|
|
666
805
|
.map((edge) => [edge.counterpart.id, edge])).values()];
|
|
667
|
-
const
|
|
806
|
+
const participantStatus = denseParticipantStatus(payload);
|
|
807
|
+
const participantPeople = participantStatus === 'available'
|
|
808
|
+
? denseProjectPeople(payload).map((person) => ({
|
|
809
|
+
counterpart: person,
|
|
810
|
+
relation: person.accountable ? 'accountable_for' : 'participates_in',
|
|
811
|
+
role: person.accountable ? '責任者' : null,
|
|
812
|
+
}))
|
|
813
|
+
: [];
|
|
814
|
+
const participants = (participantEdges.length > 0 ? participantEdges : participantPeople).slice(0, 6);
|
|
815
|
+
const participantSnapshot = denseParticipantSnapshot(payload);
|
|
816
|
+
const participantCountSummary = denseParticipantCountSummary(payload, participantSnapshot);
|
|
817
|
+
const participantCountMismatch = denseParticipantCountMismatch(payload, participantEdges, participantSnapshot);
|
|
668
818
|
const recentRecords = records.length > 0 ? sortDenseItems(records).slice(0, 4) : graphRecords.slice(0, 4);
|
|
669
819
|
const updated = [...tasks, ...records]
|
|
670
820
|
.filter((item) => item.updatedAt || item.eventAt)
|
|
@@ -683,7 +833,7 @@ function renderDenseOverview(doc, detail, context, entities, actions = {}) {
|
|
|
683
833
|
denseMetric(doc, '仕事', taskMetric, taskNote),
|
|
684
834
|
denseMetric(doc, '今日扱う仕事', taskSection?.state === 'ok' ? `${due.length}件` : '未確認', taskSection?.state === 'ok' ? '期限・更新が確認できる順' : 'タスクの状態を確認中'),
|
|
685
835
|
denseMetric(doc, '前回からの変化', updated.length > 0 ? `${updated.length}件` : '未確認', updated.length > 0 ? '更新時点がある記録' : '更新時点が未確認'),
|
|
686
|
-
denseMetric(doc, '関係者', participantEdges
|
|
836
|
+
denseMetric(doc, '関係者', denseParticipantMetricValue(payload, participantEdges, participantSnapshot), participantCountSummary ?? '現在の関係者の詳細を確認中'),
|
|
687
837
|
);
|
|
688
838
|
panel.append(metrics);
|
|
689
839
|
|
|
@@ -735,8 +885,25 @@ function renderDenseOverview(doc, detail, context, entities, actions = {}) {
|
|
|
735
885
|
const peopleHead = makeElement(doc, 'div', { className: 'bb-pkw-dense-card-head' });
|
|
736
886
|
peopleHead.append(makeElement(doc, 'h4', { text: '関係者' }), makeElement(doc, 'span', { className: 'bb-pkw-muted', text: participants.length > 0 ? `${participants.length}人を表示` : '未確認' }));
|
|
737
887
|
people.append(peopleHead);
|
|
738
|
-
if (
|
|
739
|
-
|
|
888
|
+
if (participantCountSummary) people.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: participantCountSummary }));
|
|
889
|
+
if (participantSnapshot.uncertain) {
|
|
890
|
+
people.append(workspaceNotice(doc, {
|
|
891
|
+
label: participantSnapshot.state === 'failed' ? '読み取り失敗' : '未確認',
|
|
892
|
+
text: participantSnapshot.reason,
|
|
893
|
+
tone: participantSnapshot.state === 'failed' ? 'danger' : 'info',
|
|
894
|
+
}));
|
|
895
|
+
} else if (participantCountMismatch) {
|
|
896
|
+
people.append(workspaceNotice(doc, {
|
|
897
|
+
label: '詳細未確認',
|
|
898
|
+
text: '既知の件数と取得した関係者が一致しません。現在の関係者の詳細を読み取れていません。',
|
|
899
|
+
tone: 'warning',
|
|
900
|
+
}));
|
|
901
|
+
} else if (participants.length === 0 && !participantSnapshot.confirmedEmpty) {
|
|
902
|
+
people.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: '関係者の詳細を未確認です。登録なしとは判断できません。' }));
|
|
903
|
+
} else if (participants.length === 0 && participantSnapshot.confirmedEmpty) {
|
|
904
|
+
people.append(makeElement(doc, 'p', { className: 'bb-pkw-muted', text: '確認できた関係者はありません。' }));
|
|
905
|
+
}
|
|
906
|
+
if (participants.length > 0) {
|
|
740
907
|
const list = makeElement(doc, 'ul', { className: 'bb-pkw-dense-list' });
|
|
741
908
|
for (const edge of participants) {
|
|
742
909
|
const person = edge.counterpart;
|
|
@@ -749,6 +916,9 @@ function renderDenseOverview(doc, detail, context, entities, actions = {}) {
|
|
|
749
916
|
}
|
|
750
917
|
people.append(list);
|
|
751
918
|
}
|
|
919
|
+
if (typeof actions.retryParticipants === 'function' && (participantSnapshot.uncertain || participantCountMismatch || (participants.length === 0 && !participantSnapshot.confirmedEmpty))) {
|
|
920
|
+
people.append(denseButton(doc, '再試行', 'bb-pkw-dense-action', actions.retryParticipants, { 'aria-label': '関係者の読み取りを再試行' }));
|
|
921
|
+
}
|
|
752
922
|
supportingGrid.append(people);
|
|
753
923
|
panel.append(supportingGrid);
|
|
754
924
|
return panel;
|
|
@@ -1033,11 +1203,13 @@ export function createProjectKnowledgeWorkspace({
|
|
|
1033
1203
|
mountGraph,
|
|
1034
1204
|
readEntity,
|
|
1035
1205
|
onTabChange,
|
|
1206
|
+
onRetryProject,
|
|
1036
1207
|
} = {}) {
|
|
1037
1208
|
const doc = getDocument(explicitDocument);
|
|
1038
1209
|
const payload = projectDetailPayload(detail);
|
|
1039
1210
|
const project = payload?.project;
|
|
1040
1211
|
if (!project) throw new TypeError('project detail is required');
|
|
1212
|
+
const workspaceProjectId = cleanId(project.id);
|
|
1041
1213
|
// Keep the project-bound projection separate from the mutable exploration
|
|
1042
1214
|
// graph. Reading a related entity may reveal arbitrary neighboring records,
|
|
1043
1215
|
// but those records are not evidence that they belong to this project.
|
|
@@ -1390,6 +1562,7 @@ export function createProjectKnowledgeWorkspace({
|
|
|
1390
1562
|
tabs.replaceChildren(tabButton('概要', 'overview'), tabButton('情報を探す', 'graph'));
|
|
1391
1563
|
panels.replaceChildren();
|
|
1392
1564
|
const actions = { openOverview, openWorkList, openWorkDetail, openRecordDetail, openSectionList, openGraphEntity: selectAndExplore,
|
|
1565
|
+
retryParticipants: typeof onRetryProject === 'function' ? () => onRetryProject(workspaceProjectId) : null,
|
|
1393
1566
|
recordSectionId: selectedSectionId,
|
|
1394
1567
|
recordSectionTitle: CONTEXT_CATEGORIES.find(([id]) => id === selectedSectionId)?.[1] ?? contextSectionById(currentContext, [selectedSectionId])?.title,
|
|
1395
1568
|
};
|
|
@@ -60,6 +60,31 @@
|
|
|
60
60
|
align-items: center;
|
|
61
61
|
pointer-events: auto;
|
|
62
62
|
}
|
|
63
|
+
.bb-world-canvas-ui .bb-world-canvas-context {
|
|
64
|
+
position: absolute;
|
|
65
|
+
top: calc(var(--bb-world-toolbar-top) + 50px);
|
|
66
|
+
right: var(--bb-world-right-inset);
|
|
67
|
+
width: min(360px, calc(100% - var(--bb-world-right-inset) - 8px));
|
|
68
|
+
max-width: calc(100% - var(--bb-world-right-inset));
|
|
69
|
+
min-width: 0;
|
|
70
|
+
padding: 6px 10px;
|
|
71
|
+
border: 1px solid rgb(13 25 46 / 14%);
|
|
72
|
+
border-radius: 8px;
|
|
73
|
+
background: rgb(255 255 255 / 92%);
|
|
74
|
+
color: #0d192e;
|
|
75
|
+
font-size: 11px;
|
|
76
|
+
line-height: 1.5;
|
|
77
|
+
overflow-wrap: anywhere;
|
|
78
|
+
word-break: break-word;
|
|
79
|
+
pointer-events: none;
|
|
80
|
+
}
|
|
81
|
+
.bb-world-canvas-ui .bb-world-canvas-context p {
|
|
82
|
+
margin: 0;
|
|
83
|
+
}
|
|
84
|
+
.bb-world-canvas-ui .bb-world-canvas-context-next-action {
|
|
85
|
+
margin-top: 3px;
|
|
86
|
+
color: rgb(13 25 46 / 72%);
|
|
87
|
+
}
|
|
63
88
|
.bb-world-canvas-ui .bb-world-canvas-control {
|
|
64
89
|
min-width: 0;
|
|
65
90
|
height: 40px;
|
|
@@ -318,6 +343,13 @@
|
|
|
318
343
|
--bb-world-panel-top: 72px;
|
|
319
344
|
}
|
|
320
345
|
.bb-world-canvas-ui .bb-world-canvas-toolbar { grid-template-columns: auto minmax(0, 1fr) 34px; }
|
|
346
|
+
.bb-world-canvas-ui .bb-world-canvas-context {
|
|
347
|
+
top: calc(var(--bb-world-toolbar-top) + 46px);
|
|
348
|
+
width: calc(100% - var(--bb-world-right-inset) - 4px);
|
|
349
|
+
max-width: calc(100% - var(--bb-world-right-inset));
|
|
350
|
+
padding: 5px 8px;
|
|
351
|
+
font-size: 10px;
|
|
352
|
+
}
|
|
321
353
|
.bb-world-canvas-ui .bb-world-canvas-control { height: 36px; padding: 6px; font-size: 10px; }
|
|
322
354
|
.bb-world-canvas-ui .bb-world-canvas-sky { padding: 0; }
|
|
323
355
|
.bb-world-canvas-ui .bb-world-canvas-sky-icon { font-size: 20px; }
|
|
@@ -26,6 +26,7 @@ const nodeList = (nodes) => (Array.isArray(nodes) ? nodes : [nodes]).filter((nod
|
|
|
26
26
|
* @param {(code: string) => void} [options.onCity] Enter a known business.
|
|
27
27
|
* @param {(mode: 'auto'|'day'|'dusk'|'night') => void} [options.onSky]
|
|
28
28
|
* @param {() => void} [options.onDismissTask] User dismissed task details.
|
|
29
|
+
* @param {{purpose?: string, knownCityAction?: string, unavailableCity?: string}} [options.context]
|
|
29
30
|
*
|
|
30
31
|
* showRail/showTask/setHelp accept actual DOM nodes, preserving their events and
|
|
31
32
|
* all source details. showRail never opens the drawer; openRail opens it without
|
|
@@ -34,7 +35,16 @@ const nodeList = (nodes) => (Array.isArray(nodes) ? nodes : [nodes]).filter((nod
|
|
|
34
35
|
* addNote shows a quest only for an explicit nonempty summary; its full text
|
|
35
36
|
* remains in help. No summary is inferred from a long or nonurgent notice.
|
|
36
37
|
*/
|
|
37
|
-
export function createWorldCanvasUI({
|
|
38
|
+
export function createWorldCanvasUI({
|
|
39
|
+
doc,
|
|
40
|
+
stage,
|
|
41
|
+
reducedMotion = false,
|
|
42
|
+
onReset,
|
|
43
|
+
onCity,
|
|
44
|
+
onSky,
|
|
45
|
+
onDismissTask,
|
|
46
|
+
context = {},
|
|
47
|
+
}) {
|
|
38
48
|
const id = `bb-world-canvas-${++instanceId}`;
|
|
39
49
|
const listeners = [];
|
|
40
50
|
let disposed = false;
|
|
@@ -50,6 +60,11 @@ export function createWorldCanvasUI({ doc, stage, reducedMotion = false, onReset
|
|
|
50
60
|
let businesses = new Set();
|
|
51
61
|
let overlayOrder = [];
|
|
52
62
|
let overlayLayer = 10;
|
|
63
|
+
const worldContext = {
|
|
64
|
+
purpose: context.purpose ?? '事業と案件、関連する仕事を地図で確認します。',
|
|
65
|
+
knownCityAction: context.knownCityAction ?? '都市を選ぶと、その事業の区画に入ります。',
|
|
66
|
+
unavailableCity: context.unavailableCity ?? '現在、選べる事業を確認できません。',
|
|
67
|
+
};
|
|
53
68
|
|
|
54
69
|
function element(tag, className, text, attrs = {}) {
|
|
55
70
|
const node = doc.createElement(tag);
|
|
@@ -127,6 +142,16 @@ export function createWorldCanvasUI({ doc, stage, reducedMotion = false, onReset
|
|
|
127
142
|
const skyIcon = element('span', 'bb-world-canvas-sky-icon', SKY_MODES[0].icon, { 'aria-hidden': 'true' });
|
|
128
143
|
sky.append(skyIcon);
|
|
129
144
|
toolbar.append(reset, city, sky);
|
|
145
|
+
const contextRoot = element('div', 'bb-world-canvas-context', null, {
|
|
146
|
+
'aria-label': 'この画面で確認できること',
|
|
147
|
+
});
|
|
148
|
+
const contextPurpose = element('p', 'bb-world-canvas-context-purpose', worldContext.purpose, {
|
|
149
|
+
'data-world-context': 'purpose',
|
|
150
|
+
});
|
|
151
|
+
const contextNextAction = element('p', 'bb-world-canvas-context-next-action', worldContext.unavailableCity, {
|
|
152
|
+
'data-world-context': 'next-action',
|
|
153
|
+
});
|
|
154
|
+
contextRoot.append(contextPurpose, contextNextAction);
|
|
130
155
|
const skyMenu = element('div', 'bb-world-canvas-sky-menu', null, { id: `${id}-sky`, role: 'menu', 'aria-label': '空の時間帯' });
|
|
131
156
|
skyMenu.hidden = true;
|
|
132
157
|
const skyOptions = SKY_MODES.map((mode) => {
|
|
@@ -176,7 +201,7 @@ export function createWorldCanvasUI({ doc, stage, reducedMotion = false, onReset
|
|
|
176
201
|
const notes = element('div', 'bb-world-canvas-notes');
|
|
177
202
|
helpBody.append(helpContent, notes);
|
|
178
203
|
help.append(helpTop, helpBody);
|
|
179
|
-
root.append(toolbar, skyMenu, quests, edge, rail, task, helpButton, help);
|
|
204
|
+
root.append(toolbar, contextRoot, skyMenu, quests, edge, rail, task, helpButton, help);
|
|
180
205
|
stage.append(root);
|
|
181
206
|
|
|
182
207
|
function setCities(items = [], selectedCode = null) {
|
|
@@ -195,6 +220,9 @@ export function createWorldCanvasUI({ doc, stage, reducedMotion = false, onReset
|
|
|
195
220
|
}
|
|
196
221
|
city.replaceChildren(...options);
|
|
197
222
|
city.disabled = businesses.size === 0;
|
|
223
|
+
contextNextAction.textContent = businesses.size === 0
|
|
224
|
+
? worldContext.unavailableCity
|
|
225
|
+
: worldContext.knownCityAction;
|
|
198
226
|
setCity(selectedCode);
|
|
199
227
|
}
|
|
200
228
|
function setCity(code) {
|
package/ui/world/world-view.js
CHANGED
|
@@ -1658,6 +1658,11 @@ export function createWorldView({ root, rail, page, presentation = 'standard', d
|
|
|
1658
1658
|
wrap.append(stage);
|
|
1659
1659
|
canvasUI = createWorldCanvasUI({
|
|
1660
1660
|
doc, stage, reducedMotion,
|
|
1661
|
+
context: {
|
|
1662
|
+
purpose: '事業と案件、関連する仕事を地図で確認します。',
|
|
1663
|
+
knownCityAction: '都市を選ぶと、その事業の区画に入ります。',
|
|
1664
|
+
unavailableCity: '現在、選べる事業を確認できません。詳しい状況は「世界の見方」で確認できます。',
|
|
1665
|
+
},
|
|
1661
1666
|
onReset: () => { goToWorld(); clearAll(); },
|
|
1662
1667
|
onCity: (code) => {
|
|
1663
1668
|
if (!code) { goToWorld(); clearAll(); return; }
|