codex-work-receipt 0.10.1 → 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 +10 -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 +303 -54
- 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 {
|
|
@@ -1230,12 +1273,142 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1230
1273
|
.sidebar { flex-direction: column; }
|
|
1231
1274
|
.sidebar-card { flex-basis: auto; }
|
|
1232
1275
|
}
|
|
1276
|
+
${OFFICE_STYLE_SOURCE}
|
|
1233
1277
|
</style>
|
|
1234
1278
|
</head>
|
|
1235
1279
|
<body>
|
|
1236
|
-
<div class="
|
|
1237
|
-
|
|
1238
|
-
|
|
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">
|
|
1239
1412
|
<nav class="theme-switcher" aria-label="${escapeHtml(copy.themeAria)}">
|
|
1240
1413
|
<button class="theme-button" type="button" data-theme-value="classic">${escapeHtml(copy.themes.classic)}</button>
|
|
1241
1414
|
<button class="theme-button" type="button" data-theme-value="diner">${escapeHtml(copy.themes.diner)}</button>
|
|
@@ -1247,6 +1420,9 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1247
1420
|
</div>
|
|
1248
1421
|
</div>
|
|
1249
1422
|
|
|
1423
|
+
<div class="ticket-sway">
|
|
1424
|
+
<div class="receipt-scope">
|
|
1425
|
+
|
|
1250
1426
|
<div class="export-sheet" id="receipt-export">
|
|
1251
1427
|
<article class="paper receipt" aria-label="${escapeHtml(copy.receiptAria)}">
|
|
1252
1428
|
<header class="brand">
|
|
@@ -1299,55 +1475,19 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1299
1475
|
<p class="transfer-note">${escapeHtml(copy.transferNote)}</p>
|
|
1300
1476
|
</section>
|
|
1301
1477
|
</div>
|
|
1302
|
-
|
|
1303
|
-
<p class="privacy">${escapeHtml(copy.privacy)}</p>
|
|
1304
|
-
</main>
|
|
1305
|
-
<aside class="sidebar" aria-label="${escapeHtml(copy.sidebar.aria)}">
|
|
1306
|
-
<div class="sidebar-card">
|
|
1307
|
-
<h3 class="sidebar-card__title">
|
|
1308
|
-
<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>
|
|
1309
|
-
${escapeHtml(copy.sidebar.supportTitle)}
|
|
1310
|
-
</h3>
|
|
1311
|
-
<p>${escapeHtml(copy.sidebar.supportDescription)}</p>
|
|
1312
|
-
<a class="github-star-link" href="${escapeHtml(githubStarPrompt.url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(githubStarPrompt.label)}</a>
|
|
1313
|
-
</div>
|
|
1314
|
-
<div class="sidebar-card">
|
|
1315
|
-
<h3 class="sidebar-card__title">
|
|
1316
|
-
<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>
|
|
1317
|
-
${escapeHtml(copy.sidebar.changelogTitle)}
|
|
1318
|
-
</h3>
|
|
1319
|
-
<a href="${escapeHtml(changelogUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(copy.sidebar.changelogLink)}</a>
|
|
1320
1478
|
</div>
|
|
1321
|
-
<div class="sidebar-card sidebar-sponsor">
|
|
1322
|
-
<span class="sidebar-sponsor__label">${escapeHtml(copy.sidebar.sponsorLabel)}</span>
|
|
1323
|
-
<a class="sidebar-sponsor__link" href="${MODELFLARE_URL}" target="_blank" rel="noopener noreferrer">
|
|
1324
|
-
<img src="${MODELFLARE_LOGO_DATA_URL}" alt="${escapeHtml(copy.sidebar.sponsorAlt)}" width="40" height="40">
|
|
1325
|
-
<span class="sidebar-sponsor__name">ModelFlare<br>modelflare.dev</span>
|
|
1326
|
-
</a>
|
|
1327
1479
|
</div>
|
|
1328
|
-
<
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
</span>
|
|
1334
|
-
<span class="sidebar-features__meta">
|
|
1335
|
-
<span class="sidebar-features__count">${escapeHtml(featureCountLabel)}</span>
|
|
1336
|
-
<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>
|
|
1337
|
-
</span>
|
|
1338
|
-
</summary>
|
|
1339
|
-
<div class="sidebar-features__body">
|
|
1340
|
-
<p class="sidebar-features__description">${escapeHtml(copy.sidebar.features.description)}</p>${featureCommands}
|
|
1341
|
-
<span class="feature-copy-status" data-feature-copy-status role="status" aria-live="polite"></span>
|
|
1342
|
-
</div>
|
|
1343
|
-
</details>
|
|
1344
|
-
</aside>
|
|
1345
|
-
</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>
|
|
1346
1485
|
<script>${inlineScript(DOM_TO_IMAGE_SOURCE)}</script>
|
|
1347
1486
|
<script>
|
|
1348
1487
|
const exportConfig = ${exportConfig};
|
|
1349
1488
|
const transferConfig = ${transferConfig};
|
|
1350
1489
|
const featureConfig = ${featureConfig};
|
|
1490
|
+
const officeConfig = ${officeConfig};
|
|
1351
1491
|
const themes = new Set(["classic", "diner", "payroll"]);
|
|
1352
1492
|
const buttons = [...document.querySelectorAll("[data-theme-value]")];
|
|
1353
1493
|
function applyTheme(theme) {
|
|
@@ -1361,17 +1501,126 @@ export function renderHtml({ record, dataQrDataUrl = null, miniProgramCodeDataUr
|
|
|
1361
1501
|
try { savedTheme = localStorage.getItem("codex-work-receipt-theme"); } catch {}
|
|
1362
1502
|
applyTheme(savedTheme || document.documentElement.dataset.theme);
|
|
1363
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");
|
|
1364
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
|
+
}
|
|
1365
1565
|
if (featureDetails) {
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
if (savedFeatureState === "true") featureDetails.open = true;
|
|
1369
|
-
if (savedFeatureState === "false") featureDetails.open = false;
|
|
1370
|
-
} catch {}
|
|
1371
|
-
featureDetails.addEventListener("toggle", () => {
|
|
1372
|
-
try { localStorage.setItem("codex-work-receipt-features-open", String(featureDetails.open)); } catch {}
|
|
1373
|
-
});
|
|
1566
|
+
featureDetails.open = false;
|
|
1567
|
+
featureDetails.addEventListener("toggle", syncCommandDrawerState);
|
|
1374
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
|
+
});
|
|
1375
1624
|
|
|
1376
1625
|
const featureTabs = document.querySelector("[data-feature-tabs]");
|
|
1377
1626
|
if (featureTabs) {
|