codex-work-receipt 0.9.0 → 0.10.0
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/CHANGELOG.md +10 -0
- package/README.en.md +13 -3
- package/README.md +13 -3
- package/docs/cli.en.md +40 -2
- package/docs/cli.md +40 -2
- package/docs/codex-skill.en.md +1 -1
- package/docs/codex-skill.md +1 -1
- package/docs/data-schema.en.md +3 -3
- package/docs/data-schema.md +3 -3
- package/docs/privacy.en.md +5 -0
- package/docs/privacy.md +5 -0
- package/package.json +2 -2
- package/skills/ai-work-receipt/SKILL.md +10 -5
- package/src/auto-runner.mjs +4 -4
- package/src/cli.mjs +64 -5
- package/src/core/args.mjs +59 -3
- package/src/core/fact-buckets.mjs +3 -3
- package/src/core/fact-identity.mjs +8 -4
- package/src/core/generator.mjs +15 -3
- package/src/core/metrics.mjs +9 -6
- package/src/core/presentation.mjs +54 -5
- package/src/core/project-identity.mjs +132 -0
- package/src/core/range.mjs +155 -8
- package/src/core/receipt-record.mjs +6 -1
- package/src/core/selector.mjs +146 -21
- package/src/parsers/codex.mjs +52 -7
- package/src/renderers/html.mjs +133 -21
package/src/renderers/html.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { formatDate, formatDuration, formatNumber, formatTime } from "../lib/tim
|
|
|
7
7
|
import {
|
|
8
8
|
buildCompensation,
|
|
9
9
|
DEFAULT_LOCALE,
|
|
10
|
+
getCustomSummaryNotice,
|
|
10
11
|
getReceiptCopy,
|
|
11
12
|
getRollingSummaryNotice,
|
|
12
13
|
getScopeLabel,
|
|
@@ -183,7 +184,7 @@ function renderInsights(record, copy, locale) {
|
|
|
183
184
|
|
|
184
185
|
function renderFeatureCommands(featureCopy, locale) {
|
|
185
186
|
const languageArgument = locale === "en" ? " --lang en" : "";
|
|
186
|
-
|
|
187
|
+
const tabs = featureCopy.groups.map((group, groupIndex) => {
|
|
187
188
|
const commands = group.commands.map((item) => {
|
|
188
189
|
const command = `npx codex-work-receipt@latest ${item.args}${languageArgument}`;
|
|
189
190
|
return `
|
|
@@ -198,13 +199,22 @@ function renderFeatureCommands(featureCopy, locale) {
|
|
|
198
199
|
</div>
|
|
199
200
|
</li>`;
|
|
200
201
|
}).join("");
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
const tabId = `feature-tab-${group.id}`;
|
|
203
|
+
const panelId = `feature-panel-${group.id}`;
|
|
204
|
+
return {
|
|
205
|
+
tab: `<button id="${tabId}" class="feature-tab" type="button" role="tab" aria-selected="${groupIndex === 0 ? "true" : "false"}" aria-controls="${panelId}" tabindex="${groupIndex === 0 ? "0" : "-1"}" data-feature-tab="${escapeHtml(group.id)}">${escapeHtml(group.title)}<span>${formatNumber(group.commands.length, locale)}</span></button>`,
|
|
206
|
+
panel: `
|
|
207
|
+
<section id="${panelId}" class="feature-panel" role="tabpanel" aria-labelledby="${tabId}" data-feature-panel="${escapeHtml(group.id)}">
|
|
204
208
|
<ul>${commands}
|
|
205
209
|
</ul>
|
|
206
|
-
</section
|
|
207
|
-
|
|
210
|
+
</section>`,
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
return `
|
|
214
|
+
<div class="feature-tabs" data-feature-tabs>
|
|
215
|
+
<div class="feature-tabs__list" role="tablist" aria-label="${escapeHtml(featureCopy.tabAria)}">${tabs.map((item) => item.tab).join("")}</div>
|
|
216
|
+
<div class="feature-tabs__panels">${tabs.map((item) => item.panel).join("")}</div>
|
|
217
|
+
</div>`;
|
|
208
218
|
}
|
|
209
219
|
|
|
210
220
|
export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUrl = null, transferFile = null }) {
|
|
@@ -218,11 +228,17 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
218
228
|
const displayDate = formatDate(endAt, timezone, locale);
|
|
219
229
|
const rangeStartDate = record.period.range_start_date || formatDateKey(record.period.start_at.slice(0, 10), "zh-CN").replaceAll("/", "-");
|
|
220
230
|
const rangeEndDate = record.period.range_end_date || formatDateKey(record.period.end_at.slice(0, 10), "zh-CN").replaceAll("/", "-");
|
|
221
|
-
const isCalendarScope = new Set(["today", "last-7-days", "this-week"]).has(record.source.scope)
|
|
231
|
+
const isCalendarScope = new Set(["today", "last-7-days", "this-week"]).has(record.source.scope)
|
|
232
|
+
|| (record.source.scope === "custom-range" && record.source.range_kind === "calendar-days");
|
|
222
233
|
const spansMultipleDates = rangeStartDate !== rangeEndDate;
|
|
223
|
-
const scopeLabel = getScopeLabel(record.source.scope, locale, record.source.hours
|
|
234
|
+
const scopeLabel = getScopeLabel(record.source.scope, locale, record.source.hours, {
|
|
235
|
+
rangeKind: record.source.range_kind,
|
|
236
|
+
filterKind: record.source.filter_kind,
|
|
237
|
+
});
|
|
224
238
|
const rollingSummaryNotice = record.source.scope === "last-hours"
|
|
225
239
|
? getRollingSummaryNotice(locale, record.source.hours)
|
|
240
|
+
: record.source.scope === "custom-range" && record.source.range_kind === "exact-time"
|
|
241
|
+
? getCustomSummaryNotice(locale)
|
|
226
242
|
: null;
|
|
227
243
|
const dateRange = spansMultipleDates
|
|
228
244
|
? `${formatDateKey(rangeStartDate, locale)}—${formatDateKey(rangeEndDate, locale)}`
|
|
@@ -542,30 +558,82 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
542
558
|
margin: 10px 0 12px;
|
|
543
559
|
color: var(--feature-muted);
|
|
544
560
|
}
|
|
545
|
-
.feature-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
561
|
+
.feature-tabs__list {
|
|
562
|
+
display: flex;
|
|
563
|
+
gap: 5px;
|
|
564
|
+
margin: 0 -2px 12px;
|
|
565
|
+
padding: 2px;
|
|
566
|
+
overflow-x: auto;
|
|
567
|
+
scrollbar-width: thin;
|
|
568
|
+
scrollbar-color: #b8b0a4 transparent;
|
|
549
569
|
}
|
|
550
|
-
.feature-
|
|
551
|
-
|
|
570
|
+
.feature-tab {
|
|
571
|
+
display: inline-flex;
|
|
572
|
+
align-items: center;
|
|
573
|
+
justify-content: center;
|
|
574
|
+
gap: 5px;
|
|
575
|
+
min-height: 34px;
|
|
576
|
+
padding: 7px 10px;
|
|
577
|
+
flex: 0 0 auto;
|
|
578
|
+
border: 1px solid var(--feature-line);
|
|
579
|
+
border-radius: 5px;
|
|
580
|
+
background: var(--feature-command);
|
|
552
581
|
color: var(--feature-muted);
|
|
582
|
+
cursor: pointer;
|
|
553
583
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
|
|
554
|
-
font-size:
|
|
584
|
+
font-size: 10px;
|
|
555
585
|
font-weight: 700;
|
|
556
|
-
|
|
557
|
-
|
|
586
|
+
line-height: 1.2;
|
|
587
|
+
white-space: nowrap;
|
|
558
588
|
}
|
|
559
|
-
.feature-
|
|
589
|
+
.feature-tab span {
|
|
590
|
+
min-width: 17px;
|
|
591
|
+
padding: 1px 4px;
|
|
592
|
+
border-radius: 999px;
|
|
593
|
+
background: rgba(40, 38, 32, .08);
|
|
594
|
+
font-size: 8px;
|
|
595
|
+
text-align: center;
|
|
596
|
+
}
|
|
597
|
+
.feature-tab[aria-selected="true"] {
|
|
598
|
+
border-color: var(--feature-ink);
|
|
599
|
+
background: var(--feature-ink);
|
|
600
|
+
color: #fff;
|
|
601
|
+
}
|
|
602
|
+
.feature-tab[aria-selected="true"] span { background: rgba(255, 255, 255, .18); }
|
|
603
|
+
.feature-tab:focus-visible {
|
|
604
|
+
outline: 2px solid #81786c;
|
|
605
|
+
outline-offset: 2px;
|
|
606
|
+
}
|
|
607
|
+
.feature-tabs[data-ready="true"] .feature-panel[hidden] { display: none; }
|
|
608
|
+
.feature-panel + .feature-panel {
|
|
609
|
+
margin-top: 14px;
|
|
610
|
+
padding-top: 13px;
|
|
611
|
+
border-top: 1px solid var(--feature-soft-line);
|
|
612
|
+
}
|
|
613
|
+
.feature-tabs[data-ready="true"] .feature-panel + .feature-panel {
|
|
614
|
+
margin-top: 0;
|
|
615
|
+
padding-top: 0;
|
|
616
|
+
border-top: 0;
|
|
617
|
+
}
|
|
618
|
+
.feature-panel ul {
|
|
619
|
+
display: grid;
|
|
620
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
621
|
+
column-gap: 12px;
|
|
560
622
|
margin: 0;
|
|
561
623
|
padding: 0;
|
|
562
624
|
list-style: none;
|
|
563
625
|
}
|
|
564
|
-
.feature-command
|
|
626
|
+
.feature-command {
|
|
627
|
+
min-width: 0;
|
|
565
628
|
margin-top: 10px;
|
|
566
629
|
padding-top: 10px;
|
|
567
630
|
border-top: 1px dashed var(--feature-soft-line);
|
|
568
631
|
}
|
|
632
|
+
.feature-command:nth-child(-n + 2) {
|
|
633
|
+
margin-top: 0;
|
|
634
|
+
padding-top: 0;
|
|
635
|
+
border-top: 0;
|
|
636
|
+
}
|
|
569
637
|
.feature-command__name {
|
|
570
638
|
display: block;
|
|
571
639
|
margin-bottom: 5px;
|
|
@@ -653,8 +721,8 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
653
721
|
}
|
|
654
722
|
@media (min-width: 1021px) {
|
|
655
723
|
.sidebar-features[open] {
|
|
656
|
-
width:
|
|
657
|
-
margin-left: -
|
|
724
|
+
width: 560px;
|
|
725
|
+
margin-left: -380px;
|
|
658
726
|
}
|
|
659
727
|
}
|
|
660
728
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -1133,6 +1201,12 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1133
1201
|
.structure-grid { grid-template-columns: 1fr; }
|
|
1134
1202
|
.sidebar { flex-direction: column; }
|
|
1135
1203
|
.sidebar-card { flex-basis: auto; }
|
|
1204
|
+
.feature-panel ul { grid-template-columns: 1fr; }
|
|
1205
|
+
.feature-command:nth-child(2) {
|
|
1206
|
+
margin-top: 10px;
|
|
1207
|
+
padding-top: 10px;
|
|
1208
|
+
border-top: 1px dashed var(--feature-soft-line);
|
|
1209
|
+
}
|
|
1136
1210
|
}
|
|
1137
1211
|
</style>
|
|
1138
1212
|
</head>
|
|
@@ -1277,6 +1351,44 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1277
1351
|
});
|
|
1278
1352
|
}
|
|
1279
1353
|
|
|
1354
|
+
const featureTabs = document.querySelector("[data-feature-tabs]");
|
|
1355
|
+
if (featureTabs) {
|
|
1356
|
+
const tabs = [...featureTabs.querySelectorAll("[data-feature-tab]")];
|
|
1357
|
+
const panels = [...featureTabs.querySelectorAll("[data-feature-panel]")];
|
|
1358
|
+
const availableTabs = new Set(tabs.map((tab) => tab.dataset.featureTab));
|
|
1359
|
+
function activateFeatureTab(value, { focus = false } = {}) {
|
|
1360
|
+
const selected = availableTabs.has(value) ? value : tabs[0]?.dataset.featureTab;
|
|
1361
|
+
tabs.forEach((tab) => {
|
|
1362
|
+
const active = tab.dataset.featureTab === selected;
|
|
1363
|
+
tab.setAttribute("aria-selected", String(active));
|
|
1364
|
+
tab.tabIndex = active ? 0 : -1;
|
|
1365
|
+
if (active && focus) {
|
|
1366
|
+
tab.focus();
|
|
1367
|
+
tab.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
panels.forEach((panel) => { panel.hidden = panel.dataset.featurePanel !== selected; });
|
|
1371
|
+
try { localStorage.setItem("codex-work-receipt-feature-tab", selected); } catch {}
|
|
1372
|
+
}
|
|
1373
|
+
let savedFeatureTab = null;
|
|
1374
|
+
try { savedFeatureTab = localStorage.getItem("codex-work-receipt-feature-tab"); } catch {}
|
|
1375
|
+
featureTabs.dataset.ready = "true";
|
|
1376
|
+
activateFeatureTab(savedFeatureTab || tabs[0]?.dataset.featureTab);
|
|
1377
|
+
tabs.forEach((tab, index) => {
|
|
1378
|
+
tab.addEventListener("click", () => activateFeatureTab(tab.dataset.featureTab));
|
|
1379
|
+
tab.addEventListener("keydown", (event) => {
|
|
1380
|
+
let nextIndex = null;
|
|
1381
|
+
if (event.key === "ArrowRight") nextIndex = (index + 1) % tabs.length;
|
|
1382
|
+
if (event.key === "ArrowLeft") nextIndex = (index - 1 + tabs.length) % tabs.length;
|
|
1383
|
+
if (event.key === "Home") nextIndex = 0;
|
|
1384
|
+
if (event.key === "End") nextIndex = tabs.length - 1;
|
|
1385
|
+
if (nextIndex === null) return;
|
|
1386
|
+
event.preventDefault();
|
|
1387
|
+
activateFeatureTab(tabs[nextIndex].dataset.featureTab, { focus: true });
|
|
1388
|
+
});
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1280
1392
|
function fallbackCopyText(value) {
|
|
1281
1393
|
const textarea = document.createElement("textarea");
|
|
1282
1394
|
textarea.value = value;
|