@takagaki/cortex-decisions-viewer 0.12.98 → 0.12.99
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 +3 -1
- package/dist/render.js +10 -0
- package/dist/story.js +86 -4
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -2362,7 +2362,9 @@ async function build(opts) {
|
|
|
2362
2362
|
// Decisions ディレクトリで実行すると pathspec に一致せず常に空になる
|
|
2363
2363
|
const aiHistory = (0, repo_1.collectAiChangeHistory)(process.cwd());
|
|
2364
2364
|
// ストーリーの日々の動き(#901 V2-5)。帳簿で on の日のファイルが無ければ null(キーごと出さない)
|
|
2365
|
-
const story = await (0, story_1.readStory)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null
|
|
2365
|
+
const story = await (0, story_1.readStory)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null,
|
|
2366
|
+
// 動きのない日を埋める終点=ビルドの時刻の日本時間の日付(時計を注入できる)
|
|
2367
|
+
(0, story_1.todayJst)(new Date(opts.generatedAt)));
|
|
2366
2368
|
// 案件アイコン(#348)。Cortex構成でなければ undefined(旧レイアウトには置き場が無い)
|
|
2367
2369
|
const projectIcon = await findProjectIcon(knowledgeRoot);
|
|
2368
2370
|
const site = {
|
package/dist/render.js
CHANGED
|
@@ -4741,6 +4741,8 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
|
|
|
4741
4741
|
.story-card {
|
|
4742
4742
|
border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 18px; background: var(--surface);
|
|
4743
4743
|
}
|
|
4744
|
+
.story-quiet { margin: -6px 0; padding: 0 18px; font-size: 12.5px; color: var(--muted); }
|
|
4745
|
+
.story-quiet-date { font-variant-numeric: tabular-nums; }
|
|
4744
4746
|
.story-card-heading { margin: 0 0 8px; font-size: 15px; }
|
|
4745
4747
|
.story-card-edit { display: inline-block; margin-top: 8px; font-size: 12.5px; }
|
|
4746
4748
|
.story-card .body h2 { font-size: 14px; margin: 14px 0 6px; }
|
|
@@ -8372,6 +8374,14 @@ ${calendarAddUrl.toString()}
|
|
|
8372
8374
|
function renderStory() {
|
|
8373
8375
|
var list = el("div", { class: "story-cards" });
|
|
8374
8376
|
(SITE.story || []).forEach(function (d) {
|
|
8377
|
+
// 動きのない日は薄い 1 行(日付+文言。畳んだ行は「9/10〜9/18」)
|
|
8378
|
+
if (d.quiet) {
|
|
8379
|
+
list.appendChild(el("div", { class: "story-quiet" }, [
|
|
8380
|
+
el("span", { class: "story-quiet-date", text: d.heading || d.date }),
|
|
8381
|
+
el("span", { text: " は動きはありませんでした" }),
|
|
8382
|
+
]));
|
|
8383
|
+
return;
|
|
8384
|
+
}
|
|
8375
8385
|
list.appendChild(el("section", { class: "story-card" }, [
|
|
8376
8386
|
el("h3", { class: "story-card-heading", text: d.heading || d.date }),
|
|
8377
8387
|
d.bodyHtml ? el("div", { class: "body", html: d.bodyHtml }) : null,
|
package/dist/story.js
CHANGED
|
@@ -3,10 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.STORY_MAX_FILE_BYTES = exports.STORY_EMPTY_LINE = exports.STORY_MAX_DAYS = exports.STORY_DAYS_DIR = exports.STORY_LEDGER_PATH = void 0;
|
|
6
|
+
exports.STORY_MAX_FILE_BYTES = exports.STORY_COLLAPSE_QUIET_DAYS = exports.STORY_EMPTY_LINE = exports.STORY_MAX_DAYS = exports.STORY_DAYS_DIR = exports.STORY_LEDGER_PATH = void 0;
|
|
7
7
|
exports.parseStoryLedger = parseStoryLedger;
|
|
8
8
|
exports.onDaysOf = onDaysOf;
|
|
9
9
|
exports.storyDayMarkdown = storyDayMarkdown;
|
|
10
|
+
exports.isEngineQuietDay = isEngineQuietDay;
|
|
11
|
+
exports.todayJst = todayJst;
|
|
12
|
+
exports.quietStoryRow = quietStoryRow;
|
|
13
|
+
exports.withQuietDays = withQuietDays;
|
|
10
14
|
exports.readStory = readStory;
|
|
11
15
|
/**
|
|
12
16
|
* ストーリーのタブ(#901 記憶 v2・V2-5)。engine V2-3 が書く日々の動き
|
|
@@ -56,6 +60,10 @@ const MACHINE_WORD_RE = /urn:|cortex-engine|sha=|<!--|-->|\.md\b|Days\/|story-da
|
|
|
56
60
|
const SUBHEADING_RE = /^#{2,6}\s/;
|
|
57
61
|
/** 伏せて本文が空になった日の 1 行 */
|
|
58
62
|
exports.STORY_EMPTY_LINE = "この日は表示できる行がありません。";
|
|
63
|
+
/** engine の「大きな動きなし」の日の行(`scripts/story/gates.mjs` の `QUIET_LINE`)。この 1 行だけの日は「動きのない日」と同じ見た目にそろえる */
|
|
64
|
+
const ENGINE_QUIET_LINE = "大きな動きなし";
|
|
65
|
+
/** 動きのない日がこの日数以上続いたら 1 行に畳む(分類 B・`docs/product-decisions.md` 2026-09-29) */
|
|
66
|
+
exports.STORY_COLLAPSE_QUIET_DAYS = 3;
|
|
59
67
|
/** 1 枚の上限(これを超えるファイルは読まない・分類 B。engine の 1 枚は 4 KiB + 人の追記) */
|
|
60
68
|
exports.STORY_MAX_FILE_BYTES = 64 * 1024;
|
|
61
69
|
/** 帳簿を読む(投げない)。無い → `{ missing }`、壊れ・版違い → `{ broken }` */
|
|
@@ -161,6 +169,71 @@ function storyDayMarkdown(content, day) {
|
|
|
161
169
|
// 伏せて空になった日は、見出しだけのカードにしない
|
|
162
170
|
return { heading: heading, markdown: md || exports.STORY_EMPTY_LINE };
|
|
163
171
|
}
|
|
172
|
+
/** 「大きな動きなし」の 1 行だけの日か(人の追記が 1 行でもあれば違う。機械の印・空行は数えない) */
|
|
173
|
+
function isEngineQuietDay(content) {
|
|
174
|
+
const lines = String(content ?? "").replace(/^\ufeff/, "").split("\n").map((l) => l.replace(/\r$/, "").trim());
|
|
175
|
+
const at = lines.findIndex((l) => l === HUMAN_HEADING);
|
|
176
|
+
const main = (at < 0 ? lines : lines.slice(0, at)).filter((l) => l !== "" && !STORY_MARKER_RE.test(l) && !/^#\s/.test(l));
|
|
177
|
+
const human = at < 0 ? [] : lines.slice(at + 1).filter((l) => l !== "" && !STORY_MARKER_RE.test(l));
|
|
178
|
+
return human.length === 0 && main.length === 1 && main[0].replace(/^[-*]\s+/, "") === ENGINE_QUIET_LINE;
|
|
179
|
+
}
|
|
180
|
+
const WEEKDAYS = ["日", "月", "火", "水", "木", "金", "土"];
|
|
181
|
+
const utcMs = (day) => Date.parse(`${day}T00:00:00Z`);
|
|
182
|
+
const dayOfMs = (ms) => new Date(ms).toISOString().slice(0, 10);
|
|
183
|
+
/** 日本時間の今日(`YYYY-MM-DD`)。時計は注入できる */
|
|
184
|
+
function todayJst(now = new Date()) {
|
|
185
|
+
// 不正な時刻でビルドを落とさない(実時計に倒す)
|
|
186
|
+
if (Number.isNaN(now.getTime()))
|
|
187
|
+
now = new Date();
|
|
188
|
+
return dayOfMs(now.getTime() + 9 * 3600 * 1000);
|
|
189
|
+
}
|
|
190
|
+
const mdOf = (day) => `${Number(day.slice(5, 7))}/${Number(day.slice(8, 10))}`;
|
|
191
|
+
/**
|
|
192
|
+
* 動きのない日の行(純関数)。`from`〜`to`(新しい方が `to`)を 1 行にする。1 日なら `2026-09-18(金)`、
|
|
193
|
+
* 続くなら `9/10〜9/18`。文言(「は動きはありませんでした」)は画面が付ける
|
|
194
|
+
*/
|
|
195
|
+
function quietStoryRow(from, to) {
|
|
196
|
+
const heading = from === to ? `${to}(${WEEKDAYS[new Date(utcMs(to)).getUTCDay()]})` : `${mdOf(from)}〜${mdOf(to)}`;
|
|
197
|
+
return { date: to, ...(from === to ? {} : { fromDate: from }), heading: heading, bodyHtml: "", quiet: true };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* ページのある日(`pages`)の間と、一番古いページから `today` までの日付の抜けを、
|
|
201
|
+
* 動きのない日の行で埋めて新しい順に返す。連続する `STORY_COLLAPSE_QUIET_DAYS` 日以上は 1 行に畳む。
|
|
202
|
+
* 🔴 ページより新しい日(今日まで)も埋める。`pages` が空なら空(タブを出さない)
|
|
203
|
+
*/
|
|
204
|
+
function withQuietDays(pages, today) {
|
|
205
|
+
if (!pages.length)
|
|
206
|
+
return [];
|
|
207
|
+
const have = new Map(pages.map((p) => [p.date, p]));
|
|
208
|
+
const dates = pages.map((p) => p.date);
|
|
209
|
+
const oldest = dates.reduce((a, d) => (d < a ? d : a));
|
|
210
|
+
const newest = dates.reduce((a, d) => (d > a ? d : a));
|
|
211
|
+
const last = today > newest ? today : newest;
|
|
212
|
+
const out = [];
|
|
213
|
+
let run = []; // 新しい日から並ぶ動きのない日
|
|
214
|
+
const flush = () => {
|
|
215
|
+
if (!run.length)
|
|
216
|
+
return;
|
|
217
|
+
if (run.length >= exports.STORY_COLLAPSE_QUIET_DAYS)
|
|
218
|
+
out.push(quietStoryRow(run[run.length - 1], run[0]));
|
|
219
|
+
else
|
|
220
|
+
for (const d of run)
|
|
221
|
+
out.push(quietStoryRow(d, d));
|
|
222
|
+
run = [];
|
|
223
|
+
};
|
|
224
|
+
for (let ms = utcMs(last); ms >= utcMs(oldest); ms -= 86400000) {
|
|
225
|
+
const d = dayOfMs(ms);
|
|
226
|
+
const page = have.get(d);
|
|
227
|
+
if (page) {
|
|
228
|
+
flush();
|
|
229
|
+
out.push(page);
|
|
230
|
+
}
|
|
231
|
+
else
|
|
232
|
+
run.push(d);
|
|
233
|
+
}
|
|
234
|
+
flush();
|
|
235
|
+
return out;
|
|
236
|
+
}
|
|
164
237
|
/**
|
|
165
238
|
* 案件のストーリーを読む。**載せる日が無ければ `null`**(タブも器も出さない)。
|
|
166
239
|
*
|
|
@@ -168,7 +241,9 @@ function storyDayMarkdown(content, day) {
|
|
|
168
241
|
*/
|
|
169
242
|
async function readStory(repoRoot, renderMarkdown,
|
|
170
243
|
// その日のファイルを GitHub で開く URL(リポジトリ相対パス → URL。リポジトリの URL が無い案件は null)
|
|
171
|
-
editUrlOf = () => null
|
|
244
|
+
editUrlOf = () => null,
|
|
245
|
+
// 日本時間の今日(テストは時計を注入する)
|
|
246
|
+
today = todayJst()) {
|
|
172
247
|
let text;
|
|
173
248
|
try {
|
|
174
249
|
text = await node_fs_1.promises.readFile(node_path_1.default.join(repoRoot, exports.STORY_LEDGER_PATH), "utf8");
|
|
@@ -229,7 +304,10 @@ editUrlOf = () => null) {
|
|
|
229
304
|
}
|
|
230
305
|
files.sort((a, b) => (a.day < b.day ? 1 : a.day > b.day ? -1 : 0));
|
|
231
306
|
const out = [];
|
|
232
|
-
for (const f of files
|
|
307
|
+
for (const f of files) {
|
|
308
|
+
// 上限は「動きのある日」の枚数で数える(「大きな動きなし」の日で枠を使わない)
|
|
309
|
+
if (out.length >= exports.STORY_MAX_DAYS)
|
|
310
|
+
break;
|
|
233
311
|
let raw;
|
|
234
312
|
try {
|
|
235
313
|
raw = await node_fs_1.promises.readFile(f.abs, "utf8");
|
|
@@ -237,9 +315,13 @@ editUrlOf = () => null) {
|
|
|
237
315
|
catch {
|
|
238
316
|
continue;
|
|
239
317
|
}
|
|
318
|
+
// 「大きな動きなし」だけの日は、ページの無い日と同じ「動きはありませんでした」の行にそろえる
|
|
319
|
+
if (isEngineQuietDay(raw))
|
|
320
|
+
continue;
|
|
240
321
|
const { heading, markdown } = storyDayMarkdown(raw, f.day);
|
|
241
322
|
const editUrl = editUrlOf(f.rel);
|
|
242
323
|
out.push({ date: f.day, heading: heading, bodyHtml: await renderMarkdown(markdown), ...(editUrl ? { editUrl: editUrl } : {}) });
|
|
243
324
|
}
|
|
244
|
-
|
|
325
|
+
// 一番古い「動きのある日」から今日まで、日付の抜けを動きのない日の行で埋める。動きのある日が 1 つも無ければ出さない
|
|
326
|
+
return out.length ? withQuietDays(out, today) : null;
|
|
245
327
|
}
|
package/package.json
CHANGED