Repository navigation
Editor button-style normalization, batch photo upload, and real TOC page numbers - #39
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…code gate) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Domain rating-tile component (app, not shared-ui — severity tone map is domain-coupled) that will consolidate RatingButtonRow, BatchActionBar, and SpeedMode's hand-rolled rating tiles in a later phase. Radiogroup a11y pattern mirrors SegmentedControl (role=radio/aria-checked, roving tabindex, Arrow/Home/End). Union of the three call sites' needs: optional shortLabel (RatingButtonRow's abbreviation), hint (keyboard-shortcut digit shown by RatingButtonRow/SpeedMode), and color (BatchActionBar's dynamic tenant-color override, applied regardless of selection). warn tone maps to the real bg-ih-watch token — there is no ih-warn token in tailwind.css. Bumps scripts/knip-baseline.json by 2 entries (RatingOption/RatingTone) — intended public API for Phase-2 adoption, currently unused since no call site has adopted the component yet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…utton, MenuItem, vertical TabStrip) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…d-ui primitives Group A of the editor button-normalization pass. Behavior-preserving: Publish green bg-ih-ok normalized to primary; secondary actions, item-filter SegmentedControl, batch/fullscreen toggles, and toolbar icon buttons moved onto Button/IconButton/SegmentedControl. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…Name wins A migration reviewer proved (via the compiled Tailwind config) that appending a className to Button/IconButton did not reliably override the primitive's own base/size utilities — Tailwind resolves same-property conflicts by stylesheet order, not string order, so responsive-hide (hidden xl:inline-flex) and size overrides silently lost. Introduce cn() (tailwind-merge) and use it in Button, IconButton, MenuItem, SegmentedControl, TabStrip, RatingSegment with className last, so consumer classes win. Also restore EditorHeader Preview/Preview PDF/ Sign now to size=md (h-9), correcting a Group A height regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…red-ui primitives Group B of the editor button-normalization pass. Behavior-preserving: link/danger-link text actions, MenuItem catalog rows, and PSQ-status + report-tier SegmentedControls; retires CompliancePanel's bg-ih-bad-fg Decline in favor of the single danger token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…ed-ui primitives Group C of the editor button-normalization pass. Behavior-preserving: ⋯ menu triggers to IconButton, popover rows to MenuItem (Delete unified as tone=danger across SectionRail/ItemList), scope/template/clone triggers to secondary Button. Nav trees, the SideRail vertical icon strip, and the photo-download <a> are intentionally left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…t, Button type default - BreadcrumbDropdown scope options revert from MenuItem to plain button (they are listbox options, not menu items — MenuItem's hardcoded role=menuitem conflicted with the parent li role=option). - TemplateMenu trigger size sm->md (h-9) to align with sibling header controls. - Button now defaults type=button (consistent with IconButton/MenuItem); explicit type still wins. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…itives Group D (normal surfaces) of the editor button-normalization pass. ItemPhotoStrip select-mode controls to ghost/link/danger-link Button; RatingButtonRow and BatchActionBar rating tiles consolidated onto RatingSegment (values + onChange preserved; BatchActionBar keeps dynamic per-option colors via RatingSegment color). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…abels Group D review: TONE_FILLED.neutral paired bg-ih-bg-muted with text-ih-fg-inverse (~1:1 contrast, invisible) — the common minor/Not-Inspected tier maps here. Use bg-ih-bg-inverse (solid, high-contrast in both themes). Also restore the pre- migration responsive abbreviation: at md/lg, shortLabel shows below sm breakpoint and the full label at sm+; sm stays compact-short. Accessible name stays the full label. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…trols to primitives Per product direction, SpeedMode and BurstCamera must be consistent with the rest of the (theme-aware) editor. SpeedMode: convert its fixed-dark slate palette to theme-aware ih-* tokens (works in light + dark), Prev/Next/Exit onto Button, large number-hinted rating tiles kept bespoke but theme-colored. BurstCamera: controls onto IconButton/Button while intentionally keeping the over-video translucent-dark treatment (the frame is live camera video, not a themed surface — light-on-dark is the correct, theme-independent camera-UI pattern); shutter stays special. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
… light theme) bg-ih-bg-app/80 is near-white at 80% in light theme -> the first-run coach scrim vanished. Use the dedicated ih-backdrop token (the app's standard overlay scrim, alpha baked in) like every other full-screen overlay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…conButton Group E of the editor button-normalization pass. Cancel/Stay/Close -> ghost, confirm -> primary (PublishModal green bg-ih-ok normalized to primary), Delete/Leave/ Crop&clear -> danger, hand-rolled modal close glyphs -> IconButton; KeyboardHud raw rgba backdrop -> ih-backdrop token. SectionPickerModal (listbox pattern) and TagPickerModal (dynamic-color chips) intentionally left bespoke. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…red-ui primitives Group G of the editor button-normalization pass. UnitsManager/CommentLibraryDrawer filters -> SegmentedControl; unit + progress-dot + clear icons -> IconButton; InspectionSettingsSheet Save keeps type=submit/form; ItemEditor + CannedCommentTabs + TagChipRow text/dashed actions -> Button; InspectorToolsDock tool tiles -> MenuItem (gradient FAB left as a documented DS exception). Dynamic-color toggle chips and content cards intentionally kept bespoke. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…tent layout Group G review: the secondary variant leaked a solid bg-ih-bg-card fill onto the transparent dashed +Add empty-state CTAs (SectionRail/ItemList/CannedCommentTabs/ ItemEditor/TagChipRow) -> switch to ghost + dashed border + preserved per-control hover. MenuItem wrapped children in a span, breaking rich flex-1 label + trailing badge layouts (InspectorToolsDock hotkeys) -> render children directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Group F (final migration group). Save/Rating System actions -> Button, rail tabs -> TabStrip, RatingSystemEditor move/remove/preset controls -> IconButton/Button; raw hex page-bg leaks (bg-[#f8fafc] dark:bg-[#0f172a]) -> theme-aware bg-ih-bg-app; navigation Links kept as links (tokens fixed). Preview watch-tone toggle kept bespoke. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…umed) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
… results
Replace the sequential for-await photo-upload loop in the BFF action with mapPool
(a generic order-preserving bounded-concurrency pool, concurrency 4). The action
now returns a per-file results[] ({index, ok, key?, error?}) alongside the existing
keys[] contract, so the client can report N-of-M and retry only failed files. The
concurrency cap bounds R2-write/isolate-memory concurrency; total subrequests stay
client-chunked. Also tightens file-size baselines to the branch's post-migration
line counts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Add a dedicated 'Add from library' input (multiple, no capture) alongside the 'Take photo' camera input; handlePhotoUpload now bakes and submits every selected file as one batch (repeated file keys, mirroring burst), capped at 20/submission, with the offline branch enqueuing each file. The result effect uses the action's per-file results[] to report 'N of M uploaded' on partial failure. Adds an E2E spec (runs against the seeded D1 harness in CI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
Chrome encodes intra-doc <a href=#id> TOC links as named destinations whose name equals the element id; pdf-lib resolves each via the /Names /Dests tree to the correct 1-based page. Validates the two-pass approach (render -> extractAnchorPages -> inject -> re-render). Verified with Playwright Chromium (same engine as CF Browser Rendering). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…ces Paged.js) Chrome encodes intra-doc <a href=#id> TOC links as named destinations whose name is the element id. New server/lib/toc-pages.ts:extractAnchorPages reads the PDF /Names /Dests tree to map anchor id -> 1-based page. server/lib/pdf.ts gains generatePdfWithTocPages: render pass 1, extract the page map, re-render pass 2 with the map injected via a &tocpages= param; it self-short-circuits to a single pass when there are no anchors, so report-pdf.service calls it unconditionally with no per-caller plumbing and non-commercial reports are unchanged. ReportToc fills each reserved slot from the tocPages map (stable pagination); the report loader parses the param. Removes the falsified/gated Paged.js path (component, synthetic e2e, pagedjs dep, vendor entry, target-counter CSS). RENDER_VERSION r6 -> r7. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
…worker bundle diet) The full twilio + telnyx npm SDKs added ~567 KiB gzip and, together with the new pdf-lib TOC dependency, pushed the worker past the Workers Free 3 MiB limit. The compliance providers already program against narrow local client interfaces (TwilioComplianceClient / TelnyxComplianceClient) with the SDK instances duck-typed in, so this swaps the construction seam only: new twilio-rest-client.ts / telnyx-rest-client.ts implement those interfaces over fetch (Twilio Basic-auth form-encoded; Telnyx Bearer JSON), wired in resolve-compliance-provider.ts. Provider logic + their mocked tests are unchanged. Removes twilio/telnyx deps + the dead fetch-http-client shim. Worker bundle 101.7% -> 83.2% of the Free limit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
twilio-compliance header now describes the fetch REST client (not the dropped SDK); remove the dangling twilio-sdk-fetch.spec.ts reference; correct ReportToc's pagination-stability rationale (no added TOC lines, not 'same width'). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
RepairItemsPanel's search-filtered repair-item picker is a listbox, not a menu — revert its rows from MenuItem (role=menuitem) to plain buttons inside role=option li's under a role=listbox ul. CloneLastButton's dropdown IS a menu (clone-scope actions) but its container lacked role=menu while the trigger declared aria-haspopup=menu — add role=menu so its MenuItems aren't orphaned. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
… CI) Fork CI caught two E2E failures the local env (JWT-keypair gap) couldn't run: - inspection-edit-hotkeys: RatingButtonRow is now a RatingSegment radiogroup, so the 'press 4' assertion targets the checked radio (role=radio + aria-checked), not a button with aria-pressed. - report-toc-numbers: loginApi omitted the double-submit CSRF token /api/auth/login requires; send it as header + __Host-csrf_token cookie (mirrors editor-seed). The existing 503 skip still covers CI's missing BROWSER binding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
The wizard create returns 200 (not 201) with { data: { id } } (not data.inspection.id);
assert 2xx via ok() and read data.id. Broaden the render-unavailable skip from
exactly-503 to any non-ok status, so CI (no live BROWSER binding, possible pipeline
gate) skips cleanly — the real two-pass proof runs only on deployed CF, never a
false pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iM1JG433tMu7MGJjEC49X
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent editor/report follow-ups plus an enabling worker-bundle diet.
Editor button normalization
link/danger-linkvariants +selectedtoggle state,IconButton,MenuItem, verticalTabStrip, and acn()tailwind-merge helper so consumer classNames reliably win conflicts; plus an app-levelRatingSegmentconsolidating the rating-tile copies.Batch photo upload
mapPool) returning per-file results; a dedicated multi-select "Add from library" input alongside "Take photo"; results-based "N of M uploaded" messaging.TOC page numbers (commercial PCA)
extractAnchorPages) → re-render with the page map injected into the TOC. Self-short-circuits to a single pass when a report has no anchors. Replaces the previous Paged.js path. RENDER_VERSION → r7.Worker bundle diet
Full gate green on Node 22 (type-check, lint, bundle, db:check, unit, workers, web). Two new E2Es (batch-upload, TOC-numbers) run in CI.
🤖 Generated with Claude Code