@unson/brainbase-mcp 0.23.7 → 0.23.9
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/world-extension.js +1 -1
- package/package.json +2 -2
- package/ui/outcome-knowledge.css +38 -8
- package/ui/outcome-knowledge.js +205 -30
- package/ui/world/world-canvas-notes.js +31 -0
- package/ui/world/world-canvas-ui.css +352 -0
- package/ui/world/world-canvas-ui.js +467 -0
- package/ui/world/world-district.js +79 -11
- package/ui/world/world-view.css +2 -0
- package/ui/world/world-view.js +241 -65
- package/ui/world/world-work-rail.js +3 -3
- package/ui/world-model-view.css +57 -0
- package/ui/world-model-view.js +45 -7
package/ui/world/world-view.js
CHANGED
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
|
|
25
25
|
import { THREE, MapControls } from './world-vendor.js';
|
|
26
26
|
import { changesSince, cityMeasures, districtChanges, districtLots, districtSnapshot, districtStage, groupJudgmentPlaces, skyAt, UNPLACED_REASON_TEXT } from './world-placement.js';
|
|
27
|
-
import { createDistrictView } from './world-district.js';
|
|
27
|
+
import { createDistrictView, DISTRICT_LEGEND } from './world-district.js';
|
|
28
|
+
import { createWorldCanvasUI } from './world-canvas-ui.js';
|
|
29
|
+
import { workCanvasNotes } from './world-canvas-notes.js';
|
|
28
30
|
import { canvasTexture, drawFacade, FACADE_SIZES, FACADE_UNITS, gableRoof, hashUnit, muted, paintVertices, roundedPlate, valueNoise } from './world-scenery.js';
|
|
29
31
|
import { WORK_STATES, workCityBlocks, workGrowthBlock, workPickBlock, workSign, workSiteBlocks } from './world-work-rail.js';
|
|
30
32
|
import {
|
|
@@ -38,6 +40,8 @@ import {
|
|
|
38
40
|
} from '../../workspace-kit.js';
|
|
39
41
|
|
|
40
42
|
export const WORLD_VIEW_CONTRACT_VERSION = 'brainbase.world-view.v0';
|
|
43
|
+
/** Optional full-canvas presentation; the standard host contract remains unchanged. */
|
|
44
|
+
export const WORLD_CANVAS_PRESENTATION_VERSION = 'brainbase.world-canvas.v1';
|
|
41
45
|
|
|
42
46
|
const STATE_LABELS = Object.freeze({ delegated: '任せている', verifying: '確かめ中', returned: '戻している' });
|
|
43
47
|
const STATE_COLORS = Object.freeze({ delegated: 0x087d62, verifying: 0xc99a3a, returned: 0xd92335 });
|
|
@@ -1034,7 +1038,7 @@ function createScene({ doc, stage, labelsLayer, reducedMotion, onPick, onClear,
|
|
|
1034
1038
|
});
|
|
1035
1039
|
renderer.domElement.addEventListener('keydown', (event) => {
|
|
1036
1040
|
// Escape goes one level up; the view decides what that level is.
|
|
1037
|
-
if (event.key === 'Escape') onEscape();
|
|
1041
|
+
if (event.key === 'Escape' && !event.defaultPrevented) onEscape();
|
|
1038
1042
|
});
|
|
1039
1043
|
|
|
1040
1044
|
let flight = null;
|
|
@@ -1431,12 +1435,32 @@ function addressSelection() {
|
|
|
1431
1435
|
* organization web passes its own route.
|
|
1432
1436
|
* @param {(site: { task_id: string }) => string | null} [options.taskHref] Where a work site's task opens in the
|
|
1433
1437
|
* host's task screen (to check or correct it). Without one the rail says it cannot be corrected here.
|
|
1438
|
+
* @param {'standard' | 'canvas'} [options.presentation] Canvas owns its accessible overlays and ignores the host rail.
|
|
1434
1439
|
* @param {{ read(): { business: string | null, site: string | null }, write(selection): void }} [options.selection]
|
|
1435
1440
|
*/
|
|
1436
|
-
export function createWorldView({ root, rail, page, document: explicitDocument, fetcher, projectHref = (project) => `#projects?project=${encodeURIComponent(project.id)}`, taskHref = null, selection = addressSelection() }) {
|
|
1441
|
+
export function createWorldView({ root, rail, page, presentation = 'standard', document: explicitDocument, fetcher, projectHref = (project) => `#projects?project=${encodeURIComponent(project.id)}`, taskHref = null, selection = addressSelection() }) {
|
|
1437
1442
|
const doc = explicitDocument ?? globalThis.document;
|
|
1438
1443
|
const reducedMotion = Boolean(globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches);
|
|
1439
|
-
const
|
|
1444
|
+
const canvasMode = presentation === 'canvas';
|
|
1445
|
+
const wrap = el(doc, 'div', { className: `bb-world${canvasMode ? ' bb-world--canvas' : ''}` });
|
|
1446
|
+
let disposed = false;
|
|
1447
|
+
let canvasUI = null;
|
|
1448
|
+
const canvasNotes = new Map();
|
|
1449
|
+
const renderCanvasNotes = () => {
|
|
1450
|
+
if (!canvasUI || disposed) return;
|
|
1451
|
+
canvasUI.clearNotes();
|
|
1452
|
+
for (const note of canvasNotes.values()) canvasUI.addNote(note);
|
|
1453
|
+
};
|
|
1454
|
+
const setCanvasNote = (key, note) => {
|
|
1455
|
+
if (note) canvasNotes.set(key, note);
|
|
1456
|
+
else canvasNotes.delete(key);
|
|
1457
|
+
renderCanvasNotes();
|
|
1458
|
+
};
|
|
1459
|
+
function updateDistrictNotes(work) {
|
|
1460
|
+
for (const key of canvasNotes.keys()) if (key.startsWith('district:')) canvasNotes.delete(key);
|
|
1461
|
+
if (work) for (const [index, note] of workCanvasNotes(work).entries()) canvasNotes.set(`district:${index}`, note);
|
|
1462
|
+
renderCanvasNotes();
|
|
1463
|
+
}
|
|
1440
1464
|
root.append(wrap);
|
|
1441
1465
|
let scene = null;
|
|
1442
1466
|
|
|
@@ -1481,12 +1505,50 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1481
1505
|
);
|
|
1482
1506
|
const stage = el(doc, 'div', { className: 'bb-world-stage' });
|
|
1483
1507
|
const labelsLayer = el(doc, 'div', { className: 'bb-world-labels' });
|
|
1484
|
-
stage.append(labelsLayer
|
|
1485
|
-
|
|
1508
|
+
stage.append(labelsLayer);
|
|
1509
|
+
if (canvasMode) {
|
|
1510
|
+
wrap.append(stage);
|
|
1511
|
+
canvasUI = createWorldCanvasUI({
|
|
1512
|
+
doc, stage, reducedMotion,
|
|
1513
|
+
onReset: () => { goToWorld(); clearAll(); },
|
|
1514
|
+
onCity: (code) => {
|
|
1515
|
+
if (!code) { goToWorld(); clearAll(); return; }
|
|
1516
|
+
const business = businessOf(code);
|
|
1517
|
+
if (business) openCity(business, scene?.selectKey({ code })?.center ?? null);
|
|
1518
|
+
},
|
|
1519
|
+
onSky: (mode) => {
|
|
1520
|
+
skyIndex = Math.max(0, SKY_MODES.findIndex(([key]) => key === mode));
|
|
1521
|
+
scene?.setSky(SKY_MODES[skyIndex][0]);
|
|
1522
|
+
district?.setSky(SKY_MODES[skyIndex][0]);
|
|
1523
|
+
},
|
|
1524
|
+
onDismissTask: () => dismissTask(),
|
|
1525
|
+
});
|
|
1526
|
+
const help = el(doc, 'div');
|
|
1527
|
+
help.append(el(doc, 'p', { text: 'ドラッグで移動、ホイールで寄る・離れる、右ドラッグで回転。都市を選ぶと区画に入ります。右端から詳細と絞り込みを開けます。Escで詳細を閉じ、その後は区画・全体の順に戻ります。ここから記録は書き換えません。' }));
|
|
1528
|
+
help.append(legend, workLegend);
|
|
1529
|
+
workLegend.hidden = false;
|
|
1530
|
+
const list = el(doc, 'ul');
|
|
1531
|
+
for (const [, text] of DISTRICT_LEGEND) list.append(el(doc, 'li', { text }));
|
|
1532
|
+
help.append(list);
|
|
1533
|
+
if (page?.source) help.append(el(doc, 'p', { text: page.source }));
|
|
1534
|
+
canvasUI.setHelp([help]);
|
|
1535
|
+
} else {
|
|
1536
|
+
stage.append(notices, legend, workLegend);
|
|
1537
|
+
wrap.append(header, stage);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
const onRootEscape = (event) => {
|
|
1541
|
+
if (!canvasMode || event.key !== 'Escape' || event.defaultPrevented) return;
|
|
1542
|
+
event.preventDefault();
|
|
1543
|
+
event.stopPropagation();
|
|
1544
|
+
if (!canvasUI.dismissOverlay()) goUp();
|
|
1545
|
+
};
|
|
1546
|
+
wrap.addEventListener('keydown', onRootEscape, true);
|
|
1486
1547
|
|
|
1487
1548
|
function showRail(nodes) {
|
|
1488
|
-
if (
|
|
1489
|
-
|
|
1549
|
+
if (disposed) return;
|
|
1550
|
+
if (canvasUI) canvasUI.showRail(nodes);
|
|
1551
|
+
else rail?.replaceChildren(...nodes);
|
|
1490
1552
|
}
|
|
1491
1553
|
/**
|
|
1492
1554
|
* With nothing chosen, the rail lists the ways in (AC-19): one button per business, the ones with the
|
|
@@ -1501,9 +1563,9 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1501
1563
|
const noteOf = (business) => {
|
|
1502
1564
|
const summary = workSummaries[business.code];
|
|
1503
1565
|
if (!summary) return { text: workConnected === false ? '' : '仕事は区画で読みます', count: -1 };
|
|
1504
|
-
if (summary.state !== 'complete' && summary.state !== 'partial') return { text: '仕事を読めない(0
|
|
1566
|
+
if (summary.state !== 'complete' && summary.state !== 'partial') return { text: '仕事を読めない(0件とは確認できません)', count: -1, muted: true };
|
|
1505
1567
|
const needs = summary.needs_check?.length ?? 0;
|
|
1506
|
-
const parts = [needs ? `要確認 ${needs}` : `未完了 ${summary.open
|
|
1568
|
+
const parts = [needs ? `要確認 ${needs}` : Number.isInteger(summary.open) ? `未完了 ${summary.open}` : '未完了の件数は未確認'];
|
|
1507
1569
|
if (summary.state === 'partial') parts.push('一部だけ読めた');
|
|
1508
1570
|
return { text: parts.join('・'), count: needs };
|
|
1509
1571
|
};
|
|
@@ -1593,17 +1655,21 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1593
1655
|
/** Whether the host has a task store: true, false (not connected), or null (not known yet / unreadable). */
|
|
1594
1656
|
let workConnected = null;
|
|
1595
1657
|
const workCache = new Map();
|
|
1596
|
-
const
|
|
1658
|
+
const workRequests = new Map();
|
|
1659
|
+
const current = { code: null, siteId: null, level: 'world', focus: null, pendingSite: null, detachedSiteId: null };
|
|
1597
1660
|
|
|
1598
1661
|
/** Inside a city the notices fold away (they would cover it); its rail carries the sources and times. */
|
|
1599
1662
|
function setLevel(level) {
|
|
1600
1663
|
current.level = level;
|
|
1601
1664
|
notices.hidden = level !== 'world';
|
|
1665
|
+
if (canvasMode && level === 'world') updateDistrictNotes(null);
|
|
1602
1666
|
}
|
|
1603
1667
|
|
|
1604
1668
|
function setCurrent(code, siteId) {
|
|
1605
1669
|
current.code = code;
|
|
1606
1670
|
current.siteId = siteId;
|
|
1671
|
+
if (!siteId) current.detachedSiteId = null;
|
|
1672
|
+
canvasUI?.setCity(code);
|
|
1607
1673
|
selection?.write?.({ business: code, site: siteId });
|
|
1608
1674
|
}
|
|
1609
1675
|
|
|
@@ -1639,7 +1705,7 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1639
1705
|
|
|
1640
1706
|
function workBlocksFor(business) {
|
|
1641
1707
|
if (workConnected === false) {
|
|
1642
|
-
return [workspaceRailBlock(doc, { title: '仕事', content: { text: '
|
|
1708
|
+
return [workspaceRailBlock(doc, { title: '仕事', content: { text: 'この画面はタスクの正本に接続していないため、都市の中の仕事は描いていません。仕事の件数は未確認です。0件とは確認できません。' } })];
|
|
1643
1709
|
}
|
|
1644
1710
|
const work = workCache.get(business.code) ?? null;
|
|
1645
1711
|
return workCityBlocks(doc, {
|
|
@@ -1680,13 +1746,22 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1680
1746
|
title: `${label} ${sites.length}件`,
|
|
1681
1747
|
sites,
|
|
1682
1748
|
note: '該当する仕事を街の中で濃く、ほかを薄く出しています。選ぶと、その仕事の根拠が開きます。',
|
|
1683
|
-
onSelectSite: (taskId) => openSiteById(business, taskId),
|
|
1749
|
+
onSelectSite: (taskId) => openSiteById(business, taskId, { focus: true }),
|
|
1684
1750
|
}));
|
|
1685
1751
|
blocks.push(workspaceRailBlock(doc, { title: '', content: workspaceButton(doc, { text: 'すべての仕事を表示', onClick: () => showFocus(business, null) }) }));
|
|
1686
1752
|
}
|
|
1687
1753
|
const growth = growthFor(business.code);
|
|
1688
1754
|
if (growth) blocks.push(workGrowthBlock(doc, growth));
|
|
1689
|
-
blocks.push(...workBlocksFor(business)
|
|
1755
|
+
blocks.push(...workBlocksFor(business));
|
|
1756
|
+
if (canvasMode && work?.status === 'ok' && ['complete', 'partial'].includes(work.reads.tasks.state)) {
|
|
1757
|
+
// The canvas objects have an equivalent keyboard/touch entry for every
|
|
1758
|
+
// actual record, including completed/cancelled work outside open-work filters.
|
|
1759
|
+
const directory = el(doc, 'details', { className: 'bb-world-task-directory' });
|
|
1760
|
+
directory.append(el(doc, 'summary', { text: `仕事を選ぶ(${work.reads.tasks.state === 'partial' ? '読めた範囲 ' : ''}${work.sites.length}件)` }));
|
|
1761
|
+
directory.append(workPickBlock(doc, { title: '仕事の記録', sites: work.sites, onSelectSite: (taskId) => openSiteById(business, taskId, { focus: true }) }));
|
|
1762
|
+
blocks.push(directory);
|
|
1763
|
+
}
|
|
1764
|
+
blocks.push(...cityRegistrationBlocks(business));
|
|
1690
1765
|
showRail(blocks);
|
|
1691
1766
|
if (revealFocus) reveal(focusBlock ?? blocks[0]);
|
|
1692
1767
|
}
|
|
@@ -1723,7 +1798,7 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1723
1798
|
// Without storage the district shows no changes next time.
|
|
1724
1799
|
}
|
|
1725
1800
|
}
|
|
1726
|
-
const liveSites = (work) => (work?.status === 'ok' && work.reads.tasks.state
|
|
1801
|
+
const liveSites = (work) => (work?.status === 'ok' && work.reads.tasks.state === 'complete' ? work.sites : null);
|
|
1727
1802
|
/** Compares against what this viewer last saw, then remembers what they see now. */
|
|
1728
1803
|
function noteGrowth(code, work, previous) {
|
|
1729
1804
|
const sites = liveSites(work);
|
|
@@ -1742,22 +1817,34 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1742
1817
|
|
|
1743
1818
|
// AC-23: coming back from the task screen with the district open, read its work again and answer.
|
|
1744
1819
|
async function recheckDistrict() {
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
const
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
const
|
|
1752
|
-
if (
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
district
|
|
1759
|
-
|
|
1820
|
+
const code = current.level !== 'world' ? current.code : null;
|
|
1821
|
+
const business = code ? businessOf(code) : null;
|
|
1822
|
+
if (!business || (!canvasMode && !district?.isOpen())) return;
|
|
1823
|
+
const previous = growthFor(code)?.seen ?? readSnapshot(code);
|
|
1824
|
+
const request = Symbol(code);
|
|
1825
|
+
workRequests.set(code, request);
|
|
1826
|
+
const result = await readJson(fetcher, `/api/extensions/world/businesses/${encodeURIComponent(code)}/work`);
|
|
1827
|
+
if (disposed || workRequests.get(code) !== request) return;
|
|
1828
|
+
const work = result.ok ? result.data : { status: 'unavailable', reason: result.error };
|
|
1829
|
+
workCache.set(code, work);
|
|
1830
|
+
if (current.code !== code || current.level === 'world') return;
|
|
1831
|
+
updateDistrictNotes(work);
|
|
1832
|
+
scene?.showWork(code, work);
|
|
1833
|
+
if (districtCode === code && district?.isOpen()) {
|
|
1834
|
+
district.refresh(business, work, { neighbours: new Map(knownBusinesses.map((entry) => [entry.code, entry.name])) });
|
|
1835
|
+
const growth = noteGrowth(code, work, previous);
|
|
1836
|
+
district.celebrate(growth?.changes ?? null);
|
|
1837
|
+
district.focus(focusIdsFor(work, current.focus));
|
|
1838
|
+
}
|
|
1839
|
+
if (current.siteId) {
|
|
1840
|
+
const siteId = current.siteId;
|
|
1841
|
+
if (openSiteById(business, siteId, { select: false })) return;
|
|
1842
|
+
dismissTask();
|
|
1843
|
+
setCanvasNote('district:selection', { label: '選択中の仕事', tone: 'warning', summary: '選択中の仕事を現在の読込で確認できません', text: '選択中の仕事を現在の読み込み結果で確認できません。削除されたとは限りません。詳細から読み込み状態を確認してください。' });
|
|
1844
|
+
}
|
|
1845
|
+
renderCityRail(business);
|
|
1760
1846
|
}
|
|
1847
|
+
|
|
1761
1848
|
const onVisible = () => {
|
|
1762
1849
|
if (doc.visibilityState === 'visible') void recheckDistrict();
|
|
1763
1850
|
};
|
|
@@ -1782,11 +1869,16 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1782
1869
|
if (owner && current.level === 'site') openCity(owner, null);
|
|
1783
1870
|
},
|
|
1784
1871
|
onEscape: goUp,
|
|
1872
|
+
focusSelection: !canvasMode,
|
|
1873
|
+
showChrome: !canvasMode,
|
|
1874
|
+
onProjectSelection: (anchor) => {
|
|
1875
|
+
if (anchor.taskId === current.siteId && current.detachedSiteId !== current.siteId) canvasUI?.setTaskAnchor(anchor);
|
|
1876
|
+
else if (!anchor.visible && !current.siteId) canvasUI?.setTaskAnchor({ visible: false });
|
|
1877
|
+
},
|
|
1785
1878
|
});
|
|
1786
1879
|
district.setSky(SKY_MODES[skyIndex][0]);
|
|
1787
1880
|
scene.setPaused(true);
|
|
1788
|
-
legend.hidden = true;
|
|
1789
|
-
workLegend.hidden = true;
|
|
1881
|
+
if (!canvasMode) { legend.hidden = true; workLegend.hidden = true; }
|
|
1790
1882
|
if (districtCode !== business.code || !district.isOpen()) {
|
|
1791
1883
|
district.show(business, work, { neighbours: new Map(knownBusinesses.map((entry) => [entry.code, entry.name])) });
|
|
1792
1884
|
districtCode = business.code;
|
|
@@ -1802,22 +1894,32 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1802
1894
|
district.hide();
|
|
1803
1895
|
districtCode = null;
|
|
1804
1896
|
scene?.setPaused(false);
|
|
1805
|
-
legend.hidden = false;
|
|
1897
|
+
if (!canvasMode) legend.hidden = false;
|
|
1806
1898
|
}
|
|
1807
1899
|
|
|
1808
1900
|
async function loadWork(business) {
|
|
1809
1901
|
if (workConnected === false || workCache.has(business.code)) return workCache.get(business.code) ?? null;
|
|
1902
|
+
const request = Symbol(business.code);
|
|
1903
|
+
workRequests.set(business.code, request);
|
|
1810
1904
|
const result = await readJson(fetcher, `/api/extensions/world/businesses/${encodeURIComponent(business.code)}/work`);
|
|
1905
|
+
if (disposed || workRequests.get(business.code) !== request) return null;
|
|
1811
1906
|
const work = result.ok ? result.data : { status: 'unavailable', reason: result.error };
|
|
1812
1907
|
if (work?.status === 'not_connected') workConnected = false;
|
|
1813
1908
|
workCache.set(business.code, work);
|
|
1814
1909
|
if (current.code !== business.code) return work;
|
|
1910
|
+
if (current.level !== 'world') updateDistrictNotes(work);
|
|
1815
1911
|
scene?.showWork(business.code, work);
|
|
1816
|
-
if (current.level !== 'world')
|
|
1817
|
-
|
|
1912
|
+
if (current.level !== 'world') {
|
|
1913
|
+
if (districtCode === business.code && district?.isOpen()) {
|
|
1914
|
+
district.refresh(business, work, { neighbours: new Map(knownBusinesses.map((entry) => [entry.code, entry.name])) });
|
|
1915
|
+
district.focus(focusIdsFor(work, current.focus));
|
|
1916
|
+
} else enterDistrict(business, work);
|
|
1917
|
+
}
|
|
1918
|
+
if (current.pendingSite && current.level !== 'world') {
|
|
1818
1919
|
const siteId = current.pendingSite;
|
|
1819
1920
|
current.pendingSite = null;
|
|
1820
1921
|
if (openSiteById(business, siteId)) return work;
|
|
1922
|
+
setCanvasNote('district:selection', { label: '選択中の仕事', tone: 'warning', summary: '保存された仕事を現在の読込で確認できません', text: '保存された仕事を現在の読み込みで確認できません。削除とは断定せず、区画の読み込み状態を確認してください。' });
|
|
1821
1923
|
}
|
|
1822
1924
|
if (current.level !== 'site') renderCityRail(business);
|
|
1823
1925
|
return work;
|
|
@@ -1825,12 +1927,19 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1825
1927
|
|
|
1826
1928
|
/** Opens a city: its rail, and its district once its work is read (the district is where its work is). */
|
|
1827
1929
|
function openCity(business, center, { zoom = 2.8 } = {}) {
|
|
1828
|
-
|
|
1930
|
+
canvasUI?.closeTask();
|
|
1931
|
+
current.pendingSite = null;
|
|
1932
|
+
if (current.code !== business.code) {
|
|
1933
|
+
current.focus = null;
|
|
1934
|
+
leaveDistrict();
|
|
1935
|
+
}
|
|
1829
1936
|
setCurrent(business.code, null);
|
|
1830
1937
|
setLevel('city');
|
|
1831
1938
|
const work = workCache.get(business.code);
|
|
1939
|
+
updateDistrictNotes(work ?? null);
|
|
1832
1940
|
if (districtCode === business.code && district?.isOpen()) {
|
|
1833
|
-
district.home();
|
|
1941
|
+
if (!canvasMode) district.home();
|
|
1942
|
+
else district.clearSelection();
|
|
1834
1943
|
district.focus(focusIdsFor(work, current.focus));
|
|
1835
1944
|
} else {
|
|
1836
1945
|
if (center) scene?.flyTo(new THREE.Vector3(center.x, 0, center.z), zoom);
|
|
@@ -1843,31 +1952,53 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1843
1952
|
if (!work) void loadWork(business);
|
|
1844
1953
|
}
|
|
1845
1954
|
|
|
1846
|
-
function openSite(business, site) {
|
|
1955
|
+
function openSite(business, site, { select = true, focus = false } = {}) {
|
|
1847
1956
|
setCurrent(business.code, site.task_id);
|
|
1848
1957
|
setLevel('site');
|
|
1849
|
-
|
|
1958
|
+
const drawable = Boolean(scene && districtCode === business.code && site.work.status !== 'cancelled');
|
|
1959
|
+
const detached = !drawable || focus || (!select && current.detachedSiteId === site.task_id);
|
|
1960
|
+
current.detachedSiteId = detached ? site.task_id : null;
|
|
1961
|
+
if (select && drawable) district?.select(site.task_id);
|
|
1850
1962
|
const work = workCache.get(business.code);
|
|
1851
1963
|
const back = workspaceButton(doc, { text: `← ${business.name}の区画に戻る`, onClick: () => goUp() });
|
|
1852
1964
|
const head = workspaceRailHead(doc, { kicker: `${business.name} の仕事(タスク)`, title: site.title, sub: `記録上:${site.work.label}${site.gaps.length ? `・把握できていないこと${site.gaps.length}種` : ''}` });
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1965
|
+
const nodes = [head, ...(canvasMode && detached ? [el(doc, 'p', { text: drawable ? '一覧から選んだ仕事の記録を、地図の位置とは独立して表示しています。' : 'この仕事は地図上に描いていません。記録の詳細を表示しています。' })] : []), ...workSiteBlocks(doc, { business, site, readAt: work?.reads?.tasks?.read_at ?? null, taskHref })];
|
|
1966
|
+
if (canvasUI) {
|
|
1967
|
+
renderCityRail(business);
|
|
1968
|
+
// WebGL fallback keeps the complete task inspectable in the same nonmodal surface.
|
|
1969
|
+
if (detached) {
|
|
1970
|
+
const rect = stage.getBoundingClientRect();
|
|
1971
|
+
canvasUI.setTaskAnchor({ x: rect.width / 2, y: rect.height / 2, visible: true, unanchored: true });
|
|
1972
|
+
}
|
|
1973
|
+
canvasUI.showTask(nodes, { focus });
|
|
1974
|
+
} else {
|
|
1975
|
+
showRail([head, workspaceRailBlock(doc, { title: '', content: back }), ...nodes.slice(1)]);
|
|
1976
|
+
reveal(head);
|
|
1977
|
+
}
|
|
1859
1978
|
}
|
|
1860
1979
|
|
|
1861
|
-
function openSiteById(business, taskId) {
|
|
1980
|
+
function openSiteById(business, taskId, options) {
|
|
1862
1981
|
const work = workCache.get(business.code);
|
|
1863
1982
|
const site = work?.status === 'ok' ? work.sites.find((entry) => entry.task_id === taskId) : null;
|
|
1864
1983
|
if (!site) return false;
|
|
1865
|
-
openSite(business, site);
|
|
1984
|
+
openSite(business, site, options);
|
|
1866
1985
|
return true;
|
|
1867
1986
|
}
|
|
1868
1987
|
|
|
1988
|
+
function dismissTask() {
|
|
1989
|
+
canvasUI?.closeTask();
|
|
1990
|
+
district?.clearSelection();
|
|
1991
|
+
if (current.level !== 'site') return;
|
|
1992
|
+
const business = businessOf(current.code);
|
|
1993
|
+
setCurrent(current.code, null);
|
|
1994
|
+
setLevel('city');
|
|
1995
|
+
if (business) renderCityRail(business);
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1869
1998
|
/** The whole world again, with the open city kept in hand (ring and rail stay). */
|
|
1870
1999
|
function goToWorld() {
|
|
2000
|
+
canvasUI?.closeTask();
|
|
2001
|
+
current.pendingSite = null;
|
|
1871
2002
|
leaveDistrict();
|
|
1872
2003
|
setLevel('world');
|
|
1873
2004
|
if (current.code) {
|
|
@@ -1883,6 +2014,9 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1883
2014
|
}
|
|
1884
2015
|
|
|
1885
2016
|
function clearAll() {
|
|
2017
|
+
scene?.clearSelection();
|
|
2018
|
+
canvasUI?.closeTask();
|
|
2019
|
+
current.pendingSite = null;
|
|
1886
2020
|
setCurrent(null, null);
|
|
1887
2021
|
setLevel('world');
|
|
1888
2022
|
current.focus = null;
|
|
@@ -1893,6 +2027,7 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1893
2027
|
function goUp() {
|
|
1894
2028
|
const business = current.code ? businessOf(current.code) : null;
|
|
1895
2029
|
if (business && current.level === 'site') {
|
|
2030
|
+
if (canvasMode) { dismissTask(); return; }
|
|
1896
2031
|
district?.clearSelection();
|
|
1897
2032
|
openCity(business, null);
|
|
1898
2033
|
return;
|
|
@@ -1911,34 +2046,42 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1911
2046
|
const business = saved.business ? businessOf(saved.business) : null;
|
|
1912
2047
|
if (!business) return;
|
|
1913
2048
|
const picked = scene?.selectKey({ code: business.code });
|
|
1914
|
-
current.pendingSite = saved.site ?? null;
|
|
1915
2049
|
openCity(business, picked?.center ?? null);
|
|
2050
|
+
current.pendingSite = saved.site ?? null;
|
|
2051
|
+
if (current.pendingSite && openSiteById(business, current.pendingSite)) current.pendingSite = null;
|
|
1916
2052
|
}
|
|
1917
2053
|
|
|
1918
2054
|
async function loadWorkSummary(note) {
|
|
1919
2055
|
const result = await readJson(fetcher, '/api/extensions/world/work-summary');
|
|
2056
|
+
if (disposed) return;
|
|
1920
2057
|
if (!result.ok) {
|
|
1921
|
-
note('仕事', `仕事の要約を読めません(${result.error})。都市を選ぶと、その事業の仕事を読みにいきます。0
|
|
2058
|
+
note('仕事', `仕事の要約を読めません(${result.error})。都市を選ぶと、その事業の仕事を読みにいきます。0件とは確認できません。`, 'warning', '仕事の要約:読込失敗');
|
|
1922
2059
|
return;
|
|
1923
2060
|
}
|
|
1924
2061
|
if (result.data?.status === 'not_connected') {
|
|
1925
2062
|
workConnected = false;
|
|
1926
|
-
note('仕事', 'この画面はタスクの正本に接続していないため、都市の中の仕事は描いていません(0
|
|
2063
|
+
note('仕事', 'この画面はタスクの正本に接続していないため、都市の中の仕事は描いていません(0件とは確認できません)。', 'warning', '仕事:未接続(0件とは確認できません)');
|
|
1927
2064
|
return;
|
|
1928
2065
|
}
|
|
1929
2066
|
if (result.data?.status !== 'ok') {
|
|
1930
|
-
note('仕事', `仕事の要約を読めません(${result.data?.reason ?? result.data?.status ?? '理由不明'})。0
|
|
2067
|
+
note('仕事', `仕事の要約を読めません(${result.data?.reason ?? result.data?.status ?? '理由不明'})。0件とは確認できません。`, 'warning', '仕事の要約:読めない');
|
|
1931
2068
|
return;
|
|
1932
2069
|
}
|
|
1933
2070
|
workConnected = true;
|
|
1934
|
-
const summaries = result.data.businesses ?? {};
|
|
2071
|
+
const summaries = { ...(result.data.businesses ?? {}) };
|
|
2072
|
+
// An omitted business is missing data, not an unremarkable city with zero work.
|
|
2073
|
+
for (const business of knownBusinesses) {
|
|
2074
|
+
if (!summaries[business.code]) summaries[business.code] = { state: 'unavailable', reason: 'summary_missing', open: null, needs_check: null };
|
|
2075
|
+
}
|
|
1935
2076
|
workSummaries = summaries;
|
|
1936
2077
|
if (current.level === 'world' && !current.code) showEmptyRail();
|
|
1937
2078
|
scene?.setWorkSigns(summaries);
|
|
1938
2079
|
const entries = Object.values(summaries);
|
|
1939
2080
|
const readable = entries.filter((entry) => entry.state === 'complete' || entry.state === 'partial');
|
|
1940
2081
|
const unreadable = entries.length - readable.length;
|
|
1941
|
-
const
|
|
2082
|
+
const openKnown = readable.every((entry) => Number.isInteger(entry.open) && entry.open >= 0);
|
|
2083
|
+
const open = openKnown ? readable.reduce((sum, entry) => sum + entry.open, 0) : null;
|
|
2084
|
+
const needsKnown = readable.every((entry) => Array.isArray(entry.needs_check));
|
|
1942
2085
|
const needs = readable.reduce((sum, entry) => sum + (entry.needs_check?.length ?? 0), 0);
|
|
1943
2086
|
const cities = readable.filter((entry) => entry.needs_check?.length).length;
|
|
1944
2087
|
const partial = readable.filter((entry) => entry.state === 'partial').length;
|
|
@@ -1947,11 +2090,20 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
1947
2090
|
for (const entry of readable) for (const [kind, ids] of Object.entries(entry.gaps ?? {})) kinds.set(kind, (kinds.get(kind) ?? 0) + ids.length);
|
|
1948
2091
|
const kindText = [...kinds].sort((a, b) => b[1] - a[1]).map(([kind, count]) => `${GAP_SHORT_TEXT[kind] ?? kind}${count}`).join('・');
|
|
1949
2092
|
const parts = readable.length
|
|
1950
|
-
? [
|
|
2093
|
+
? [needsKnown
|
|
2094
|
+
? `未完了の仕事${open === null ? '(件数未確認)' : `${open}件`}のうち${needs}件(${cities}事業)に、記録から把握できていないことがあります(?の札${kindText ? `。内訳:${kindText}` : ''})`
|
|
2095
|
+
: '仕事の要約に記録の要確認件数がなく、件数は未確認です']
|
|
1951
2096
|
: [];
|
|
1952
|
-
if (unreadable) parts.push(`${readable.length ? '' : 'どの事業の仕事も読めませんでした。'}仕事を読めなかった事業${unreadable}件(灰色の札。0
|
|
2097
|
+
if (unreadable) parts.push(`${readable.length ? '' : 'どの事業の仕事も読めませんでした。'}仕事を読めなかった事業${unreadable}件(灰色の札。0件とは確認できません)`);
|
|
1953
2098
|
if (partial) parts.push(`一部だけ読めた事業${partial}件`);
|
|
1954
2099
|
note('仕事', `${parts.join('。')}。「止まっている」という意味ではありません(${shortTimeText(result.data.as_of)}時点)。`);
|
|
2100
|
+
if (canvasMode) {
|
|
2101
|
+
if (!needsKnown) note('記録の確認件数', '仕事の要約に記録の要確認件数がありません。確認事項が0件とは確認できません。', 'warning', '記録の要確認件数:未確認');
|
|
2102
|
+
if (!openKnown) note('仕事の件数', '読めた仕事の要約に未完了件数の記録がありません。0件とは確認できません。', 'warning', '未完了の件数:未確認');
|
|
2103
|
+
if (unreadable) note('仕事の読込', `仕事を読めなかった事業${unreadable}件。0件とは確認できません。`, 'warning', `仕事が読めない事業 ${unreadable}件`);
|
|
2104
|
+
if (partial) note('仕事の範囲', `一部だけ読めた事業${partial}件。件数は読めた範囲です。`, 'warning', `仕事が一部だけ読めた事業 ${partial}件`);
|
|
2105
|
+
if (needs) note('仕事の記録', `読めた未完了の仕事${needs}件(${cities}事業)に、記録から把握できていないことがあります。仕事の失敗や停止を意味しません。`, 'attention', `記録の要確認 ${needs}件・${cities}事業`);
|
|
2106
|
+
}
|
|
1955
2107
|
}
|
|
1956
2108
|
|
|
1957
2109
|
function shortTimeText(value) {
|
|
@@ -2055,6 +2207,7 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
2055
2207
|
|
|
2056
2208
|
async function load() {
|
|
2057
2209
|
const businessesResult = await readJson(fetcher, '/api/extensions/world/businesses');
|
|
2210
|
+
if (disposed) return;
|
|
2058
2211
|
// A world drawn from an organization's Graph shows only that organization's judgments: the host keeps
|
|
2059
2212
|
// the others (another company's work, or unknown) back and sends only how many it kept back.
|
|
2060
2213
|
const organizationScoped = businessesResult.ok && businessesResult.data?.status === 'ok'
|
|
@@ -2073,8 +2226,13 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
2073
2226
|
readJson(fetcher, '/api/extensions/world/judgment-places'),
|
|
2074
2227
|
]);
|
|
2075
2228
|
}
|
|
2229
|
+
if (disposed) return;
|
|
2076
2230
|
notices.replaceChildren();
|
|
2077
|
-
const note = (label, textValue, tone = 'info') => {
|
|
2231
|
+
const note = (label, textValue, tone = 'info', summary = null) => {
|
|
2232
|
+
if (canvasUI) {
|
|
2233
|
+
setCanvasNote(`${label}:${summary ?? 'detail'}`, { label, text: textValue, tone, summary });
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2078
2236
|
const line = el(doc, 'p', { className: `bb-world-hud-line is-${tone}`, attrs: tone === 'info' ? {} : { role: 'alert' } });
|
|
2079
2237
|
line.append(el(doc, 'strong', { text: label }), el(doc, 'span', { text: textValue }));
|
|
2080
2238
|
notices.append(line);
|
|
@@ -2084,7 +2242,7 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
2084
2242
|
if (businessPayload?.status === 'ok' && businessPayload.vocabulary) worldVocabulary = businessPayload.vocabulary;
|
|
2085
2243
|
if (!businessesResult.ok || businessPayload?.status !== 'ok') {
|
|
2086
2244
|
const reason = businessesResult.ok ? `${BUSINESS_STATE_TEXT[businessPayload?.status] ?? '状態不明'}(${businessPayload?.reason ?? '理由不明'})` : `取得に失敗しました(${businessesResult.error})`;
|
|
2087
|
-
note('事業', `${reason}
|
|
2245
|
+
note('事業', `${reason}。事業の件数は未確認です。0件とは確認できません。`, 'warning', '事業:読めない(0件とは確認できません)');
|
|
2088
2246
|
} else {
|
|
2089
2247
|
const extra = [];
|
|
2090
2248
|
if (businessPayload.unplaced.length) extra.push(`親の事業がGraphに無い案件${businessPayload.unplaced.length}件は描いていません`);
|
|
@@ -2092,7 +2250,11 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
2092
2250
|
const sourceText = businessPayload.source.authority === 'organization_graph' ? `組織のGraph(${businessPayload.source.server})` : 'このMacのGraph';
|
|
2093
2251
|
const unclassified = businesses.filter((business) => !business.kind).length;
|
|
2094
2252
|
if (unclassified) extra.push(`分類の無い事業${unclassified}件は「分類なし」として描いています`);
|
|
2095
|
-
if (businessPayload.vocabulary?.terms === 'unavailable')
|
|
2253
|
+
if (businessPayload.vocabulary?.terms === 'unavailable') {
|
|
2254
|
+
extra.push('組織の用語を読めないため、分類の名前は語彙ファイルか値のままです');
|
|
2255
|
+
if (canvasMode) note('用語', '組織の用語を読めないため、分類の名前は語彙ファイルか値のままです。', 'warning', '組織の用語:読めない');
|
|
2256
|
+
}
|
|
2257
|
+
if (canvasMode && businessPayload.unplaced.length) note('案件', '親の事業がGraphに無い案件は描いていません。', 'warning', `親の事業が見つからない案件 ${businessPayload.unplaced.length}件`);
|
|
2096
2258
|
note('事業', `${sourceText}の事業${businesses.length}件・案件${businesses.reduce((sum, business) => sum + business.engagements.length, 0)}件。${extra.join('。')}${extra.length ? '。' : ''}`);
|
|
2097
2259
|
}
|
|
2098
2260
|
const home = homeResult.ok ? homeResult.data : null;
|
|
@@ -2100,8 +2262,8 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
2100
2262
|
const rows = readable ? home.delegation_map.rows : [];
|
|
2101
2263
|
if (!homeResult.ok || !readable) {
|
|
2102
2264
|
const reason = homeResult.ok ? home?.reason ?? home?.status ?? '状態不明' : homeResult.error;
|
|
2103
|
-
if (JUDGMENT_UNAVAILABLE_TEXT[reason]) note('判断', `${JUDGMENT_UNAVAILABLE_TEXT[reason]}
|
|
2104
|
-
else note('判断', `判断の記録を読めません(${reason}
|
|
2265
|
+
if (JUDGMENT_UNAVAILABLE_TEXT[reason]) note('判断', `${JUDGMENT_UNAVAILABLE_TEXT[reason]}。事業だけを描いています。`, 'warning', '判断の記録:未接続');
|
|
2266
|
+
else note('判断', `判断の記録を読めません(${reason})。判断の記録の件数は未確認です。0件とは確認できません。`, 'warning', '判断の記録:読めない');
|
|
2105
2267
|
} else {
|
|
2106
2268
|
proofs = proofIndex(home);
|
|
2107
2269
|
judgmentsConnected = true;
|
|
@@ -2113,20 +2275,30 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
2113
2275
|
placement = groupJudgmentPlaces(known, businesses);
|
|
2114
2276
|
const placed = known.length - placement.unplaced.length;
|
|
2115
2277
|
const kept = withheld ? `この組織の事業で作業したと確かめられない判断${withheld}件は、この世界には出していません(「今日」で見られます)。` : '';
|
|
2116
|
-
note('判断', `${home.delegation_map.judged}件のうち${placed}件を作業した事業の判断所に、${placement.unplaced.length}件は事業が分からないため広場に。${kept}今あなたを待っている判断${waiting}件${waiting ? '(光の柱)' : ''}。`, waiting ? 'attention' : 'info');
|
|
2278
|
+
note('判断', `${home.delegation_map.judged}件のうち${placed}件を作業した事業の判断所に、${placement.unplaced.length}件は事業が分からないため広場に。${kept}今あなたを待っている判断${waiting}件${waiting ? '(光の柱)' : ''}。`, waiting ? 'attention' : 'info', waiting ? `あなたを待っている判断 ${waiting}件` : null);
|
|
2117
2279
|
} else {
|
|
2118
|
-
note('判断', `${home.delegation_map.judged}件。作業した場所を読めないため、事業には置かず広場の種類別だけで描いています(${placesResult.ok ? placesResult.data?.reason ?? '理由不明' : placesResult.error})。今あなたを待っている判断${waiting}件。`, 'warning');
|
|
2280
|
+
note('判断', `${home.delegation_map.judged}件。作業した場所を読めないため、事業には置かず広場の種類別だけで描いています(${placesResult.ok ? placesResult.data?.reason ?? '理由不明' : placesResult.error})。今あなたを待っている判断${waiting}件。`, 'warning', '判断の作業場所:読めない');
|
|
2119
2281
|
}
|
|
2120
2282
|
}
|
|
2121
2283
|
const rowItems = new Map(rows.map((row) => [row.key, Array.isArray(row.items) ? row.items : []]));
|
|
2122
2284
|
|
|
2285
|
+
knownBusinesses = businesses;
|
|
2286
|
+
canvasUI?.setCities(businesses, current.code);
|
|
2123
2287
|
if (!webglAvailable(doc)) {
|
|
2124
|
-
note('表示', 'この環境では3Dを表示できないため、一覧で出しています。', 'warning');
|
|
2125
|
-
|
|
2126
|
-
|
|
2288
|
+
note('表示', 'この環境では3Dを表示できないため、一覧で出しています。', 'warning', '3D表示を利用できません・一覧表示');
|
|
2289
|
+
if (canvasMode) {
|
|
2290
|
+
renderFallbackList(doc, stage, businesses, rows);
|
|
2291
|
+
showEmptyRail();
|
|
2292
|
+
restoreSelection();
|
|
2293
|
+
void loadWorkSummary(note);
|
|
2294
|
+
} else {
|
|
2295
|
+
stage.hidden = true;
|
|
2296
|
+
renderFallbackList(doc, wrap, businesses, rows);
|
|
2297
|
+
}
|
|
2127
2298
|
return;
|
|
2128
2299
|
}
|
|
2129
2300
|
scene = createScene({ doc, stage, labelsLayer, reducedMotion, onPick, onClear: clearAll, onEscape: goUp });
|
|
2301
|
+
scene.setSky(SKY_MODES[skyIndex][0]);
|
|
2130
2302
|
scene.buildPlaza(rows, rowItems, (row) => (rowItems.get(row.key) ?? []).filter((ref) => isWaiting(proofs.get(ref.decision_attempt_id))).length, Date.now());
|
|
2131
2303
|
scene.buildCities(layoutWorld(businesses), placement.byBusiness);
|
|
2132
2304
|
showChangesSinceLastVisit(note, businesses, rows);
|
|
@@ -2178,7 +2350,11 @@ export function createWorldView({ root, rail, page, document: explicitDocument,
|
|
|
2178
2350
|
void load();
|
|
2179
2351
|
return {
|
|
2180
2352
|
dispose() {
|
|
2353
|
+
disposed = true;
|
|
2181
2354
|
doc.removeEventListener?.('visibilitychange', onVisible);
|
|
2355
|
+
wrap.removeEventListener?.('keydown', onRootEscape, true);
|
|
2356
|
+
canvasUI?.dispose();
|
|
2357
|
+
district?.dispose();
|
|
2182
2358
|
scene?.dispose();
|
|
2183
2359
|
},
|
|
2184
2360
|
};
|
|
@@ -55,7 +55,7 @@ export function readReasonText(reason) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
const WORK_UNAVAILABLE_TEXT = Object.freeze({
|
|
58
|
-
task_store_not_connected: '
|
|
58
|
+
task_store_not_connected: 'この画面はタスクの正本に接続していないため、都市の中の仕事は描いていません。仕事の件数は未確認です。0件とは確認できません。',
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
function jst(value) {
|
|
@@ -182,7 +182,7 @@ function workStateBlocks(doc, { work, summary, onSelectGap, onSelectStatus }) {
|
|
|
182
182
|
export function workCityBlocks(doc, { business, work, onSelectGap, onSelectStatus, onReload }) {
|
|
183
183
|
if (!work) return [workspaceRailBlock(doc, { title: '仕事', content: { text: '仕事を読み込んでいます…' } })];
|
|
184
184
|
if (work.status !== 'ok') {
|
|
185
|
-
const text = WORK_UNAVAILABLE_TEXT[work.reason] ?? `仕事の記録を読めません(${work.reason ?? work.status}
|
|
185
|
+
const text = WORK_UNAVAILABLE_TEXT[work.reason] ?? `仕事の記録を読めません(${work.reason ?? work.status})。仕事の件数は未確認です。0件とは確認できません。`;
|
|
186
186
|
const content = [{ text }];
|
|
187
187
|
if (onReload && work.status !== 'not_connected') content.push(workspaceButton(doc, { text: 'もう一度読む', onClick: onReload }));
|
|
188
188
|
return [workspaceRailBlock(doc, { title: '仕事', content })];
|
|
@@ -209,7 +209,7 @@ export function workCityBlocks(doc, { business, work, onSelectGap, onSelectStatu
|
|
|
209
209
|
blocks.push(workspaceRailBlock(doc, {
|
|
210
210
|
title: '仕事',
|
|
211
211
|
content: [
|
|
212
|
-
{ text: `仕事の記録を${READ_STATE_TEXT[summary.state] ?? summary.state}(${readReasonText(summary.reason) ?? '理由不明'}
|
|
212
|
+
{ text: `仕事の記録を${READ_STATE_TEXT[summary.state] ?? summary.state}(${readReasonText(summary.reason) ?? '理由不明'})。仕事の件数は未確認です。0件とは確認できません。` },
|
|
213
213
|
onReload ? workspaceButton(doc, { text: 'もう一度読む', onClick: onReload }) : null,
|
|
214
214
|
],
|
|
215
215
|
}));
|