@unson/brainbase-mcp 0.41.11 → 0.41.13
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.js +19 -2
- package/package.json +6 -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/local-web-shell.css +1 -13
- package/ui/local-web-shell.js +64 -40
- 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/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/world/world-view.js
CHANGED
|
@@ -32,7 +32,7 @@ import { createDistrictView, DISTRICT_LEGEND } from './world-district.js';
|
|
|
32
32
|
import { createWorldCanvasUI } from './world-canvas-ui.js';
|
|
33
33
|
import { workCanvasNotes } from './world-canvas-notes.js';
|
|
34
34
|
import { canvasTexture, drawFacade, FACADE_SIZES, FACADE_UNITS, gableRoof, hashUnit, muted, paintVertices, roundedPlate, valueNoise } from './world-scenery.js';
|
|
35
|
-
import { WORK_STATES, workCityBlocks, workGrowthBlock, workPickBlock, workSign, workSiteBlocks } from './world-work-rail.js';
|
|
35
|
+
import { WORK_STATES, readReasonShort, workCityBlocks, workGrowthBlock, workPickBlock, workSign, workSignBlocks, workSigns, workSiteBlocks } from './world-work-rail.js';
|
|
36
36
|
import { exitHrefKind, exitLegend, exitsRailBlock, normalizeBusinessExits } from './world-exits.js';
|
|
37
37
|
import {
|
|
38
38
|
makeWorkspaceElement as el,
|
|
@@ -197,7 +197,7 @@ const GAP_SHORT_TEXT = Object.freeze({
|
|
|
197
197
|
review_overdue: '見直し超過',
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
-
function createScene({ doc, stage, labelsLayer, reducedMotion, onPick, onClear, onEscape }) {
|
|
200
|
+
function createScene({ doc, stage, labelsLayer, reducedMotion, onPick, onClear, onEscape, onSign }) {
|
|
201
201
|
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
202
202
|
renderer.setPixelRatio(Math.min(globalThis.devicePixelRatio ?? 1, 2));
|
|
203
203
|
renderer.shadowMap.enabled = true;
|
|
@@ -1113,18 +1113,30 @@ function createScene({ doc, stage, labelsLayer, reducedMotion, onPick, onClear,
|
|
|
1113
1113
|
for (const post of citySigns.splice(0)) scene.remove(post);
|
|
1114
1114
|
for (const [code, entry] of cityIndex) {
|
|
1115
1115
|
const summary = summaries?.[code];
|
|
1116
|
-
entry.label?.node.querySelector('.bb-world-badge')?.remove();
|
|
1117
1116
|
if (entry.label) entry.label.size = null;
|
|
1117
|
+
entry.label?.node.querySelectorAll('.bb-world-badge').forEach((badge) => badge.remove());
|
|
1118
1118
|
const signal = workSign(summary);
|
|
1119
1119
|
if (!signal) continue;
|
|
1120
|
-
const { text } = signal;
|
|
1121
1120
|
const unreadable = signal.kind === 'unreadable';
|
|
1122
1121
|
const { city } = entry;
|
|
1123
1122
|
const post = sign(unreadable ? 'unreadable' : 'check', 2.2, 0.9);
|
|
1124
1123
|
post.position.set(city.x - city.size / 2 + 0.6, 0.45, city.z + city.size / 2 - 0.6);
|
|
1125
1124
|
scene.add(post);
|
|
1126
1125
|
citySigns.push(post);
|
|
1127
|
-
if (entry.label)
|
|
1126
|
+
if (!entry.label) continue;
|
|
1127
|
+
// Each sign says what it means in a few words; pressing one opens why and what to do next
|
|
1128
|
+
// (story-world-sign-meaning-v1). The rail's entrances are the keyboard way to the same blocks.
|
|
1129
|
+
for (const item of workSigns(summary)) {
|
|
1130
|
+
const badge = el(doc, 'span', { className: `bb-world-badge ${item.kind === 'unreadable' ? 'is-unreadable' : 'is-check'}${onSign ? ' is-pressable' : ''}`, text: item.text });
|
|
1131
|
+
if (onSign) {
|
|
1132
|
+
badge.title = 'この札の意味と、次にすることを見る';
|
|
1133
|
+
badge.addEventListener('click', (event) => {
|
|
1134
|
+
event.stopPropagation();
|
|
1135
|
+
onSign(entry.label.business ?? null, code);
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
entry.label.node.append(badge);
|
|
1139
|
+
}
|
|
1128
1140
|
}
|
|
1129
1141
|
}
|
|
1130
1142
|
|
|
@@ -1748,9 +1760,9 @@ export function createWorldView({ root, rail, page, presentation = 'standard', d
|
|
|
1748
1760
|
if (!summary) return { text: workConnected === false ? '' : '仕事は区画で読みます', count: -1 };
|
|
1749
1761
|
if (summary.state !== 'complete' && summary.state !== 'partial') return { text: '仕事を読めない(0件とは確認できません)', count: -1, muted: true };
|
|
1750
1762
|
const needs = summary.needs_check?.length ?? 0;
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1753
|
-
return { text: parts.join('・'), count: needs };
|
|
1763
|
+
const signs = workSigns(summary);
|
|
1764
|
+
const parts = [...(needs ? [] : [Number.isInteger(summary.open) ? `未完了 ${summary.open}` : '未完了の件数は未確認']), ...signs.map((sign) => sign.text)];
|
|
1765
|
+
return { text: parts.join('・'), count: needs, signs: signs.length > 0 };
|
|
1754
1766
|
};
|
|
1755
1767
|
const rows = knownBusinesses
|
|
1756
1768
|
.map((business) => ({ business, note: noteOf(business) }))
|
|
@@ -1761,6 +1773,7 @@ export function createWorldView({ root, rail, page, presentation = 'standard', d
|
|
|
1761
1773
|
const button = workspaceButton(doc, { text: `${business.name}の区画に入る ›`, onClick: () => enterFromRail(business) });
|
|
1762
1774
|
item.append(button);
|
|
1763
1775
|
if (note.text) item.append(el(doc, 'span', { className: `bb-world-entrance-note${note.count > 0 ? ' is-check' : ''}${note.muted ? ' is-muted' : ''}`, text: note.text }));
|
|
1776
|
+
if (note.signs) item.append(workspaceButton(doc, { text: '札の意味 ›', variant: 'quiet', onClick: () => showSignMeaning(business) }));
|
|
1764
1777
|
list.append(item);
|
|
1765
1778
|
}
|
|
1766
1779
|
showRail([
|
|
@@ -1771,6 +1784,32 @@ export function createWorldView({ root, rail, page, presentation = 'standard', d
|
|
|
1771
1784
|
// The businesses are not known yet here (the list is drawn again once the world is built).
|
|
1772
1785
|
showRail([workspaceDetailEmpty(doc, { mark: '◇', title: '都市や建物を選ぶと、ここに出ます', text: 'ドラッグで移動、ホイールで寄る・離れる、右ドラッグで回転します。Escで全体に戻ります。' })]);
|
|
1773
1786
|
|
|
1787
|
+
/**
|
|
1788
|
+
* What a city's signs mean (story-world-sign-meaning-v1): pressing 要確認 or 一部だけ読めた over a city
|
|
1789
|
+
* opens, without entering it, which gaps the open work shows and how to correct them, and why the
|
|
1790
|
+
* work was read only in part. From there the district opens on one kind of gap.
|
|
1791
|
+
*/
|
|
1792
|
+
function showSignMeaning(business) {
|
|
1793
|
+
if (!business) return;
|
|
1794
|
+
const summary = workSummaries[business.code];
|
|
1795
|
+
const blocks = workSignBlocks(doc, {
|
|
1796
|
+
summary,
|
|
1797
|
+
onEnter: () => enterFromRail(business),
|
|
1798
|
+
onSelectGap: (kind) => {
|
|
1799
|
+
enterFromRail(business);
|
|
1800
|
+
showFocus(business, { kind });
|
|
1801
|
+
},
|
|
1802
|
+
});
|
|
1803
|
+
if (!blocks.length) return;
|
|
1804
|
+
clearAll();
|
|
1805
|
+
showRail([
|
|
1806
|
+
workspaceRailHead(doc, { kicker: `${business.name}の札`, title: '札の意味と、次にすること', lead: '地図の札は、タスクの記録と、その読み込みの状態から出しています。' }),
|
|
1807
|
+
...blocks,
|
|
1808
|
+
workspaceRailBlock(doc, { title: '', content: workspaceButton(doc, { text: '← 区画の入口に戻る', variant: 'quiet', onClick: () => showEntrances() }) }),
|
|
1809
|
+
]);
|
|
1810
|
+
canvasUI?.openRail();
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1774
1813
|
/** The entrances of the districts, the ones with the most work to check first, in the opened rail. */
|
|
1775
1814
|
function showEntrances() {
|
|
1776
1815
|
if (current.level !== 'world') goToWorld();
|
|
@@ -2396,7 +2435,10 @@ export function createWorldView({ root, rail, page, presentation = 'standard', d
|
|
|
2396
2435
|
if (!needsKnown) note('記録の確認件数', '仕事の要約に記録の要確認件数がありません。確認事項が0件とは確認できません。', 'warning', '記録の要確認件数:未確認');
|
|
2397
2436
|
if (!openKnown) note('仕事の件数', '読めた仕事の要約に未完了件数の記録がありません。0件とは確認できません。', 'warning', '未完了の件数:未確認');
|
|
2398
2437
|
if (unreadable) note('仕事の読込', `仕事を読めなかった事業${unreadable}件。0件とは確認できません。`, 'warning', `仕事が読めない事業 ${unreadable}件`);
|
|
2399
|
-
if (partial)
|
|
2438
|
+
if (partial) {
|
|
2439
|
+
const reasons = [...new Set(readable.filter((entry) => entry.state === 'partial').map((entry) => readReasonShort(entry.reason) ?? '理由の記録なし'))].join('・');
|
|
2440
|
+
note('仕事の範囲', `一部だけ読めた事業${partial}件(${reasons})。件数は読めた範囲です。区画の入口で、事業ごとの理由と次にすることを見られます。`, 'warning', `仕事が一部だけ読めた事業 ${partial}件(${reasons})`, null, { action: { label: '理由を見る', onClick: showEntrances } });
|
|
2441
|
+
}
|
|
2400
2442
|
if (needs) note('仕事の記録', `読めた未完了の仕事${needs}件(${cities}事業)に、記録から把握できていないことがあります。仕事の失敗や停止を意味しません。`, 'attention', `記録の要確認 ${needs}件・${cities}事業`, null, { action: { label: '区画を選ぶ', onClick: showEntrances } });
|
|
2401
2443
|
}
|
|
2402
2444
|
}
|
|
@@ -2627,7 +2669,7 @@ export function createWorldView({ root, rail, page, presentation = 'standard', d
|
|
|
2627
2669
|
for (const business of exitsEnabled ? businesses : []) loadExits(business);
|
|
2628
2670
|
return;
|
|
2629
2671
|
}
|
|
2630
|
-
scene = createScene({ doc, stage, labelsLayer, reducedMotion, onPick, onClear: clearAll, onEscape: goUp });
|
|
2672
|
+
scene = createScene({ doc, stage, labelsLayer, reducedMotion, onPick, onClear: clearAll, onEscape: goUp, onSign: (business, code) => showSignMeaning(business ?? knownBusinesses.find((entry) => entry.code === code) ?? null) });
|
|
2631
2673
|
scene.setSky(SKY_MODES[skyIndex][0]);
|
|
2632
2674
|
scene.buildPlaza(rows, rowItems, (row) => (rowItems.get(row.key) ?? []).filter((ref) => isWaiting(proofs.get(ref.decision_attempt_id))).length, Date.now(), { state: judgmentsUnavailable ? judgmentsUnavailable.title.replace('判断の記録:', '') : null });
|
|
2633
2675
|
scene.buildCities(layoutWorld(businesses), placement.byBusiness);
|
|
@@ -54,6 +54,47 @@ export function readReasonText(reason) {
|
|
|
54
54
|
return String(reason).split(',').map((part) => READ_REASON_TEXT[part] ?? part).join('。');
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/** The same reasons in a few words, for a sign over a city. A reason not listed here gets no words (none is guessed). */
|
|
58
|
+
const READ_REASON_SHORT = Object.freeze({
|
|
59
|
+
codes_not_permitted: '権限外の案件あり',
|
|
60
|
+
page_limit_reached: '件数が上限',
|
|
61
|
+
limit_reached: '件数が上限',
|
|
62
|
+
upstream_timeout: '応答なし',
|
|
63
|
+
upstream_request_failed: '接続できない',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export function readReasonShort(reason) {
|
|
67
|
+
if (!reason) return null;
|
|
68
|
+
const words = [...new Set(String(reason).split(',').map((part) => READ_REASON_SHORT[part]).filter(Boolean))];
|
|
69
|
+
return words.length ? words.join('・') : null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** What makes a read whole again, for each reason: where to go, or that nothing needs doing. */
|
|
73
|
+
const READ_REASON_NEXT = Object.freeze({
|
|
74
|
+
codes_not_permitted: '読むには、その案件のタスクを見る権限が要ります。権限は組織の管理者が付けます。',
|
|
75
|
+
page_limit_reached: '直す操作はありません。件数と区画の中の仕事は、読めた範囲だけです。',
|
|
76
|
+
limit_reached: '直す操作はありません。件数と区画の中の仕事は、読めた範囲だけです。',
|
|
77
|
+
upstream_timeout: '時間をおいて画面を開き直すと、もう一度読みます。',
|
|
78
|
+
upstream_request_failed: '時間をおいて画面を開き直すと、もう一度読みます。',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/** What to correct in the task record for each kind of gap (the same words as the task screen). */
|
|
82
|
+
const GAP_FIX_TEXT = Object.freeze({
|
|
83
|
+
assignee_unlinked_mentioned: '本文に書かれた人を、タスクの担当欄に登録する',
|
|
84
|
+
assignee_unrecorded: 'タスクの担当欄に、担当する人を登録する',
|
|
85
|
+
outcome_unlinked: '完了条件にある成果物の場所を、タスクの出典リンクに足す',
|
|
86
|
+
source_unlinked: '元になった議事録・Slack・資料のリンクを、タスクに足す',
|
|
87
|
+
review_overdue: '今の状態を確かめて、タスクの状態か見直し予定を更新する',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const GAP_TEXT = Object.freeze({
|
|
91
|
+
assignee_unlinked_mentioned: '担当欄に未接続(本文に人物あり)',
|
|
92
|
+
assignee_unrecorded: '担当の記録なし',
|
|
93
|
+
outcome_unlinked: '成果物の記録が未接続',
|
|
94
|
+
source_unlinked: '出典リンクなし',
|
|
95
|
+
review_overdue: '見直し予定を過ぎた',
|
|
96
|
+
});
|
|
97
|
+
|
|
57
98
|
const WORK_UNAVAILABLE_TEXT = Object.freeze({
|
|
58
99
|
task_store_not_connected: 'この画面はタスクの正本に接続していないため、都市の中の仕事は描いていません。仕事の件数は未確認です。0件とは確認できません。',
|
|
59
100
|
});
|
|
@@ -82,24 +123,88 @@ export function shortTime(value) {
|
|
|
82
123
|
}
|
|
83
124
|
|
|
84
125
|
/**
|
|
85
|
-
*
|
|
86
|
-
* be read or was read only in part
|
|
126
|
+
* The signs over a city, each with its meaning in a few words: amber when open work shows a gap in its
|
|
127
|
+
* record, grey when the work could not be read or was read only in part, with the recorded reason (an
|
|
128
|
+
* unknown city is never shown as one without problems).
|
|
87
129
|
*/
|
|
88
|
-
export function
|
|
89
|
-
if (!summary || summary.state === 'not_connected') return
|
|
130
|
+
export function workSigns(summary) {
|
|
131
|
+
if (!summary || summary.state === 'not_connected') return [];
|
|
132
|
+
const reason = readReasonShort(summary.reason);
|
|
90
133
|
if (summary.state === 'complete' || summary.state === 'partial') {
|
|
134
|
+
const signs = [];
|
|
91
135
|
const count = summary.needs_check?.length ?? 0;
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
return
|
|
136
|
+
if (count > 0) signs.push({ kind: 'check', text: `要確認 ${count}:記録に抜け` });
|
|
137
|
+
if (summary.state === 'partial') signs.push({ kind: 'unreadable', text: `一部だけ読めた${reason ? `:${reason}` : ''}` });
|
|
138
|
+
return signs;
|
|
95
139
|
}
|
|
96
|
-
return { kind: 'unreadable', text: '
|
|
140
|
+
return [{ kind: 'unreadable', text: `仕事を読めない${summary.state === 'forbidden' ? ':権限がない' : reason ? `:${reason}` : ''}` }];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** The pole over a city: amber when any open work shows a gap, else grey; its text is all its signs. */
|
|
144
|
+
export function workSign(summary) {
|
|
145
|
+
const signs = workSigns(summary);
|
|
146
|
+
if (!signs.length) return null;
|
|
147
|
+
return { kind: signs.some((sign) => sign.kind === 'check') ? 'check' : 'unreadable', text: signs.map((sign) => sign.text).join('・') };
|
|
97
148
|
}
|
|
98
149
|
|
|
99
150
|
export function workSignText(summary) {
|
|
100
151
|
return workSign(summary)?.text ?? null;
|
|
101
152
|
}
|
|
102
153
|
|
|
154
|
+
/**
|
|
155
|
+
* What a city's signs mean, opened by pressing one (story-world-sign-meaning-v1): for 要確認, how many
|
|
156
|
+
* open tasks show which gap and what to correct in the task record; for 一部だけ読めた or 仕事を読めない,
|
|
157
|
+
* the reason the host recorded and what would make the read whole. Every sentence is drawn from the
|
|
158
|
+
* summary; a reason the host did not give is said to be unrecorded, not guessed.
|
|
159
|
+
*/
|
|
160
|
+
export function workSignBlocks(doc, { summary, onSelectGap, onEnter }) {
|
|
161
|
+
const blocks = [];
|
|
162
|
+
if (!summary) return blocks;
|
|
163
|
+
const readable = summary.state === 'complete' || summary.state === 'partial';
|
|
164
|
+
const needs = readable ? summary.needs_check?.length ?? 0 : 0;
|
|
165
|
+
if (needs > 0) {
|
|
166
|
+
const list = el(doc, 'ul', { className: 'bb-world-rail-list is-plain bb-world-sign-gaps' });
|
|
167
|
+
const kinds = Object.entries(summary.gaps ?? {}).filter(([, ids]) => ids?.length).sort((a, b) => b[1].length - a[1].length);
|
|
168
|
+
for (const [kind, ids] of kinds) {
|
|
169
|
+
const li = el(doc, 'li');
|
|
170
|
+
const button = el(doc, 'button', { className: 'bb-world-rail-pick is-gap', text: `${GAP_TEXT[kind] ?? kind} ${ids.length}件 ›`, attrs: { type: 'button', disabled: !onSelectGap } });
|
|
171
|
+
button.addEventListener('click', () => onSelectGap?.(kind));
|
|
172
|
+
li.append(button);
|
|
173
|
+
if (GAP_FIX_TEXT[kind]) li.append(el(doc, 'small', { className: 'bb-world-rail-note', text: `直し方:${GAP_FIX_TEXT[kind]}` }));
|
|
174
|
+
list.append(li);
|
|
175
|
+
}
|
|
176
|
+
blocks.push(workspaceRailBlock(doc, {
|
|
177
|
+
title: `要確認 ${needs}件:記録に抜けがある仕事`,
|
|
178
|
+
className: 'bb-world-sign-meaning',
|
|
179
|
+
content: [
|
|
180
|
+
{ text: `読めた未完了の仕事${Number.isInteger(summary.open) ? `${summary.open}件` : ''}のうち${needs}件に、タスクの記録から分からないことがあります。仕事が止まっている・遅れているという意味ではありません。` },
|
|
181
|
+
kinds.length ? list : null,
|
|
182
|
+
{ text: '直すのはタスクの記録です。種類を選ぶと区画に入り、該当する仕事が並びます。1件ずつ開くと、確かめることと、タスク画面への行き先が出ます。直すと、次に読んだときに札が減ります。', className: 'bb-world-rail-caveat is-quiet' },
|
|
183
|
+
],
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
if (summary.state !== 'complete') {
|
|
187
|
+
const reason = readReasonText(summary.reason);
|
|
188
|
+
const parts = String(summary.reason ?? '').split(',').filter(Boolean);
|
|
189
|
+
const next = [...new Set(parts.map((part) => READ_REASON_NEXT[part]).filter(Boolean))];
|
|
190
|
+
const what = { partial: '一部だけ読めました', failed: '読めませんでした', forbidden: '権限が無く読めませんでした' }[summary.state] ?? `読めませんでした(${summary.state})`;
|
|
191
|
+
const at = shortTime(summary.read_at);
|
|
192
|
+
const short = readReasonShort(summary.reason) ?? (summary.state === 'forbidden' ? '権限がない' : null);
|
|
193
|
+
blocks.push(workspaceRailBlock(doc, {
|
|
194
|
+
title: `${summary.state === 'partial' ? '一部だけ読めた' : '仕事を読めない'}${short ? `:${short}` : ''}`,
|
|
195
|
+
className: 'bb-world-sign-meaning',
|
|
196
|
+
content: workspaceDefinition(doc, [
|
|
197
|
+
['読み込み', `この事業の仕事を${what}${at ? `(${at})` : ''}`],
|
|
198
|
+
['理由', reason ?? { text: '理由は記録されていません', className: 'is-unrecorded' }],
|
|
199
|
+
['件数', summary.state === 'partial' ? '読めた範囲です。読めていない仕事は0件とは限りません。' : '未確認です。0件とは確認できません。'],
|
|
200
|
+
['次にすること', next.length ? next.join('') : { text: '記録された理由からは分かりません', className: 'is-unrecorded' }],
|
|
201
|
+
]),
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
if (blocks.length && onEnter) blocks.push(workspaceRailBlock(doc, { title: '', content: workspaceButton(doc, { text: '区画に入って仕事を見る ›', onClick: onEnter }) }));
|
|
205
|
+
return blocks;
|
|
206
|
+
}
|
|
207
|
+
|
|
103
208
|
function linkLine(doc, link, label, extra) {
|
|
104
209
|
const li = el(doc, 'li', { className: `bb-world-link is-${link}` });
|
|
105
210
|
li.append(el(doc, 'span', { className: 'bb-world-link-mark', text: LINK_MARKS[link]?.mark ?? '・', attrs: { 'aria-label': LINK_MARKS[link]?.text ?? link } }));
|
|
@@ -176,6 +281,7 @@ function workStateBlocks(doc, { work, summary, onSelectGap, onSelectStatus }) {
|
|
|
176
281
|
const button = el(doc, 'button', { className: 'bb-world-rail-pick is-gap', text: `${label} ${taskIds.length}件`, attrs: { type: 'button', disabled: !onSelectGap } });
|
|
177
282
|
button.addEventListener('click', () => onSelectGap?.(kind));
|
|
178
283
|
li.append(button);
|
|
284
|
+
if (GAP_FIX_TEXT[kind]) li.append(el(doc, 'small', { className: 'bb-world-rail-note', text: `直し方:${GAP_FIX_TEXT[kind]}` }));
|
|
179
285
|
gapList.append(li);
|
|
180
286
|
}
|
|
181
287
|
blocks.push(workspaceRailBlock(doc, {
|