codex-work-receipt 0.10.0 → 0.11.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 +15 -0
- package/assets/codex-pet/ai-work-receipt/window-pet.png.base64 +1 -0
- package/package.json +1 -1
- package/src/core/presentation.mjs +120 -2
- package/src/renderers/html.mjs +336 -65
- package/src/renderers/office-v6.css +2270 -0
package/src/renderers/html.mjs
CHANGED
|
@@ -19,9 +19,17 @@ const DOM_TO_IMAGE_SOURCE = fs.readFileSync(
|
|
|
19
19
|
process.env.CODEX_WORK_RECEIPT_DOM_TO_IMAGE || require.resolve("dom-to-image-more"),
|
|
20
20
|
"utf8",
|
|
21
21
|
);
|
|
22
|
+
const OFFICE_STYLE_SOURCE = fs.readFileSync(
|
|
23
|
+
new URL("./office-v6.css", import.meta.url),
|
|
24
|
+
"utf8",
|
|
25
|
+
);
|
|
22
26
|
const MODELFLARE_LOGO_DATA_URL = `data:image/png;base64,${fs.readFileSync(
|
|
23
27
|
new URL("../../docs/images/sponsors/modelflare-logo.png", import.meta.url),
|
|
24
28
|
).toString("base64")}`;
|
|
29
|
+
const TICKET_BUDDY_WINDOW_DATA_URL = `data:image/png;base64,${fs.readFileSync(
|
|
30
|
+
new URL("../../assets/codex-pet/ai-work-receipt/window-pet.png.base64", import.meta.url),
|
|
31
|
+
"utf8",
|
|
32
|
+
).trim()}`;
|
|
25
33
|
const MODELFLARE_URL = "https://modelflare.dev/sign-up?partner=OB9YXNSEEGOL";
|
|
26
34
|
|
|
27
35
|
function inlineScript(value) {
|
|
@@ -80,6 +88,15 @@ function formatSeconds(milliseconds, locale) {
|
|
|
80
88
|
return `${formatDecimal(Number(milliseconds || 0) / 1000, locale)}s`;
|
|
81
89
|
}
|
|
82
90
|
|
|
91
|
+
function getHourInTimezone(date, timezone) {
|
|
92
|
+
const hourPart = new Intl.DateTimeFormat("en-US", {
|
|
93
|
+
timeZone: timezone,
|
|
94
|
+
hour: "2-digit",
|
|
95
|
+
hourCycle: "h23",
|
|
96
|
+
}).formatToParts(date).find((item) => item.type === "hour");
|
|
97
|
+
return Number(hourPart?.value || 0);
|
|
98
|
+
}
|
|
99
|
+
|
|
83
100
|
function renderBreakdown(items, { key, labels = {}, unit, emptyLabel, locale }) {
|
|
84
101
|
if (!items.length) return `<p class="structure-empty">${escapeHtml(emptyLabel)}</p>`;
|
|
85
102
|
const maximum = Math.max(1, ...items.map((item) => Number(item.count || 0)));
|
|
@@ -225,6 +242,15 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
225
242
|
const startAt = new Date(record.period.start_at);
|
|
226
243
|
const endAt = new Date(record.period.end_at);
|
|
227
244
|
const timezone = record.period.timezone;
|
|
245
|
+
const generatedAt = new Date(record.generated_at);
|
|
246
|
+
const generatedDate = formatDate(generatedAt, timezone, locale);
|
|
247
|
+
const generatedTime = formatTime(generatedAt, timezone, locale);
|
|
248
|
+
const generatedHour = getHourInTimezone(generatedAt, timezone);
|
|
249
|
+
const generatedScene = generatedHour >= 6 && generatedHour < 18 ? "day" : "night";
|
|
250
|
+
const generatedShift = copy.office.shifts[generatedScene];
|
|
251
|
+
const shiftHeader = copy.office.shiftHeader.replace("{shift}", generatedShift);
|
|
252
|
+
const shiftSign = copy.office.shiftSign.replace("{shift}", generatedShift);
|
|
253
|
+
const shiftOnDuty = copy.office.shiftOnDuty.replace("{shift}", generatedShift);
|
|
228
254
|
const displayDate = formatDate(endAt, timezone, locale);
|
|
229
255
|
const rangeStartDate = record.period.range_start_date || formatDateKey(record.period.start_at.slice(0, 10), "zh-CN").replaceAll("/", "-");
|
|
230
256
|
const rangeEndDate = record.period.range_end_date || formatDateKey(record.period.end_at.slice(0, 10), "zh-CN").replaceAll("/", "-");
|
|
@@ -287,6 +313,22 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
287
313
|
.reduce((total, group) => total + group.commands.length, 0);
|
|
288
314
|
const featureCountLabel = copy.sidebar.features.countLabel
|
|
289
315
|
.replace("{count}", formatNumber(featureCommandCount, locale));
|
|
316
|
+
const commandDescription = copy.office.commandDescription
|
|
317
|
+
.replace("{count}", formatNumber(featureCommandCount, locale));
|
|
318
|
+
const officeConfig = JSON.stringify({
|
|
319
|
+
generatedScene,
|
|
320
|
+
shifts: copy.office.shifts,
|
|
321
|
+
shiftHeader: copy.office.shiftHeader,
|
|
322
|
+
shiftSign: copy.office.shiftSign,
|
|
323
|
+
shiftOnDuty: copy.office.shiftOnDuty,
|
|
324
|
+
switchedAutoDay: copy.office.switchedAutoDay,
|
|
325
|
+
switchedAutoNight: copy.office.switchedAutoNight,
|
|
326
|
+
switchedDay: copy.office.switchedDay,
|
|
327
|
+
switchedNight: copy.office.switchedNight,
|
|
328
|
+
windPause: copy.office.windPause,
|
|
329
|
+
windResume: copy.office.windResume,
|
|
330
|
+
workReceiptDescription: copy.office.workReceiptDescription,
|
|
331
|
+
}).replaceAll("<", "\\u003c");
|
|
290
332
|
const insights = renderInsights(record, copy, locale);
|
|
291
333
|
const rows = [
|
|
292
334
|
receiptRow(copy.rows.scope, scopeLabel),
|
|
@@ -346,10 +388,11 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
346
388
|
</div>`;
|
|
347
389
|
|
|
348
390
|
return `<!doctype html>
|
|
349
|
-
<html lang="${escapeHtml(copy.htmlLang)}" data-theme="${escapeHtml(record.presentation.default_theme)}">
|
|
391
|
+
<html lang="${escapeHtml(copy.htmlLang)}" data-theme="${escapeHtml(record.presentation.default_theme)}" data-scene="${generatedScene}">
|
|
350
392
|
<head>
|
|
351
393
|
<meta charset="utf-8">
|
|
352
394
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
395
|
+
<meta name="color-scheme" content="light dark">
|
|
353
396
|
<title>${escapeHtml(copy.pageTitle)} · ${escapeHtml(displayDate)}</title>
|
|
354
397
|
<style>
|
|
355
398
|
:root {
|
|
@@ -719,10 +762,15 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
719
762
|
from { opacity: 0; transform: translateY(-3px); }
|
|
720
763
|
to { opacity: 1; transform: translateY(0); }
|
|
721
764
|
}
|
|
722
|
-
@media (min-width:
|
|
765
|
+
@media (min-width: 1120px) {
|
|
766
|
+
.sidebar {
|
|
767
|
+
align-items: center;
|
|
768
|
+
}
|
|
769
|
+
.sidebar-card {
|
|
770
|
+
width: 100%;
|
|
771
|
+
}
|
|
723
772
|
.sidebar-features[open] {
|
|
724
|
-
width: 560px;
|
|
725
|
-
margin-left: -380px;
|
|
773
|
+
width: clamp(360px, calc(100vw - 880px), 560px);
|
|
726
774
|
}
|
|
727
775
|
}
|
|
728
776
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -1169,7 +1217,7 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1169
1217
|
line-height: 1.6;
|
|
1170
1218
|
text-align: center;
|
|
1171
1219
|
}
|
|
1172
|
-
@media (max-width:
|
|
1220
|
+
@media (max-width: 1119px) {
|
|
1173
1221
|
.layout {
|
|
1174
1222
|
grid-template-columns: 1fr;
|
|
1175
1223
|
max-width: 540px;
|
|
@@ -1186,7 +1234,30 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1186
1234
|
flex: 1 1 160px;
|
|
1187
1235
|
min-width: 0;
|
|
1188
1236
|
}
|
|
1189
|
-
.sidebar-features {
|
|
1237
|
+
.sidebar-features {
|
|
1238
|
+
width: 100%;
|
|
1239
|
+
max-width: 100%;
|
|
1240
|
+
margin-inline: 0;
|
|
1241
|
+
flex-basis: 100%;
|
|
1242
|
+
}
|
|
1243
|
+
.feature-tabs__list {
|
|
1244
|
+
display: grid;
|
|
1245
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1246
|
+
overflow: visible;
|
|
1247
|
+
}
|
|
1248
|
+
.feature-tab {
|
|
1249
|
+
width: 100%;
|
|
1250
|
+
min-width: 0;
|
|
1251
|
+
white-space: normal;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
@media (max-width: 560px) {
|
|
1255
|
+
.feature-panel ul { grid-template-columns: 1fr; }
|
|
1256
|
+
.feature-command:nth-child(2) {
|
|
1257
|
+
margin-top: 10px;
|
|
1258
|
+
padding-top: 10px;
|
|
1259
|
+
border-top: 1px dashed var(--feature-soft-line);
|
|
1260
|
+
}
|
|
1190
1261
|
}
|
|
1191
1262
|
@media (max-width: 420px) {
|
|
1192
1263
|
.layout { padding-inline: 10px; }
|
|
@@ -1201,19 +1272,143 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1201
1272
|
.structure-grid { grid-template-columns: 1fr; }
|
|
1202
1273
|
.sidebar { flex-direction: column; }
|
|
1203
1274
|
.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
|
-
}
|
|
1210
1275
|
}
|
|
1276
|
+
${OFFICE_STYLE_SOURCE}
|
|
1211
1277
|
</style>
|
|
1212
1278
|
</head>
|
|
1213
1279
|
<body>
|
|
1214
|
-
<div class="
|
|
1215
|
-
|
|
1216
|
-
|
|
1280
|
+
<div class="ambient" aria-hidden="true"><div class="stars"></div><div class="haze"></div></div>
|
|
1281
|
+
|
|
1282
|
+
<header class="world-header">
|
|
1283
|
+
<div class="office-id">
|
|
1284
|
+
<div class="office-id__mark">C</div>
|
|
1285
|
+
<div><strong id="officeShiftName">${escapeHtml(shiftHeader)}</strong><span>WORLD'S EDGE / RECEIPT OFFICE NO. 07</span></div>
|
|
1286
|
+
</div>
|
|
1287
|
+
|
|
1288
|
+
<nav class="primary-nav" aria-label="${escapeHtml(copy.office.primaryNavAria)}">
|
|
1289
|
+
<button class="primary-nav__button is-active" type="button" aria-current="page">${escapeHtml(copy.office.todayReceipt)}</button>
|
|
1290
|
+
<button class="primary-nav__button" type="button" data-planned-message="${escapeHtml(copy.office.ticketHousePlanned)}">${escapeHtml(copy.office.ticketHouse)}<small>${escapeHtml(copy.office.planned)}</small></button>
|
|
1291
|
+
<button class="primary-nav__button" type="button" data-planned-message="${escapeHtml(copy.office.badgeWallPlanned)}">${escapeHtml(copy.office.badgeWall)}<small>${escapeHtml(copy.office.planned)}</small></button>
|
|
1292
|
+
</nav>
|
|
1293
|
+
|
|
1294
|
+
<div class="header-controls">
|
|
1295
|
+
<div class="scene-mode-switch" role="group" aria-label="${escapeHtml(copy.office.shiftAria)}">
|
|
1296
|
+
<button type="button" data-scene-mode="auto" aria-pressed="true" title="${escapeHtml(copy.office.autoModeTitle)}">${escapeHtml(copy.office.autoMode)}</button>
|
|
1297
|
+
<button type="button" data-scene-mode="day" aria-pressed="false">${escapeHtml(copy.office.dayMode)}</button>
|
|
1298
|
+
<button type="button" data-scene-mode="night" aria-pressed="false">${escapeHtml(copy.office.nightMode)}</button>
|
|
1299
|
+
</div>
|
|
1300
|
+
|
|
1301
|
+
<button class="world-button command-drawer-toggle" id="commandDrawerToggle" type="button" aria-expanded="false" aria-controls="commandDrawer" title="${escapeHtml(copy.office.commandToggleTitle)}">
|
|
1302
|
+
<span class="command-symbol" aria-hidden="true">>_</span>
|
|
1303
|
+
<span>${escapeHtml(copy.sidebar.features.title)}</span>
|
|
1304
|
+
</button>
|
|
1305
|
+
|
|
1306
|
+
<button class="world-button" id="windToggle" type="button" aria-pressed="false" title="${escapeHtml(copy.office.windPause)}">
|
|
1307
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" aria-hidden="true"><path d="M3 8h10.5a3.5 3.5 0 1 0-3.1-5.1"/><path d="M3 12h16a3 3 0 1 1-2.6 4.5"/><path d="M3 16h7"/></svg>
|
|
1308
|
+
<span>${escapeHtml(copy.office.windPause)}</span>
|
|
1309
|
+
</button>
|
|
1310
|
+
</div>
|
|
1311
|
+
</header>
|
|
1312
|
+
|
|
1313
|
+
<main class="world">
|
|
1314
|
+
<section class="scene" aria-label="${escapeHtml(copy.office.sceneAria)}">
|
|
1315
|
+
<div class="scene-intro">
|
|
1316
|
+
<div class="arrival-shift"><i class="status-light"></i><span id="arrivalShiftLabel">${escapeHtml(shiftOnDuty)}</span></div>
|
|
1317
|
+
<p class="arrival-time">${escapeHtml(generatedDate)} · ${escapeHtml(generatedTime)} · ${escapeHtml(copy.office.arrivalSigned)}</p>
|
|
1318
|
+
<h1>${escapeHtml(copy.office.arrivalTitle)}<br><em>${escapeHtml(copy.office.arrivalEmphasis)}</em></h1>
|
|
1319
|
+
<p class="arrival-copy">${escapeHtml(copy.office.arrivalDescription)}</p>
|
|
1320
|
+
<div class="arrival-stats" aria-label="${escapeHtml(copy.office.arrivalSummaryAria)}">
|
|
1321
|
+
<span><strong>${escapeHtml(formatNumber(record.stats.session_count, locale))}</strong> ${escapeHtml(copy.office.sessionsSummary)}</span>
|
|
1322
|
+
<span><strong>${escapeHtml(formatNumber(record.stats.completed_turns, locale))}</strong> ${escapeHtml(copy.office.turnsSummary)}</span>
|
|
1323
|
+
<span><strong>${escapeHtml(formatDuration(record.stats.work_duration_ms))}</strong> ${escapeHtml(copy.office.durationSummary)}</span>
|
|
1324
|
+
</div>
|
|
1325
|
+
</div>
|
|
1326
|
+
<div class="moon" aria-hidden="true"></div>
|
|
1327
|
+
<div class="mountains" aria-hidden="true"></div>
|
|
1328
|
+
<div class="wind-lines" aria-hidden="true"></div>
|
|
1329
|
+
<div class="ambient-receipt" aria-hidden="true"><div class="ambient-paper"></div></div>
|
|
1330
|
+
<div class="grass" aria-hidden="true"></div>
|
|
1331
|
+
<div class="cliff" aria-hidden="true"></div>
|
|
1332
|
+
|
|
1333
|
+
<div class="office">
|
|
1334
|
+
<div class="antenna" aria-hidden="true"><i></i></div>
|
|
1335
|
+
<div class="roof" aria-hidden="true"></div>
|
|
1336
|
+
<div class="wall" aria-hidden="true"></div>
|
|
1337
|
+
<div class="office-sign" id="officeSceneSign">${escapeHtml(shiftSign)}</div>
|
|
1338
|
+
<div class="window">
|
|
1339
|
+
<img class="pet" src="${TICKET_BUDDY_WINDOW_DATA_URL}" alt="${escapeHtml(copy.office.petAlt)}">
|
|
1340
|
+
</div>
|
|
1341
|
+
<div class="door" aria-hidden="true"></div>
|
|
1342
|
+
<button class="printer" id="printerCommandToggle" type="button" aria-expanded="false" aria-controls="commandDrawer" title="${escapeHtml(copy.office.printerCommandTitle)}"><span>CWR / ${escapeHtml(copy.office.commandDrawerCompact)}</span></button>
|
|
1343
|
+
|
|
1344
|
+
<div class="office-info-cluster">
|
|
1345
|
+
<div class="notice-lamp" aria-hidden="true"><i></i></div>
|
|
1346
|
+
<div class="notice-title">${escapeHtml(copy.office.officeNotice)}</div>
|
|
1347
|
+
<a class="log-board" href="${escapeHtml(changelogUrl)}" target="_blank" rel="noopener noreferrer">
|
|
1348
|
+
<small>${escapeHtml(copy.office.changelogKicker)}</small>
|
|
1349
|
+
${escapeHtml(copy.office.changelogText)}
|
|
1350
|
+
</a>
|
|
1351
|
+
<a class="github-board github-star-link" href="${escapeHtml(githubStarPrompt.url)}" target="_blank" rel="noopener noreferrer">
|
|
1352
|
+
<small>${escapeHtml(copy.office.githubKicker)}</small>
|
|
1353
|
+
${escapeHtml(copy.office.githubText)}
|
|
1354
|
+
</a>
|
|
1355
|
+
<a class="sponsor-crate" href="${MODELFLARE_URL}" target="_blank" rel="noopener noreferrer">
|
|
1356
|
+
<img src="${MODELFLARE_LOGO_DATA_URL}" alt="${escapeHtml(copy.sidebar.sponsorAlt)}" width="38" height="38">
|
|
1357
|
+
<span><small>${escapeHtml(copy.office.sponsorKicker)}</small>ModelFlare<strong class="sponsor-offer">${escapeHtml(copy.office.sponsorOffer)}</strong></span>
|
|
1358
|
+
</a>
|
|
1359
|
+
</div>
|
|
1360
|
+
</div>
|
|
1361
|
+
</section>
|
|
1362
|
+
|
|
1363
|
+
<aside class="today-package" aria-label="${escapeHtml(copy.office.todayPackTitle)}">
|
|
1364
|
+
<div class="today-package__heading">
|
|
1365
|
+
<div><small>${escapeHtml(copy.office.todayPackKicker)}</small><strong>${escapeHtml(copy.office.todayPackTitle)}</strong></div>
|
|
1366
|
+
<span>${escapeHtml(generatedDate)}</span>
|
|
1367
|
+
</div>
|
|
1368
|
+
<button class="package-item is-active" type="button" id="workReceiptShortcut" aria-current="true">
|
|
1369
|
+
<span class="package-item__icon package-item__icon--receipt" aria-hidden="true">${locale === "en" ? "WR" : "票"}</span>
|
|
1370
|
+
<span><strong>${escapeHtml(copy.office.workReceipt)}</strong><small>${escapeHtml(copy.office.workReceiptDescription)}</small></span>
|
|
1371
|
+
<b>${escapeHtml(copy.office.delivered)}</b>
|
|
1372
|
+
</button>
|
|
1373
|
+
<button class="package-item" type="button" data-planned-message="${escapeHtml(copy.office.moodReceiptPlanned)}">
|
|
1374
|
+
<span class="package-item__icon package-item__icon--mood" aria-hidden="true">${locale === "en" ? "MO" : "心"}</span>
|
|
1375
|
+
<span><strong>${escapeHtml(copy.office.moodReceipt)}</strong><small>${escapeHtml(copy.office.moodReceiptDescription)}</small></span>
|
|
1376
|
+
<b>${escapeHtml(copy.office.planned)}</b>
|
|
1377
|
+
</button>
|
|
1378
|
+
<button class="package-item" type="button" data-planned-message="${escapeHtml(copy.office.badgesPlanned)}">
|
|
1379
|
+
<span class="package-item__icon package-item__icon--badge" aria-hidden="true">${locale === "en" ? "BD" : "章"}</span>
|
|
1380
|
+
<span><strong>${escapeHtml(copy.office.currentBadges)}</strong><small>${escapeHtml(copy.office.currentBadgesDescription)}</small></span>
|
|
1381
|
+
<b>${escapeHtml(copy.office.planned)}</b>
|
|
1382
|
+
</button>
|
|
1383
|
+
<div class="today-package__links">
|
|
1384
|
+
<button type="button" data-planned-message="${escapeHtml(copy.office.ticketHousePlanned)}">${escapeHtml(copy.office.enterTicketHouse)}</button>
|
|
1385
|
+
<button type="button" data-planned-message="${escapeHtml(copy.office.badgeWallPlanned)}">${escapeHtml(copy.office.viewBadgeWall)}</button>
|
|
1386
|
+
</div>
|
|
1387
|
+
</aside>
|
|
1388
|
+
|
|
1389
|
+
<aside aria-label="${escapeHtml(copy.office.commandDrawerAria)}">
|
|
1390
|
+
<details class="feature-dock" id="commandDrawer" data-feature-details>
|
|
1391
|
+
<summary class="feature-dock__summary">
|
|
1392
|
+
<span class="feature-dock__title">
|
|
1393
|
+
<span class="feature-dock__terminal" aria-hidden="true">>_</span>
|
|
1394
|
+
<span class="feature-dock__title-text">${escapeHtml(copy.sidebar.features.title)}</span>
|
|
1395
|
+
<span class="feature-dock__compact">${escapeHtml(copy.office.commandDrawerCompact)}</span>
|
|
1396
|
+
</span>
|
|
1397
|
+
<span class="feature-dock__meta">
|
|
1398
|
+
<span class="feature-dock__count">${escapeHtml(featureCountLabel)}</span>
|
|
1399
|
+
<svg class="feature-dock__chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"></path></svg>
|
|
1400
|
+
</span>
|
|
1401
|
+
</summary>
|
|
1402
|
+
<div class="feature-dock__body" data-feature-scroll>
|
|
1403
|
+
<p class="feature-dock__description">${escapeHtml(commandDescription)}</p>${featureCommands}
|
|
1404
|
+
<span class="feature-copy-status" data-feature-copy-status role="status" aria-live="polite"></span>
|
|
1405
|
+
</div>
|
|
1406
|
+
</details>
|
|
1407
|
+
</aside>
|
|
1408
|
+
|
|
1409
|
+
<section class="ticket-rail" aria-label="${escapeHtml(copy.receiptAria)}">
|
|
1410
|
+
<div class="paper-bridge" aria-hidden="true"></div>
|
|
1411
|
+
<div class="receipt-toolbar toolbar">
|
|
1217
1412
|
<nav class="theme-switcher" aria-label="${escapeHtml(copy.themeAria)}">
|
|
1218
1413
|
<button class="theme-button" type="button" data-theme-value="classic">${escapeHtml(copy.themes.classic)}</button>
|
|
1219
1414
|
<button class="theme-button" type="button" data-theme-value="diner">${escapeHtml(copy.themes.diner)}</button>
|
|
@@ -1225,6 +1420,9 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1225
1420
|
</div>
|
|
1226
1421
|
</div>
|
|
1227
1422
|
|
|
1423
|
+
<div class="ticket-sway">
|
|
1424
|
+
<div class="receipt-scope">
|
|
1425
|
+
|
|
1228
1426
|
<div class="export-sheet" id="receipt-export">
|
|
1229
1427
|
<article class="paper receipt" aria-label="${escapeHtml(copy.receiptAria)}">
|
|
1230
1428
|
<header class="brand">
|
|
@@ -1277,55 +1475,19 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1277
1475
|
<p class="transfer-note">${escapeHtml(copy.transferNote)}</p>
|
|
1278
1476
|
</section>
|
|
1279
1477
|
</div>
|
|
1280
|
-
|
|
1281
|
-
<p class="privacy">${escapeHtml(copy.privacy)}</p>
|
|
1282
|
-
</main>
|
|
1283
|
-
<aside class="sidebar" aria-label="${escapeHtml(copy.sidebar.aria)}">
|
|
1284
|
-
<div class="sidebar-card">
|
|
1285
|
-
<h3 class="sidebar-card__title">
|
|
1286
|
-
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.75.75 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"/></svg>
|
|
1287
|
-
${escapeHtml(copy.sidebar.supportTitle)}
|
|
1288
|
-
</h3>
|
|
1289
|
-
<p>${escapeHtml(copy.sidebar.supportDescription)}</p>
|
|
1290
|
-
<a class="github-star-link" href="${escapeHtml(githubStarPrompt.url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(githubStarPrompt.label)}</a>
|
|
1291
|
-
</div>
|
|
1292
|
-
<div class="sidebar-card">
|
|
1293
|
-
<h3 class="sidebar-card__title">
|
|
1294
|
-
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M11.93 8.5a4.002 4.002 0 0 1-7.86 0H.75a.75.75 0 0 1 0-1.5h3.32a4.002 4.002 0 0 1 7.86 0h3.32a.75.75 0 0 1 0 1.5Zm-1.43-.75a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z"/></svg>
|
|
1295
|
-
${escapeHtml(copy.sidebar.changelogTitle)}
|
|
1296
|
-
</h3>
|
|
1297
|
-
<a href="${escapeHtml(changelogUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(copy.sidebar.changelogLink)}</a>
|
|
1298
1478
|
</div>
|
|
1299
|
-
<div class="sidebar-card sidebar-sponsor">
|
|
1300
|
-
<span class="sidebar-sponsor__label">${escapeHtml(copy.sidebar.sponsorLabel)}</span>
|
|
1301
|
-
<a class="sidebar-sponsor__link" href="${MODELFLARE_URL}" target="_blank" rel="noopener noreferrer">
|
|
1302
|
-
<img src="${MODELFLARE_LOGO_DATA_URL}" alt="${escapeHtml(copy.sidebar.sponsorAlt)}" width="40" height="40">
|
|
1303
|
-
<span class="sidebar-sponsor__name">ModelFlare<br>modelflare.dev</span>
|
|
1304
|
-
</a>
|
|
1305
1479
|
</div>
|
|
1306
|
-
<
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
</span>
|
|
1312
|
-
<span class="sidebar-features__meta">
|
|
1313
|
-
<span class="sidebar-features__count">${escapeHtml(featureCountLabel)}</span>
|
|
1314
|
-
<svg class="sidebar-features__chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"></path></svg>
|
|
1315
|
-
</span>
|
|
1316
|
-
</summary>
|
|
1317
|
-
<div class="sidebar-features__body">
|
|
1318
|
-
<p class="sidebar-features__description">${escapeHtml(copy.sidebar.features.description)}</p>${featureCommands}
|
|
1319
|
-
<span class="feature-copy-status" data-feature-copy-status role="status" aria-live="polite"></span>
|
|
1320
|
-
</div>
|
|
1321
|
-
</details>
|
|
1322
|
-
</aside>
|
|
1323
|
-
</div>
|
|
1480
|
+
<p class="privacy">${escapeHtml(copy.privacy)}</p>
|
|
1481
|
+
</section>
|
|
1482
|
+
</main>
|
|
1483
|
+
|
|
1484
|
+
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
|
1324
1485
|
<script>${inlineScript(DOM_TO_IMAGE_SOURCE)}</script>
|
|
1325
1486
|
<script>
|
|
1326
1487
|
const exportConfig = ${exportConfig};
|
|
1327
1488
|
const transferConfig = ${transferConfig};
|
|
1328
1489
|
const featureConfig = ${featureConfig};
|
|
1490
|
+
const officeConfig = ${officeConfig};
|
|
1329
1491
|
const themes = new Set(["classic", "diner", "payroll"]);
|
|
1330
1492
|
const buttons = [...document.querySelectorAll("[data-theme-value]")];
|
|
1331
1493
|
function applyTheme(theme) {
|
|
@@ -1339,17 +1501,126 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1339
1501
|
try { savedTheme = localStorage.getItem("codex-work-receipt-theme"); } catch {}
|
|
1340
1502
|
applyTheme(savedTheme || document.documentElement.dataset.theme);
|
|
1341
1503
|
|
|
1504
|
+
const toast = document.getElementById("toast");
|
|
1505
|
+
let toastTimer = null;
|
|
1506
|
+
function showToast(message) {
|
|
1507
|
+
if (!toast) return;
|
|
1508
|
+
clearTimeout(toastTimer);
|
|
1509
|
+
toast.textContent = message;
|
|
1510
|
+
toast.dataset.show = "true";
|
|
1511
|
+
toastTimer = setTimeout(() => { toast.dataset.show = "false"; }, 2600);
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
const sceneModeButtons = [...document.querySelectorAll("[data-scene-mode]")];
|
|
1515
|
+
const officeShiftName = document.getElementById("officeShiftName");
|
|
1516
|
+
const arrivalShiftLabel = document.getElementById("arrivalShiftLabel");
|
|
1517
|
+
const officeSceneSign = document.getElementById("officeSceneSign");
|
|
1518
|
+
function applySceneMode(mode) {
|
|
1519
|
+
const selectedMode = new Set(["auto", "day", "night"]).has(mode) ? mode : "auto";
|
|
1520
|
+
const scene = selectedMode === "auto" ? officeConfig.generatedScene : selectedMode;
|
|
1521
|
+
const shift = officeConfig.shifts[scene];
|
|
1522
|
+
document.documentElement.dataset.scene = scene;
|
|
1523
|
+
document.documentElement.style.colorScheme = scene === "day" ? "light" : "dark";
|
|
1524
|
+
sceneModeButtons.forEach((button) => button.setAttribute("aria-pressed", String(button.dataset.sceneMode === selectedMode)));
|
|
1525
|
+
if (officeShiftName) officeShiftName.textContent = officeConfig.shiftHeader.replace("{shift}", shift);
|
|
1526
|
+
if (arrivalShiftLabel) arrivalShiftLabel.textContent = officeConfig.shiftOnDuty.replace("{shift}", shift);
|
|
1527
|
+
if (officeSceneSign) officeSceneSign.textContent = officeConfig.shiftSign.replace("{shift}", shift);
|
|
1528
|
+
}
|
|
1529
|
+
sceneModeButtons.forEach((button) => button.addEventListener("click", () => {
|
|
1530
|
+
applySceneMode(button.dataset.sceneMode);
|
|
1531
|
+
const message = button.dataset.sceneMode === "auto"
|
|
1532
|
+
? officeConfig.generatedScene === "day" ? officeConfig.switchedAutoDay : officeConfig.switchedAutoNight
|
|
1533
|
+
: button.dataset.sceneMode === "day" ? officeConfig.switchedDay : officeConfig.switchedNight;
|
|
1534
|
+
showToast(message);
|
|
1535
|
+
}));
|
|
1536
|
+
applySceneMode("auto");
|
|
1537
|
+
|
|
1538
|
+
const windToggle = document.getElementById("windToggle");
|
|
1539
|
+
if (windToggle) {
|
|
1540
|
+
windToggle.addEventListener("click", () => {
|
|
1541
|
+
const paused = document.body.classList.toggle("wind-paused");
|
|
1542
|
+
const label = paused ? officeConfig.windResume : officeConfig.windPause;
|
|
1543
|
+
windToggle.setAttribute("aria-pressed", String(paused));
|
|
1544
|
+
const text = windToggle.querySelector("span:last-child");
|
|
1545
|
+
if (text) text.textContent = label;
|
|
1546
|
+
windToggle.title = label;
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
const ticketRail = document.querySelector(".ticket-rail");
|
|
1342
1551
|
const featureDetails = document.querySelector("[data-feature-details]");
|
|
1552
|
+
const featureScroll = document.querySelector("[data-feature-scroll]");
|
|
1553
|
+
const commandDrawerToggle = document.getElementById("commandDrawerToggle");
|
|
1554
|
+
const printerCommandToggle = document.getElementById("printerCommandToggle");
|
|
1555
|
+
function syncCommandDrawerState() {
|
|
1556
|
+
const open = Boolean(featureDetails?.open);
|
|
1557
|
+
commandDrawerToggle?.setAttribute("aria-expanded", String(open));
|
|
1558
|
+
printerCommandToggle?.setAttribute("aria-expanded", String(open));
|
|
1559
|
+
}
|
|
1560
|
+
function toggleCommandDrawer(force) {
|
|
1561
|
+
if (!featureDetails) return;
|
|
1562
|
+
featureDetails.open = typeof force === "boolean" ? force : !featureDetails.open;
|
|
1563
|
+
syncCommandDrawerState();
|
|
1564
|
+
}
|
|
1343
1565
|
if (featureDetails) {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
if (savedFeatureState === "true") featureDetails.open = true;
|
|
1347
|
-
if (savedFeatureState === "false") featureDetails.open = false;
|
|
1348
|
-
} catch {}
|
|
1349
|
-
featureDetails.addEventListener("toggle", () => {
|
|
1350
|
-
try { localStorage.setItem("codex-work-receipt-features-open", String(featureDetails.open)); } catch {}
|
|
1351
|
-
});
|
|
1566
|
+
featureDetails.open = false;
|
|
1567
|
+
featureDetails.addEventListener("toggle", syncCommandDrawerState);
|
|
1352
1568
|
}
|
|
1569
|
+
commandDrawerToggle?.addEventListener("click", () => toggleCommandDrawer());
|
|
1570
|
+
printerCommandToggle?.addEventListener("click", () => toggleCommandDrawer());
|
|
1571
|
+
syncCommandDrawerState();
|
|
1572
|
+
|
|
1573
|
+
document.addEventListener("pointerdown", (event) => {
|
|
1574
|
+
if (!featureDetails?.open) return;
|
|
1575
|
+
if (featureDetails.contains(event.target) || commandDrawerToggle?.contains(event.target) || printerCommandToggle?.contains(event.target)) return;
|
|
1576
|
+
toggleCommandDrawer(false);
|
|
1577
|
+
});
|
|
1578
|
+
document.addEventListener("keydown", (event) => {
|
|
1579
|
+
if (event.key === "Escape" && featureDetails?.open) {
|
|
1580
|
+
toggleCommandDrawer(false);
|
|
1581
|
+
commandDrawerToggle?.focus();
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1585
|
+
document.querySelectorAll("[data-planned-message]").forEach((button) => {
|
|
1586
|
+
button.addEventListener("click", () => showToast(button.dataset.plannedMessage || ""));
|
|
1587
|
+
});
|
|
1588
|
+
document.getElementById("workReceiptShortcut")?.addEventListener("click", () => {
|
|
1589
|
+
ticketRail?.scrollTo({ top: 0, behavior: "smooth" });
|
|
1590
|
+
showToast(officeConfig.workReceiptDescription);
|
|
1591
|
+
});
|
|
1592
|
+
document.querySelector(".primary-nav__button.is-active")?.addEventListener("click", () => {
|
|
1593
|
+
ticketRail?.scrollTo({ top: 0, behavior: "smooth" });
|
|
1594
|
+
});
|
|
1595
|
+
|
|
1596
|
+
document.addEventListener("wheel", (event) => {
|
|
1597
|
+
if (event.ctrlKey || Math.abs(event.deltaY) < Math.abs(event.deltaX)) return;
|
|
1598
|
+
if (featureDetails?.contains(event.target)) {
|
|
1599
|
+
event.preventDefault();
|
|
1600
|
+
if (featureDetails.open && featureScroll) featureScroll.scrollBy({ top: event.deltaY, behavior: "auto" });
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (!ticketRail || ticketRail.contains(event.target)) return;
|
|
1604
|
+
event.preventDefault();
|
|
1605
|
+
ticketRail.scrollBy({ top: event.deltaY, behavior: "auto" });
|
|
1606
|
+
}, { passive: false });
|
|
1607
|
+
|
|
1608
|
+
document.addEventListener("keydown", (event) => {
|
|
1609
|
+
if (!ticketRail || event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
1610
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
1611
|
+
if (target?.closest("button, a, input, textarea, select, summary, code, [contenteditable='true'], [role='tab'], [data-feature-details]")) return;
|
|
1612
|
+
let distance = null;
|
|
1613
|
+
if (event.key === "ArrowDown") distance = 64;
|
|
1614
|
+
if (event.key === "ArrowUp") distance = -64;
|
|
1615
|
+
if (event.key === "PageDown") distance = ticketRail.clientHeight * .82;
|
|
1616
|
+
if (event.key === "PageUp") distance = -ticketRail.clientHeight * .82;
|
|
1617
|
+
if (event.key === " " || event.code === "Space") distance = ticketRail.clientHeight * (event.shiftKey ? -.82 : .82);
|
|
1618
|
+
if (event.key === "Home") distance = -ticketRail.scrollHeight;
|
|
1619
|
+
if (event.key === "End") distance = ticketRail.scrollHeight;
|
|
1620
|
+
if (distance === null) return;
|
|
1621
|
+
event.preventDefault();
|
|
1622
|
+
ticketRail.scrollBy({ top: distance, behavior: "smooth" });
|
|
1623
|
+
});
|
|
1353
1624
|
|
|
1354
1625
|
const featureTabs = document.querySelector("[data-feature-tabs]");
|
|
1355
1626
|
if (featureTabs) {
|