codex-work-receipt 0.9.0 → 0.10.1
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 +15 -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 +158 -24
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;
|
|
588
|
+
}
|
|
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;
|
|
558
596
|
}
|
|
559
|
-
.feature-
|
|
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;
|
|
@@ -651,10 +719,15 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
651
719
|
from { opacity: 0; transform: translateY(-3px); }
|
|
652
720
|
to { opacity: 1; transform: translateY(0); }
|
|
653
721
|
}
|
|
654
|
-
@media (min-width:
|
|
722
|
+
@media (min-width: 1120px) {
|
|
723
|
+
.sidebar {
|
|
724
|
+
align-items: center;
|
|
725
|
+
}
|
|
726
|
+
.sidebar-card {
|
|
727
|
+
width: 100%;
|
|
728
|
+
}
|
|
655
729
|
.sidebar-features[open] {
|
|
656
|
-
width:
|
|
657
|
-
margin-left: -120px;
|
|
730
|
+
width: clamp(360px, calc(100vw - 880px), 560px);
|
|
658
731
|
}
|
|
659
732
|
}
|
|
660
733
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -1101,7 +1174,7 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1101
1174
|
line-height: 1.6;
|
|
1102
1175
|
text-align: center;
|
|
1103
1176
|
}
|
|
1104
|
-
@media (max-width:
|
|
1177
|
+
@media (max-width: 1119px) {
|
|
1105
1178
|
.layout {
|
|
1106
1179
|
grid-template-columns: 1fr;
|
|
1107
1180
|
max-width: 540px;
|
|
@@ -1118,7 +1191,30 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1118
1191
|
flex: 1 1 160px;
|
|
1119
1192
|
min-width: 0;
|
|
1120
1193
|
}
|
|
1121
|
-
.sidebar-features {
|
|
1194
|
+
.sidebar-features {
|
|
1195
|
+
width: 100%;
|
|
1196
|
+
max-width: 100%;
|
|
1197
|
+
margin-inline: 0;
|
|
1198
|
+
flex-basis: 100%;
|
|
1199
|
+
}
|
|
1200
|
+
.feature-tabs__list {
|
|
1201
|
+
display: grid;
|
|
1202
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1203
|
+
overflow: visible;
|
|
1204
|
+
}
|
|
1205
|
+
.feature-tab {
|
|
1206
|
+
width: 100%;
|
|
1207
|
+
min-width: 0;
|
|
1208
|
+
white-space: normal;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
@media (max-width: 560px) {
|
|
1212
|
+
.feature-panel ul { grid-template-columns: 1fr; }
|
|
1213
|
+
.feature-command:nth-child(2) {
|
|
1214
|
+
margin-top: 10px;
|
|
1215
|
+
padding-top: 10px;
|
|
1216
|
+
border-top: 1px dashed var(--feature-soft-line);
|
|
1217
|
+
}
|
|
1122
1218
|
}
|
|
1123
1219
|
@media (max-width: 420px) {
|
|
1124
1220
|
.layout { padding-inline: 10px; }
|
|
@@ -1277,6 +1373,44 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1277
1373
|
});
|
|
1278
1374
|
}
|
|
1279
1375
|
|
|
1376
|
+
const featureTabs = document.querySelector("[data-feature-tabs]");
|
|
1377
|
+
if (featureTabs) {
|
|
1378
|
+
const tabs = [...featureTabs.querySelectorAll("[data-feature-tab]")];
|
|
1379
|
+
const panels = [...featureTabs.querySelectorAll("[data-feature-panel]")];
|
|
1380
|
+
const availableTabs = new Set(tabs.map((tab) => tab.dataset.featureTab));
|
|
1381
|
+
function activateFeatureTab(value, { focus = false } = {}) {
|
|
1382
|
+
const selected = availableTabs.has(value) ? value : tabs[0]?.dataset.featureTab;
|
|
1383
|
+
tabs.forEach((tab) => {
|
|
1384
|
+
const active = tab.dataset.featureTab === selected;
|
|
1385
|
+
tab.setAttribute("aria-selected", String(active));
|
|
1386
|
+
tab.tabIndex = active ? 0 : -1;
|
|
1387
|
+
if (active && focus) {
|
|
1388
|
+
tab.focus();
|
|
1389
|
+
tab.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
panels.forEach((panel) => { panel.hidden = panel.dataset.featurePanel !== selected; });
|
|
1393
|
+
try { localStorage.setItem("codex-work-receipt-feature-tab", selected); } catch {}
|
|
1394
|
+
}
|
|
1395
|
+
let savedFeatureTab = null;
|
|
1396
|
+
try { savedFeatureTab = localStorage.getItem("codex-work-receipt-feature-tab"); } catch {}
|
|
1397
|
+
featureTabs.dataset.ready = "true";
|
|
1398
|
+
activateFeatureTab(savedFeatureTab || tabs[0]?.dataset.featureTab);
|
|
1399
|
+
tabs.forEach((tab, index) => {
|
|
1400
|
+
tab.addEventListener("click", () => activateFeatureTab(tab.dataset.featureTab));
|
|
1401
|
+
tab.addEventListener("keydown", (event) => {
|
|
1402
|
+
let nextIndex = null;
|
|
1403
|
+
if (event.key === "ArrowRight") nextIndex = (index + 1) % tabs.length;
|
|
1404
|
+
if (event.key === "ArrowLeft") nextIndex = (index - 1 + tabs.length) % tabs.length;
|
|
1405
|
+
if (event.key === "Home") nextIndex = 0;
|
|
1406
|
+
if (event.key === "End") nextIndex = tabs.length - 1;
|
|
1407
|
+
if (nextIndex === null) return;
|
|
1408
|
+
event.preventDefault();
|
|
1409
|
+
activateFeatureTab(tabs[nextIndex].dataset.featureTab, { focus: true });
|
|
1410
|
+
});
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1280
1414
|
function fallbackCopyText(value) {
|
|
1281
1415
|
const textarea = document.createElement("textarea");
|
|
1282
1416
|
textarea.value = value;
|