@takagaki/cortex-decisions-viewer 0.12.103 → 0.12.105
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/build.js +6 -2
- package/dist/rederivation.js +39 -27
- package/dist/render.js +154 -212
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -2361,12 +2361,14 @@ async function build(opts) {
|
|
|
2361
2361
|
// 走るのは latestGeneratedPaths と同じ **リポジトリルート**(process.cwd())——
|
|
2362
2362
|
// Decisions ディレクトリで実行すると pathspec に一致せず常に空になる
|
|
2363
2363
|
const aiHistory = (0, repo_1.collectAiChangeHistory)(process.cwd());
|
|
2364
|
+
// ストーリー(#901 V2-5)の日本時間の今日。日・週・月の3つで同じ日にそろえるため1回だけ計算する
|
|
2365
|
+
const storyToday = (0, story_1.todayJst)(new Date(opts.generatedAt));
|
|
2364
2366
|
// ストーリーの日々の動き(#901 V2-5)。帳簿で on の日のファイルが無ければ null(キーごと出さない)
|
|
2365
2367
|
const story = await (0, story_1.readStory)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null,
|
|
2366
2368
|
// 動きのない日を埋める終点=ビルドの時刻の日本時間の日付(時計を注入できる)
|
|
2367
|
-
|
|
2369
|
+
storyToday);
|
|
2368
2370
|
// 週・月のあらすじ(#901)。週・月のファイルが無ければ null(キーごと出さない=今までと同じ画面)
|
|
2369
|
-
const storyDigest = await (0, story_1.readStoryDigest)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null,
|
|
2371
|
+
const storyDigest = await (0, story_1.readStoryDigest)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null, storyToday);
|
|
2370
2372
|
// 案件アイコン(#348)。Cortex構成でなければ undefined(旧レイアウトには置き場が無い)
|
|
2371
2373
|
const projectIcon = await findProjectIcon(knowledgeRoot);
|
|
2372
2374
|
const site = {
|
|
@@ -2428,6 +2430,8 @@ async function build(opts) {
|
|
|
2428
2430
|
// 載せる日が無い案件(帳簿なし・壊れ・shadow の日だけ・ファイルなし)はキーごと出さない
|
|
2429
2431
|
story: story ?? undefined,
|
|
2430
2432
|
storyDigest: storyDigest ?? undefined,
|
|
2433
|
+
// ストーリーのタブが出る案件だけ(日・週・月のどちらも無ければ出さない=空の器を通さない)
|
|
2434
|
+
storyToday: story || storyDigest ? storyToday : undefined,
|
|
2431
2435
|
};
|
|
2432
2436
|
// サムネイル(デザイン画面等)をサイトに同梱し、ノードのパスをサイト内相対に書き換える
|
|
2433
2437
|
await node_fs_1.promises.mkdir(outAbs, { recursive: true });
|
package/dist/rederivation.js
CHANGED
|
@@ -573,17 +573,19 @@ function rederivedBandOf(recordPath, status, ledgers, nowShaOf, verdict, miss) {
|
|
|
573
573
|
* - 同じ活性度なら元の並び(日付降順 → タイトル昇順がそのまま残る)
|
|
574
574
|
* - 用語集だけ(関係者の並びは `engagement.ts`・便 14b。Decisions と Rules は対象外)
|
|
575
575
|
*
|
|
576
|
-
* ## 強さの段(#790・engine #727)
|
|
576
|
+
* ## 強さの段(#790・engine #727・仕様書 Q5)
|
|
577
577
|
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
578
|
+
* 🔴 **活性度が主**(engine 仕様書 Q5・2026-10-01 オーナー)。強さ(使った人の数 → 場面の数 → 資料の下地の辞書式降順)は、
|
|
579
|
+
* 活性度で並べた**後に、活性度がちょうど同じ語どうしの順にだけ**使う(`breakTiesByStrength`)。栓 `term_gate` を on にしても
|
|
580
|
+
* 強さ主には並べ替えない。🔴 **engine の目次(`scripts/memory-index.mjs` の `termRows`)の写し**
|
|
580
581
|
* ——画面と目次で同じ語の順番が食い違うと「目次の上から N 件を AI に読ませる」構想(#544)の
|
|
581
582
|
* 土台が崩れるので、**目次と同じ並び**にする。一致は `glossary-activity-parity.test.mjs` の
|
|
582
583
|
* `[強さ]`(engine を実際に呼んで焼いた表)が見る。
|
|
583
584
|
* - 🔴 **帳簿の `state` が `"on"` のときだけ**(`readRederivationLedgers` がそれ以外を空にする)
|
|
584
585
|
* - 強さを持つ語が 1 件も無ければ活性度の並びのまま(帳簿が無い案件は 1 バイトも触らない)
|
|
585
|
-
* -
|
|
586
|
-
* -
|
|
586
|
+
* - 🔴 活性度が違う語の順は 1 つも変えない。活性度を知らない語どうしも同点の 1 塊
|
|
587
|
+
* - 同点の塊の中で、強さを知らない語は塊の**末尾**へ。強さも同じなら活性度の並びのまま(安定)
|
|
588
|
+
* - 🔴 活性度の帳簿が無く強さだけある案件でも並べる(全部が「知らない」の 1 塊になり、その中を強さで並べる)
|
|
587
589
|
*
|
|
588
590
|
* ℹ️ `ledgers` と `statusOf` は**受け取り続ける**。`ledgers` は活性度のために要る。
|
|
589
591
|
* `statusOf` は並びに使わなくなったが、**引数から落とすと「群分けを足し戻す」退行が
|
|
@@ -596,21 +598,18 @@ function rederivedBandOf(recordPath, status, ledgers, nowShaOf, verdict, miss) {
|
|
|
596
598
|
function orderByActivity(records, pathOf, statusOf, ledgers, nowIso) {
|
|
597
599
|
if (!records.length)
|
|
598
600
|
return records;
|
|
599
|
-
// 🔴 **強さの段は活性度の段の後に必ず通す**(活性度が 1
|
|
600
|
-
return
|
|
601
|
+
// 🔴 **強さの段は活性度の段の後に必ず通す**(活性度が 1 件も無い案件でも・上の「強さの段」)。強さは同点の中の順だけ(Q5)
|
|
602
|
+
return breakTiesByStrength(rankByActivity(records, pathOf, ledgers, nowIso), pathOf, ledgers, nowIso);
|
|
603
|
+
}
|
|
604
|
+
/** 1 件の活性度(用語集の区画の外は「知らない」)。活性度の段と強さの段で同じ値を使う */
|
|
605
|
+
function activityOf(r, pathOf, ledgers, nowIso) {
|
|
606
|
+
const p = pathOf(r);
|
|
607
|
+
return ORDER_SECTION_RE.test(p) ? activityScore(ledgers.activity.get(p), nowIso) : null;
|
|
601
608
|
}
|
|
602
609
|
/** 活性度の段。活性度を持つ語が 1 件も無ければ**渡された配列をそのまま返す** */
|
|
603
610
|
function rankByActivity(records, pathOf, ledgers, nowIso) {
|
|
604
611
|
// この区画が並べ替えの対象か(用語集で、活性度の行が 1 件以上あるか)
|
|
605
|
-
const scored = records.map((r, i) => {
|
|
606
|
-
const p = pathOf(r);
|
|
607
|
-
const inSection = ORDER_SECTION_RE.test(p);
|
|
608
|
-
return {
|
|
609
|
-
r: r,
|
|
610
|
-
i: i,
|
|
611
|
-
score: inSection ? activityScore(ledgers.activity.get(p), nowIso) : null,
|
|
612
|
-
};
|
|
613
|
-
});
|
|
612
|
+
const scored = records.map((r, i) => ({ r: r, i: i, score: activityOf(r, pathOf, ledgers, nowIso) }));
|
|
614
613
|
if (!scored.some((s) => s.score != null))
|
|
615
614
|
return records;
|
|
616
615
|
return scored
|
|
@@ -627,26 +626,30 @@ function rankByActivity(records, pathOf, ledgers, nowIso) {
|
|
|
627
626
|
.map((s) => s.r);
|
|
628
627
|
}
|
|
629
628
|
/**
|
|
630
|
-
* 強さの段(#790)。**engine `scripts/glossary/activity.mjs` の `
|
|
629
|
+
* 強さの段(#790・Q5)。**engine `scripts/glossary/activity.mjs` の `breakTiesByStrength` の写し**——塊の切り方・鍵・
|
|
631
630
|
* 「知らない」の扱いを 1 つでも変えると、画面と目次で同じ語の順番が割れる。
|
|
632
631
|
*
|
|
633
632
|
* - 強さを持つ語が 1 件も無ければ**渡された配列をそのまま返す**(`state` が on でない案件はここ)
|
|
634
|
-
* -
|
|
635
|
-
*
|
|
633
|
+
* - `records` は活性度の段を通した後の並び。**活性度がちょうど同じ語**(どちらも知らないも同じ)が隣り合う塊ごとに、
|
|
634
|
+
* 鍵 `[使った人の数, 場面の数, 下地]` の辞書式降順で並べ替える。塊の外へは 1 件も動かさない
|
|
635
|
+
* - 塊の中で知らない語(帳簿に無い・用語集の区画の外)は末尾へ、渡された順のまま。鍵も同じなら渡された順(安定)
|
|
636
636
|
*/
|
|
637
|
-
function
|
|
637
|
+
function breakTiesByStrength(records, pathOf, ledgers, nowIso) {
|
|
638
638
|
if (!ledgers.strength.size)
|
|
639
639
|
return records;
|
|
640
640
|
const scored = records.map((r, i) => {
|
|
641
641
|
const p = pathOf(r);
|
|
642
|
-
return {
|
|
642
|
+
return {
|
|
643
|
+
r: r,
|
|
644
|
+
i: i,
|
|
645
|
+
a: activityOf(r, pathOf, ledgers, nowIso),
|
|
646
|
+
s: ORDER_SECTION_RE.test(p) ? (ledgers.strength.get(p) ?? null) : null,
|
|
647
|
+
};
|
|
643
648
|
});
|
|
644
649
|
if (!scored.some((x) => x.s != null))
|
|
645
650
|
return records;
|
|
646
|
-
const key = (s) => [s.
|
|
647
|
-
|
|
648
|
-
.slice()
|
|
649
|
-
.sort((a, b) => {
|
|
651
|
+
const key = (s) => [s.people, s.scenes, s.base ? 1 : 0];
|
|
652
|
+
const byStrength = (a, b) => {
|
|
650
653
|
if ((a.s == null) !== (b.s == null))
|
|
651
654
|
return a.s == null ? 1 : -1;
|
|
652
655
|
if (a.s != null && b.s != null) {
|
|
@@ -657,8 +660,17 @@ function rankByStrength(records, pathOf, ledgers) {
|
|
|
657
660
|
return kb[k] - ka[k];
|
|
658
661
|
}
|
|
659
662
|
return a.i - b.i;
|
|
660
|
-
}
|
|
661
|
-
|
|
663
|
+
};
|
|
664
|
+
const out = [];
|
|
665
|
+
for (let start = 0; start < scored.length;) {
|
|
666
|
+
let end = start + 1;
|
|
667
|
+
while (end < scored.length && scored[end].a === scored[start].a)
|
|
668
|
+
end += 1;
|
|
669
|
+
for (const x of scored.slice(start, end).sort(byStrength))
|
|
670
|
+
out.push(x.r);
|
|
671
|
+
start = end;
|
|
672
|
+
}
|
|
673
|
+
return out;
|
|
662
674
|
}
|
|
663
675
|
/* ---------------------------------------------------------------------------
|
|
664
676
|
* 決定の「その後」の帳簿(#705・engine #509 便 4)
|
package/dist/render.js
CHANGED
|
@@ -4644,8 +4644,12 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
|
|
|
4644
4644
|
.container { max-width: 960px; margin: 0 auto; padding: 24px 20px 64px; }
|
|
4645
4645
|
|
|
4646
4646
|
/* ---- tabs ---- */
|
|
4647
|
-
|
|
4647
|
+
/* 🔴 幅が足りないときはタブの列だけが横にスクロールする(ページ全体は横スクロールさせない・分類B 2026-09-30)。
|
|
4648
|
+
overflow-x を visible 以外にすると overflow-y も auto 相当になり、.tab の margin-bottom: -1px と
|
|
4649
|
+
border-bottom の重なり(下の下線用の1px)が縦のスクロールバーとして見えかねないので、overflow-y は明示的に止める */
|
|
4650
|
+
.tabs { display: flex; gap: 6px; margin-bottom: 18px; border-bottom: 1px solid var(--border); overflow-x: auto; overflow-y: hidden; }
|
|
4648
4651
|
.tab {
|
|
4652
|
+
flex: 0 0 auto; white-space: nowrap;
|
|
4649
4653
|
padding: 8px 16px; font-size: 14px; font-weight: 600; color: var(--muted);
|
|
4650
4654
|
border: none; background: none; cursor: pointer;
|
|
4651
4655
|
border-bottom: 2px solid transparent; margin-bottom: -1px;
|
|
@@ -4693,16 +4697,16 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
|
|
|
4693
4697
|
}
|
|
4694
4698
|
.graph-reset:hover { background: rgba(30,40,80,0.9); border-color: rgba(160,180,230,0.7); }
|
|
4695
4699
|
|
|
4696
|
-
/* ---- knowledge view switch ---- */
|
|
4697
|
-
.view-switch {
|
|
4700
|
+
/* ---- knowledge view switch(ストーリーの日次/週次/月次タブも同じ見た目・分類B 2026-09-30) ---- */
|
|
4701
|
+
.view-switch, .story-sub-tabs {
|
|
4698
4702
|
display: inline-flex; border: 1px solid rgba(255,255,255,0.75); border-radius: 999px; overflow: hidden;
|
|
4699
4703
|
margin-bottom: 14px; background: rgba(255,255,255,0.45);
|
|
4700
4704
|
-webkit-backdrop-filter: blur(10px) saturate(150%); backdrop-filter: blur(10px) saturate(150%);
|
|
4701
4705
|
box-shadow: 0 2px 8px rgba(30,60,120,0.1), inset 0 1px 0 rgba(255,255,255,0.85);
|
|
4702
4706
|
}
|
|
4703
|
-
.view-btn { border: none; background: none; padding: 7px 18px; font-size: 13px; font-weight: 600; color: var(--muted); cursor: pointer; }
|
|
4704
|
-
.view-btn.active { background: rgba(37,99,235,0.85); color: #fff; box-shadow: inset 0 1px 0 rgba(255,255,255,0.35); }
|
|
4705
|
-
.view-btn:not(.active):hover { color: var(--accent); }
|
|
4707
|
+
.view-btn, .story-sub-tab { border: none; background: none; padding: 7px 18px; font-size: 13px; font-weight: 600; color: var(--muted); cursor: pointer; }
|
|
4708
|
+
.view-btn.active, .story-sub-tab.active { background: rgba(37,99,235,0.85); color: #fff; box-shadow: inset 0 1px 0 rgba(255,255,255,0.35); }
|
|
4709
|
+
.view-btn:not(.active):hover, .story-sub-tab:not(.active):hover { color: var(--accent); }
|
|
4706
4710
|
|
|
4707
4711
|
/* ---- frontmatter panel ---- */
|
|
4708
4712
|
.fm-panel { margin-top: 32px; }
|
|
@@ -4881,32 +4885,11 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
|
|
|
4881
4885
|
.story-card-edit { display: inline-block; margin-top: 8px; font-size: 12.5px; }
|
|
4882
4886
|
.story-card .body h2 { font-size: 14px; margin: 14px 0 6px; }
|
|
4883
4887
|
.story-card .body h3 { font-size: 13.5px; margin: 12px 0 6px; }
|
|
4884
|
-
/*
|
|
4885
|
-
.story-now { display: flex; flex-direction: column; gap: 14px; margin-bottom: 20px; }
|
|
4886
|
-
.story-section-heading { margin: 4px 0 0; font-size: 15px; }
|
|
4888
|
+
/* 週・月のあらすじカード(#901)。週・月のファイルがある案件だけ使う */
|
|
4887
4889
|
.story-empty { margin: 0; font-size: 13px; color: var(--muted); }
|
|
4888
4890
|
.story-phase { margin: 0 0 8px; font-weight: 600; }
|
|
4889
4891
|
.story-phase-label { display: inline-block; margin-right: 8px; font-size: 12px; color: var(--muted); font-weight: 400; }
|
|
4890
4892
|
.story-digest-human h4 { font-size: 13.5px; margin: 12px 0 6px; }
|
|
4891
|
-
.story-switch { display: flex; gap: 8px; margin: 0 0 12px; }
|
|
4892
|
-
.story-switch-btn {
|
|
4893
|
-
padding: 3px 16px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface);
|
|
4894
|
-
color: inherit; font-size: 13px; cursor: pointer;
|
|
4895
|
-
}
|
|
4896
|
-
.story-switch-btn.active { background: #1d4ed8; border-color: #1d4ed8; color: #fff; font-weight: 600; }
|
|
4897
|
-
.story-nav { display: flex; justify-content: space-between; margin: 0 0 10px; }
|
|
4898
|
-
.story-nav-btn {
|
|
4899
|
-
padding: 3px 12px; border-radius: var(--radius); border: 1px solid var(--border); background: var(--surface);
|
|
4900
|
-
color: inherit; font-size: 12.5px; cursor: pointer;
|
|
4901
|
-
}
|
|
4902
|
-
.story-nav-btn:disabled { opacity: 0.4; cursor: default; }
|
|
4903
|
-
.story-browse { display: flex; flex-direction: column; gap: 12px; }
|
|
4904
|
-
.story-days-fold { margin-top: 10px; }
|
|
4905
|
-
.story-days-fold > summary, .story-week-fold > summary { cursor: pointer; font-size: 13px; font-weight: 600; }
|
|
4906
|
-
.story-days-fold > .story-cards { margin-top: 10px; }
|
|
4907
|
-
.story-week-fold { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 18px; background: var(--surface); }
|
|
4908
|
-
.story-week-fold > .story-digest, .story-week-fold > .story-empty { margin-top: 10px; }
|
|
4909
|
-
.story-month-pick { display: flex; align-items: center; gap: 8px; font-size: 13px; }
|
|
4910
4893
|
/* ---- Home(案件の自己記述)---- */
|
|
4911
4894
|
/* 未記入の警告。**画面でいちばん強い赤にする**——27案件中24案件が未記入のまま
|
|
4912
4895
|
毎晩のAI生成が走っている状態で、控えめに出しても読まれない */
|
|
@@ -8421,6 +8404,10 @@ ${calendarAddUrl.toString()}
|
|
|
8421
8404
|
return new URLSearchParams(location.search).get(name);
|
|
8422
8405
|
}
|
|
8423
8406
|
function tabUrl(tabKey) {
|
|
8407
|
+
// ストーリーは日次/週次/月次のサブタブを持つので、そのURL(storySubUrl)に任せる。
|
|
8408
|
+
// ここで素の "?tab=story" を返すと、他のタブへ移って戻ったときに URL だけ既定(日次)に戻り、
|
|
8409
|
+
// 画面(storySub は変えていない)と URL がずれる(分類B 2026-09-30)
|
|
8410
|
+
if (tabKey === "story") return storySubUrl(storySub);
|
|
8424
8411
|
return tabKey === DEFAULT_TAB ? "?" : "?tab=" + encodeURIComponent(tabKey);
|
|
8425
8412
|
}
|
|
8426
8413
|
/** そのタブが立っているか(案件によっては操作タブが無い) */
|
|
@@ -8501,12 +8488,13 @@ ${calendarAddUrl.toString()}
|
|
|
8501
8488
|
}
|
|
8502
8489
|
|
|
8503
8490
|
// ---------- タブナビ ----------
|
|
8491
|
+
// 🔴 件数(「決定事項(466)」等)は出さない・文字は折り返さない(nowrap)。幅が足りなければ
|
|
8492
|
+
// タブの列だけが横にスクロールする(ページ全体は横にスクロールさせない・分類B 2026-09-30)
|
|
8504
8493
|
function renderTabs() {
|
|
8505
8494
|
if (TABS.length <= 1) return;
|
|
8506
8495
|
var tabs = el("div", { class: "tabs" });
|
|
8507
8496
|
TABS.forEach(function (t) {
|
|
8508
|
-
var
|
|
8509
|
-
var b = el("button", { class: "tab" + (listState.tab === t.key ? " active" : ""), text: label });
|
|
8497
|
+
var b = el("button", { class: "tab" + (listState.tab === t.key ? " active" : ""), text: t.label });
|
|
8510
8498
|
b.addEventListener("click", function () { switchTab(t.key); });
|
|
8511
8499
|
tabs.appendChild(b);
|
|
8512
8500
|
});
|
|
@@ -8544,17 +8532,13 @@ ${calendarAddUrl.toString()}
|
|
|
8544
8532
|
text: "決定・用語・レポートを起点に、つながり(根拠・出典・リンク)を1列ずつ辿る表示です。行をクリックすると右に次の列が開きます。",
|
|
8545
8533
|
},
|
|
8546
8534
|
"story": {
|
|
8547
|
-
text: "
|
|
8548
|
-
},
|
|
8549
|
-
"story-digest": {
|
|
8550
|
-
text: "今週の日々と、週・月のあらすじです。直したいことや足したいことは、各カードの「人の追記」に書けます。",
|
|
8535
|
+
text: "このプロジェクトの日々の動きを、日次・週次・月次に分けてまとめた記録です。直したいことや足したいことは、各カードの「GitHub で追記する」から書けます。",
|
|
8551
8536
|
},
|
|
8552
8537
|
"dir:レポート": {
|
|
8553
8538
|
text: "レポートの生成は終了し、履歴として凍結されています(レポートはSlack配信に一本化されました)。以下は過去に生成されたレコードです。",
|
|
8554
8539
|
},
|
|
8555
8540
|
};
|
|
8556
8541
|
var infoKey = listState.tab === "graph" ? (knowledgeView === "map" ? "graph" : "explorer") : listState.tab;
|
|
8557
|
-
if (infoKey === "story" && SITE.storyDigest) infoKey = "story-digest";
|
|
8558
8542
|
var info = INFO[infoKey];
|
|
8559
8543
|
if (!info) return;
|
|
8560
8544
|
var bar = el("div", { class: "info-bar" }, [ el("span", { text: info.text }) ]);
|
|
@@ -8578,13 +8562,34 @@ ${calendarAddUrl.toString()}
|
|
|
8578
8562
|
renderGenericList();
|
|
8579
8563
|
}
|
|
8580
8564
|
|
|
8581
|
-
// ---------- ストーリー(#901 V2-5
|
|
8582
|
-
//
|
|
8565
|
+
// ---------- ストーリー(#901 V2-5・日次/週次/月次タブへの分割は分類B 2026-09-30) ----------
|
|
8566
|
+
// 「日次」「週次」「月次」の3つのサブタブ。既定は日次。選んだサブタブは URL に添えて覚える
|
|
8567
|
+
// (?tab=story&sub=weekly のように・分類B 2026-09-30)。本文は build の関所(marked → sanitize)を
|
|
8583
8568
|
// 通した HTML で、印のコメントと機械の語の行は build が除いてある。見出しは textContent で入れる
|
|
8584
|
-
|
|
8585
|
-
|
|
8569
|
+
var STORY_SUB_DEFAULT = "daily";
|
|
8570
|
+
var STORY_SUBS = [["daily", "日次"], ["weekly", "週次"], ["monthly", "月次"]];
|
|
8571
|
+
var storySub = STORY_SUB_DEFAULT;
|
|
8572
|
+
|
|
8573
|
+
function storySubUrl(sub) {
|
|
8574
|
+
return sub === STORY_SUB_DEFAULT ? "?tab=story" : "?tab=story&sub=" + encodeURIComponent(sub);
|
|
8575
|
+
}
|
|
8576
|
+
function switchStorySub(sub) {
|
|
8577
|
+
if (storySub === sub) return;
|
|
8578
|
+
storySub = sub;
|
|
8579
|
+
history.replaceState(null, "", storySubUrl(sub));
|
|
8580
|
+
renderList();
|
|
8581
|
+
}
|
|
8582
|
+
|
|
8583
|
+
// 日ごとのカード(新しい日が上=build が並べた順のまま)。動きのない日は「◯月◯日は動きはありませんでした」の1行
|
|
8584
|
+
function renderStoryDaily() {
|
|
8585
|
+
var days = SITE.story || [];
|
|
8586
8586
|
var list = el("div", { class: "story-cards" });
|
|
8587
|
-
(
|
|
8587
|
+
if (!days.length) {
|
|
8588
|
+
list.appendChild(el("p", { class: "story-empty", text: "日々の記録はまだありません" }));
|
|
8589
|
+
app.appendChild(list);
|
|
8590
|
+
return;
|
|
8591
|
+
}
|
|
8592
|
+
days.forEach(function (d) {
|
|
8588
8593
|
// 動きのない日は薄い 1 行(日付+文言。畳んだ行は「9/10〜9/18」)
|
|
8589
8594
|
if (d.quiet) {
|
|
8590
8595
|
list.appendChild(el("div", { class: "story-quiet" }, [
|
|
@@ -8603,184 +8608,114 @@ ${calendarAddUrl.toString()}
|
|
|
8603
8608
|
app.appendChild(list);
|
|
8604
8609
|
}
|
|
8605
8610
|
|
|
8606
|
-
//
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
var
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8611
|
+
// 週・月のあらすじカード(局面・流れ・人の追記・GitHub のリンク)。見出し・局面は build が作った文字列(heading/phase)をそのまま使う
|
|
8612
|
+
function storyDigestCard(entry, phaseLabel) {
|
|
8613
|
+
var human = entry.humanHtml ? el("div", { class: "story-digest-human" }, [
|
|
8614
|
+
el("h4", { text: "人の追記" }),
|
|
8615
|
+
el("div", { class: "body", html: entry.humanHtml }),
|
|
8616
|
+
]) : null;
|
|
8617
|
+
return el("section", { class: "story-card" }, [
|
|
8618
|
+
el("h3", { class: "story-card-heading", text: entry.heading }),
|
|
8619
|
+
entry.phase ? el("p", { class: "story-phase" }, [
|
|
8620
|
+
el("span", { class: "story-phase-label", text: phaseLabel }),
|
|
8621
|
+
el("span", { text: entry.phase }),
|
|
8622
|
+
]) : null,
|
|
8623
|
+
entry.flowHtml ? el("div", { class: "body", html: entry.flowHtml }) : null,
|
|
8624
|
+
human,
|
|
8625
|
+
entry.editUrl ? el("a", { class: "story-card-edit", href: entry.editUrl, target: "_blank", rel: "noopener", text: "GitHub で追記する" }) : null,
|
|
8626
|
+
]);
|
|
8627
|
+
}
|
|
8628
|
+
|
|
8629
|
+
// 週・月の抜けを埋めるための日付計算(UTC 固定で計算し YYYY-MM-DD/YYYY-MM の文字列を返す。
|
|
8630
|
+
// これらの文字列は桁がそろっているので、そのまま辞書式に比べれば新旧が分かる)
|
|
8631
|
+
function storyUtcMs(day) { return Date.parse(day + "T00:00:00Z"); }
|
|
8632
|
+
function storyDayOfMs(ms) { return new Date(ms).toISOString().slice(0, 10); }
|
|
8633
|
+
function storyMondayOf(day) {
|
|
8634
|
+
var dow = new Date(storyUtcMs(day)).getUTCDay(); // 0=日 … 6=土
|
|
8635
|
+
var back = dow === 0 ? 6 : dow - 1;
|
|
8636
|
+
return storyDayOfMs(storyUtcMs(day) - back * 86400000);
|
|
8637
|
+
}
|
|
8638
|
+
function storyPrevWeekKey(monday) { return storyDayOfMs(storyUtcMs(monday) - 7 * 86400000); }
|
|
8639
|
+
function storyPrevMonthKey(month) {
|
|
8640
|
+
var y = parseInt(month.slice(0, 4), 10);
|
|
8641
|
+
var m = parseInt(month.slice(5, 7), 10) - 1;
|
|
8642
|
+
if (m < 1) { m = 12; y -= 1; }
|
|
8643
|
+
return y + "-" + (m < 10 ? "0" + m : String(m));
|
|
8644
|
+
}
|
|
8645
|
+
function storyWeekLabel(monday) {
|
|
8646
|
+
var md = function (d) { var p = d.split("-"); return parseInt(p[1], 10) + "/" + parseInt(p[2], 10); };
|
|
8647
|
+
return md(monday) + "〜" + md(storyDayOfMs(storyUtcMs(monday) + 6 * 86400000)) + " の週";
|
|
8648
|
+
}
|
|
8649
|
+
// その案件でストーリーが分かっている一番古い日付(YYYY-MM-DD)。月・週・日のどれかに載っている
|
|
8650
|
+
// 一番古いものを使う(無ければ null=今の区切りだけ見る)。この日より前の区切りには案内を足さない
|
|
8651
|
+
function storyEarliestKnownDate() {
|
|
8652
|
+
var dates = [];
|
|
8653
|
+
((SITE.storyDigest && SITE.storyDigest.months) || []).forEach(function (m) { dates.push(m.key + "-01"); });
|
|
8654
|
+
((SITE.storyDigest && SITE.storyDigest.weeks) || []).forEach(function (w) { dates.push(w.key); });
|
|
8655
|
+
(SITE.story || []).forEach(function (d) { dates.push(d.date); });
|
|
8656
|
+
if (!dates.length) return null;
|
|
8657
|
+
return dates.reduce(function (a, b) { return b < a ? b : a; });
|
|
8658
|
+
}
|
|
8659
|
+
|
|
8660
|
+
// 週のあらすじを新しい週から順に。「今週」と「週が閉じたがまだあらすじが無い週」は見出し+案内の1行で埋め、
|
|
8661
|
+
// 欠けなく新しい週から並べる(その案件でストーリーが始まる前の週までは埋めない)
|
|
8662
|
+
function renderStoryWeekly() {
|
|
8663
|
+
var weeks = (SITE.storyDigest && SITE.storyDigest.weeks) || [];
|
|
8664
|
+
var list = el("div", { class: "story-cards" });
|
|
8665
|
+
var curMonday = SITE.storyToday ? storyMondayOf(SITE.storyToday) : null;
|
|
8666
|
+
if (curMonday) {
|
|
8667
|
+
var have = {};
|
|
8668
|
+
weeks.forEach(function (w) { have[w.key] = true; });
|
|
8669
|
+
var earliest = storyEarliestKnownDate();
|
|
8670
|
+
var earliestMonday = earliest ? storyMondayOf(earliest) : curMonday;
|
|
8671
|
+
for (var wk = curMonday; !have[wk] && wk >= earliestMonday; wk = storyPrevWeekKey(wk)) {
|
|
8672
|
+
list.appendChild(el("section", { class: "story-card" }, [
|
|
8673
|
+
el("h3", { class: "story-card-heading", text: storyWeekLabel(wk) }),
|
|
8674
|
+
el("p", { class: "story-empty", text: "週が閉じて 2 日後の夜に作ります" }),
|
|
8675
|
+
]));
|
|
8654
8676
|
}
|
|
8655
|
-
return rows;
|
|
8656
|
-
}
|
|
8657
|
-
function pageCount(from, to) {
|
|
8658
|
-
var n = 0;
|
|
8659
|
-
for (var ms = storyMs(from); ms <= storyMs(to); ms += 86400000) if (pages[storyDay(ms)]) n++;
|
|
8660
|
-
return n;
|
|
8661
8677
|
}
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
el("h4", { text: "人の追記" }),
|
|
8667
|
-
el("div", { class: "body", html: entry.humanHtml }),
|
|
8668
|
-
]) : null;
|
|
8669
|
-
return el("div", { class: "story-digest" }, [
|
|
8670
|
-
entry.phase ? el("p", { class: "story-phase" }, [
|
|
8671
|
-
el("span", { class: "story-phase-label", text: phaseLabel }),
|
|
8672
|
-
el("span", { text: entry.phase }),
|
|
8673
|
-
]) : null,
|
|
8674
|
-
entry.flowHtml ? el("div", { class: "body", html: entry.flowHtml }) : null,
|
|
8675
|
-
human,
|
|
8676
|
-
entry.editUrl ? el("a", { class: "story-card-edit", href: entry.editUrl, target: "_blank", rel: "noopener", text: "GitHub で追記する" }) : null,
|
|
8677
|
-
]);
|
|
8678
|
-
}
|
|
8679
|
-
var WEEK_EMPTY = "あらすじはまだありません(週が閉じて 2 日後の夜に作ります)";
|
|
8680
|
-
var MONTH_EMPTY = "あらすじはまだありません(月が閉じて 3 日後の夜に作ります)";
|
|
8681
|
-
function weekEnd(mon) {
|
|
8682
|
-
var sun = storyDay(storyMs(mon) + 6 * 86400000);
|
|
8683
|
-
return sun < today ? sun : today;
|
|
8684
|
-
}
|
|
8685
|
-
function daysFold(mon) {
|
|
8686
|
-
var rows = dayRows(mon, weekEnd(mon), mon === thisMon);
|
|
8687
|
-
if (!rows.length) return null;
|
|
8688
|
-
return el("details", { class: "story-days-fold" }, [
|
|
8689
|
-
el("summary", { text: "この週の日々(" + pageCount(mon, weekEnd(mon)) + " 日)" }),
|
|
8690
|
-
el("div", { class: "story-cards" }, rows),
|
|
8691
|
-
]);
|
|
8678
|
+
if (!weeks.length && !list.children.length) {
|
|
8679
|
+
list.appendChild(el("p", { class: "story-empty", text: "あらすじはまだありません(週が閉じて 2 日後の夜に作ります)" }));
|
|
8680
|
+
} else {
|
|
8681
|
+
weeks.forEach(function (w) { list.appendChild(storyDigestCard(w, "この週の局面")); });
|
|
8692
8682
|
}
|
|
8683
|
+
app.appendChild(list);
|
|
8684
|
+
}
|
|
8693
8685
|
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
monSet[lastMon] = true;
|
|
8710
|
-
Object.keys(weekOf).forEach(function (k) { if (k < thisMon) monSet[k] = true; });
|
|
8711
|
-
Object.keys(pages).forEach(function (d) { var k = storyMonday(d); if (k < thisMon) monSet[k] = true; });
|
|
8712
|
-
var mons = Object.keys(monSet).sort().reverse();
|
|
8713
|
-
// 月の並び(新しい月から): あらすじのある月・動きのある日のある月
|
|
8714
|
-
var mSet = {};
|
|
8715
|
-
Object.keys(monthOf).forEach(function (k) { mSet[k] = true; });
|
|
8716
|
-
Object.keys(pages).forEach(function (d) { mSet[d.slice(0, 7)] = true; });
|
|
8717
|
-
var months = Object.keys(mSet).sort().reverse();
|
|
8718
|
-
|
|
8719
|
-
var view = "week";
|
|
8720
|
-
var weekAt = mons.indexOf(lastMon);
|
|
8721
|
-
var monthAt = months.length ? months[0] : null;
|
|
8722
|
-
var btnWeek = el("button", { class: "story-switch-btn", type: "button", text: "週" });
|
|
8723
|
-
var btnMonth = el("button", { class: "story-switch-btn", type: "button", text: "月" });
|
|
8724
|
-
var browse = el("div", { class: "story-browse" });
|
|
8725
|
-
app.appendChild(el("div", { class: "story-switch" }, [btnWeek, btnMonth]));
|
|
8726
|
-
app.appendChild(browse);
|
|
8727
|
-
|
|
8728
|
-
function fillWeek() {
|
|
8729
|
-
var mon = mons[weekAt];
|
|
8730
|
-
var prev = el("button", { class: "story-nav-btn", type: "button", text: "← 前の週" });
|
|
8731
|
-
var next = el("button", { class: "story-nav-btn", type: "button", text: "次の週 →" });
|
|
8732
|
-
if (weekAt >= mons.length - 1) prev.disabled = true;
|
|
8733
|
-
if (weekAt <= 0) next.disabled = true;
|
|
8734
|
-
prev.addEventListener("click", function () { if (weekAt < mons.length - 1) { weekAt++; fill(); } });
|
|
8735
|
-
next.addEventListener("click", function () { if (weekAt > 0) { weekAt--; fill(); } });
|
|
8736
|
-
browse.appendChild(el("div", { class: "story-nav" }, [prev, next]));
|
|
8737
|
-
browse.appendChild(el("section", { class: "story-card story-week" }, [
|
|
8738
|
-
el("h3", { class: "story-card-heading", text: storyWeekLabel(mon) }),
|
|
8739
|
-
digestBody(weekOf[mon], "この週の局面", WEEK_EMPTY),
|
|
8740
|
-
daysFold(mon),
|
|
8741
|
-
]));
|
|
8742
|
-
}
|
|
8743
|
-
function fillMonth() {
|
|
8744
|
-
if (monthAt === null) return;
|
|
8745
|
-
var sel = el("select", { class: "story-month-select", "aria-label": "月を選ぶ" });
|
|
8746
|
-
months.forEach(function (m) {
|
|
8747
|
-
var o = el("option", { value: m, text: storyMonthLabel(m) });
|
|
8748
|
-
if (m === monthAt) o.selected = true;
|
|
8749
|
-
sel.appendChild(o);
|
|
8750
|
-
});
|
|
8751
|
-
sel.addEventListener("change", function () { monthAt = sel.value; fill(); });
|
|
8752
|
-
browse.appendChild(el("label", { class: "story-month-pick" }, [el("span", { text: "月を選ぶ" }), sel]));
|
|
8753
|
-
browse.appendChild(el("section", { class: "story-card story-month" }, [
|
|
8754
|
-
el("h3", { class: "story-card-heading", text: storyMonthLabel(monthAt) }),
|
|
8755
|
-
digestBody(monthOf[monthAt], "この月の局面", MONTH_EMPTY),
|
|
8756
|
-
]));
|
|
8757
|
-
// その月にかかる週(新しい週から。今週も入れる)
|
|
8758
|
-
var first = monthAt + "-01";
|
|
8759
|
-
var mon = storyMonday(first);
|
|
8760
|
-
var inMonth = [];
|
|
8761
|
-
for (var ms = storyMs(mon); storyDay(ms).slice(0, 7) <= monthAt && storyDay(ms) <= today; ms += 7 * 86400000) {
|
|
8762
|
-
var k = storyDay(ms);
|
|
8763
|
-
if (weekOf[k] || pageCount(k, weekEnd(k)) > 0) inMonth.push(k);
|
|
8764
|
-
}
|
|
8765
|
-
inMonth.reverse().forEach(function (k) {
|
|
8766
|
-
browse.appendChild(el("details", { class: "story-week-fold" }, [
|
|
8767
|
-
el("summary", { text: storyWeekLabel(k) }),
|
|
8768
|
-
digestBody(weekOf[k], "この週の局面", WEEK_EMPTY),
|
|
8769
|
-
daysFold(k),
|
|
8686
|
+
// 月のあらすじを新しい月から順に。「今月」と「月が閉じたがまだあらすじが無い月」は見出し+案内の1行で埋め、
|
|
8687
|
+
// 欠けなく新しい月から並べる(その案件でストーリーが始まる前の月までは埋めない)
|
|
8688
|
+
function renderStoryMonthly() {
|
|
8689
|
+
var months = (SITE.storyDigest && SITE.storyDigest.months) || [];
|
|
8690
|
+
var list = el("div", { class: "story-cards" });
|
|
8691
|
+
var curMonth = SITE.storyToday ? SITE.storyToday.slice(0, 7) : null;
|
|
8692
|
+
if (curMonth) {
|
|
8693
|
+
var have = {};
|
|
8694
|
+
months.forEach(function (m) { have[m.key] = true; });
|
|
8695
|
+
var earliest = storyEarliestKnownDate();
|
|
8696
|
+
var earliestMonth = earliest ? earliest.slice(0, 7) : curMonth;
|
|
8697
|
+
for (var mo = curMonth; !have[mo] && mo >= earliestMonth; mo = storyPrevMonthKey(mo)) {
|
|
8698
|
+
list.appendChild(el("section", { class: "story-card" }, [
|
|
8699
|
+
el("h3", { class: "story-card-heading", text: monthLabel(mo) }),
|
|
8700
|
+
el("p", { class: "story-empty", text: "月が閉じて 3 日後の夜に作ります" }),
|
|
8770
8701
|
]));
|
|
8771
|
-
}
|
|
8772
|
-
}
|
|
8773
|
-
function fill() {
|
|
8774
|
-
browse.innerHTML = "";
|
|
8775
|
-
btnWeek.className = "story-switch-btn" + (view === "week" ? " active" : "");
|
|
8776
|
-
btnMonth.className = "story-switch-btn" + (view === "month" ? " active" : "");
|
|
8777
|
-
btnWeek.setAttribute("aria-pressed", view === "week" ? "true" : "false");
|
|
8778
|
-
btnMonth.setAttribute("aria-pressed", view === "month" ? "true" : "false");
|
|
8779
|
-
if (view === "week") fillWeek(); else fillMonth();
|
|
8702
|
+
}
|
|
8780
8703
|
}
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8704
|
+
months.forEach(function (m) { list.appendChild(storyDigestCard(m, "この月の局面")); });
|
|
8705
|
+
app.appendChild(list);
|
|
8706
|
+
}
|
|
8707
|
+
|
|
8708
|
+
function renderStory() {
|
|
8709
|
+
var nav = el("div", { class: "story-sub-tabs" });
|
|
8710
|
+
STORY_SUBS.forEach(function (s) {
|
|
8711
|
+
var b = el("button", { class: "story-sub-tab" + (storySub === s[0] ? " active" : ""), type: "button", text: s[1] });
|
|
8712
|
+
b.addEventListener("click", function () { switchStorySub(s[0]); });
|
|
8713
|
+
nav.appendChild(b);
|
|
8714
|
+
});
|
|
8715
|
+
app.appendChild(nav);
|
|
8716
|
+
if (storySub === "weekly") renderStoryWeekly();
|
|
8717
|
+
else if (storySub === "monthly") renderStoryMonthly();
|
|
8718
|
+
else renderStoryDaily();
|
|
8784
8719
|
}
|
|
8785
8720
|
|
|
8786
8721
|
// 導入経緯(overview.adoption)の表示情報。Home見出し横のチップに出すラベルだけを返す。
|
|
@@ -21203,6 +21138,13 @@ ${calendarAddUrl.toString()}
|
|
|
21203
21138
|
// 値が入っていて知らない語だったときは今までどおり動かさない(壊れた ?tab= で
|
|
21204
21139
|
// 見ていた画面を飛ばさない・go() の行き先も ?tab= か ?id= のどちらかしか作らない)
|
|
21205
21140
|
else if (tab === null) listState.tab = DEFAULT_TAB;
|
|
21141
|
+
// ストーリーのサブタブ(日次/週次/月次)。?sub= が無い・知らない語なら既定(日次)
|
|
21142
|
+
if (listState.tab === "story") {
|
|
21143
|
+
var sub = getParam("sub");
|
|
21144
|
+
var validSub = false;
|
|
21145
|
+
STORY_SUBS.forEach(function (s) { if (s[0] === sub) validSub = true; });
|
|
21146
|
+
storySub = validSub ? sub : STORY_SUB_DEFAULT;
|
|
21147
|
+
}
|
|
21206
21148
|
renderList();
|
|
21207
21149
|
}
|
|
21208
21150
|
}
|
package/package.json
CHANGED