Sign in
Join now
chalky.town
tyler.fun
/
Star
Starred
Star
Something went wrong. Try again.
Fork
0
Eye filled
Watch
Watching
Watch
a local-first atmospheric chalky task planner
Star
Starred
Star
Something went wrong. Try again.
Fork
Eye filled
Watch
Watching
Watch
Overview
Overview
Issues
Issues
Pulls
Pulls
Pipelines
Pipelines
Commits on
main
tyler.fun
5c9da040
Copied
Failed
Copy SHA
ci: add sed to the image
Copy SHA
Browse repository
committed
9h ago
tyler.fun
3acf3b68
Copied
Failed
Copy SHA
ci: get wrangler from npm, not nixpkgs
Copy SHA
Browse repository
committed
9h ago
tyler.fun
86084dbe
Copied
Failed
Copy SHA
app: count page views with GoatCounter at count-chalky.tyler.fun, paths only (never the query string, where OAuth callbacks put codes) and client-side navigations too
Copy SHA
Browse repository
committed
11h ago
tyler.fun
f6bd4da7
Copied
Failed
Copy SHA
app: a service worker that takes over with the same build no longer reloads the page — since the move to Cloudflare Pages, Mac Safari reloaded chalky.town on every return to the tab while sw.js stayed byte-identical (WebKit's update check also compares things like the TLS certificate, so it re-installed the same worker and fired controllerchange). main.ts now asks the new worker for its shell (cache-first, so the shell it precached) and reloads only when that names a different entry bundle than the running page; if it can't tell, it reloads as before
Copy SHA
Browse repository
committed
20h ago
tyler.fun
45bfcac2
Copied
Failed
Copy SHA
Host on Cloudflare Pages instead of Coolify
Copy SHA
Browse repository
committed
23h ago
tyler.fun
fb0799b6
Copied
Failed
Copy SHA
app: checking a task off checks off its subtasks too — toggleDone folds the unchecked subtasks (stamped with completedAt) into the same patch as done, so one undo reverts both; un-checking leaves subtasks as they are. Recurring occurrences are excluded: their subtasks live on the series template, so checking them would check them on every day of the series. Not gated on the auto-done setting. Verified in the preview by clicking the checkbox on a 1/2 task (became 2/2, both stamped; un-check left both checked); not exercised signed-in; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1d ago
tyler.fun
eec0c735
Copied
Failed
Copy SHA
app: a repeating task's details card dates its header by the day it was opened from, not the series start — whenLabel read task.day, which for a recurring task is the anchor, so Movie night (weekly on Fri since Aug 7) opened from Oct 23 said 'Fri, Aug 7'. It now uses ui.detailsDay for recurring tasks (the same open day repeatAnchor and snooze already use) and falls back to task.day; non-recurring tasks keep task.day so a task moved with the card open never shows a stale day. Found while taking atstore.fyi screenshots. Not verified in the browser; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
4d ago
tyler.fun
d0f299ec
Copied
Failed
Copy SHA
deps: atproto Spaces alpha SDK moves to the 2026-10-22 build (0.0.0-spaces-alpha-20261022141228, published 13 min after atproto ac0c9f1, the next pds-spaces-alpha image), and space reads accept either PDS build's commit shape so this client deploys ahead of the PDS flip — ac0c9f1 replaced `#signedCommit` {ver,hash,ikm,sig,mac,rev} with `#commit` {ver,repo,space,rev,data}, and the SDK validates every response, so the 10-15 SDK rejects the new PDS's getLatestCommit and any listRepoOps page that reaches the head (every sync cycle), and the 10-22 SDK rejects the old PDS's the same way. repo.ts's commitShapeMismatch takes a body rejected at Output/commit as-is: head() reads commit.rev (both shapes carry it), listOps() drops the unused commit and re-validates the rest, so a page wrong anywhere else still throws. Drop it once bag.laugh.town runs ac0c9f1. The rest of the range doesn't touch chalky: commit MACs only apply to space-credential / Atproto-Space-Want-Mac reads (OAuth reads skip them), getRepo (CAR→STAR, excludeValues gone) and getSpaceCredential (auth in body) aren't called, and the only DID-doc entry read is #atproto_pds, not #atproto_space. oauth-client-browser's dist is byte-identical, so the WebKit null-IDB patch carries over (renamed; same patch_hash). Verified by driving SpaceRepo through Vite SSR against mocked responses: main fails head/listOps on the new shape, the bump alone fails on the old one, bump + fallback passes on both and still rejects a page with invalid ops; not exercised signed-in against a real PDS; pnpm check 0 errors 0 warnings, pnpm build ok, Dockerfile build ok
spaces-alpha-20261022
Copy SHA
Browse repository
committed
6d ago
tyler.fun
7052eefe
Copied
Failed
Copy SHA
app: the carry-over sweep waits for a pull made today, so a tab that went days without syncing no longer moves finished tasks onto today — Mac Safari (device jjwpyk) last synced 10-18 07:19Z, sat hidden for two days (hidden tabs skip cycles), and on 10-20 the rollover check swept its stale copy at 20:30Z with no PDS traffic: the engine's once-per-session `reconciled` was still true from the first cycle. When it reloaded at 02:18Z its 16 records went out via conflict-path putRecords; their fresh place stamps beat the remote's in the field merge, so 10 tasks completed elsewhere landed on 10-20 still checked and 2 deleted ones were recreated (reconstructed from the PDS request log and space_record/space_record_oplog, no device trail). settings now keys reconciliation by day (`reconciledFor`, the day a successful cycle started), the engine reports every successful cycle rather than the first, and a never-synced device keeps sweeping locally (`markLocalOnly`). The failure-limit fallback no longer releases the sweep on a device holding synced records (`reconcile.heldForPull`); a held sweep logs `sweep.carryOver.deferred` once per day. sync-triage skill documents the signature and the new events. Verified in the preview: a 10-18 task with a cid stayed put after a cycle dated yesterday and with localOnly set, and moved to today after a cycle dated today; not exercised signed-in or across a real hidden-tab rollover; the 16 records on the PDS are not repaired; pnpm check 0 errors 0 warnings, pnpm build ok
Copy SHA
Browse repository
committed
1w ago
tyler.fun
e33d2c56
Copied
Failed
Copy SHA
app: a deploy taking over the page no longer signs you out — the service worker's controllerchange reload could land while the OAuth client was mid-refresh; the PDS had already rotated the refresh token, the aborted request never stored the new one, and the reloaded page presented the old token, which the PDS (no grace window) answers by revoking the session as 'Refresh token replayed'. The PDS logs showed exactly that shape twice on 10-18 (iPhone Safari 10:08, Orion 11:39 PDT: /oauth/token 400 nonce → request aborted → replayed, within a second), the Orion one minutes after b1878d2 deployed; the cached-shell launch makes it likely on boot too (restore() refreshes an expired token while the new worker takes over). main.ts now reloads via reloadOutsideTokenRefresh: it takes the client's '@atproto-oauth-client-<did>' Web Lock for the stored sub plus any held/pending lock with that prefix, and reloads from inside it, so an in-flight refresh finishes and stores first and none can start in the gap; locks drop with the page, and past 35s (the client caps a refresh at 30s) it reloads anyway. Verified in the preview against the function's own source with reload stubbed: a 1.5s held refresh lock delayed the reload ~1.45s and it fired after the refresh ended; idle, it reloaded in 1ms and still held the lock (an ifAvailable request got none). Not exercised with a real deploy signed-in; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1w ago
tyler.fun
b1878d20
Copied
Failed
Copy SHA
deps: atproto Spaces alpha SDK moves to the 2026-10-15 build (0.0.0-spaces-alpha-20261015143509), matching the PDS — bag.laugh.town now runs the 2026-10-15 pds-spaces-alpha image (rev e03c77f, still reports 0.5.32; migrations account 008-spaces + actor 004-space-sync; backup /pds/backup-pre-spaces-20261015-20261018074201, old image tagged local/pds-spaces-alpha:20260915). The one wire change the app hits: createSpace's `type` is now `spaceType` (atproto#5561); listRepos/notifyWrite gained repoRev/spaceRev but are unused here, and every read/write/getLatestCommit/listRepoOps shape is unchanged. oauth-client-browser's dist is byte-identical, so the WebKit null-IDB patch carries over as-is (renamed). syntax/lex-data/lex-json overrides stay exact-pinned. Live tab on the old SDK got 200s on space calls after the PDS restart; new SDK not exercised signed-in; pnpm check 0 errors 0 warnings, pnpm build ok
Copy SHA
Browse repository
committed
1w ago
tyler.fun
e226f3ef
Copied
Failed
Copy SHA
app: drag-to-reorder no longer gets worse the longer a drag goes on, plus a dependency sweep — svelte-dnd-action 0.9.74's getBoundingRectNoTransforms read rect.x/rect.y off getVisibleRectRecursive's result, which has neither, so any row measured mid-flip came back NaN; the shadow-rect cache (cleared only on scroll or drop) then held a NaN for that index and its hysteresis check pinned the placeholder whenever the pointer reached that row, one more row per flip the observer happened to catch. Upgraded to 0.9.80 (upstream reads rect.left/top) and re-ported both chalky patch hunks (no synthetic click on touchend with delayTouchStart; no preventDefault on touch handle starts). Also: svelte 5.57.2, vite 8.3.4, vite-plugin-svelte 7.3.1, svelte-check 4.7.7, bits-ui 2.19.5, @lucide/svelte 1.59.0, milkdown crepe+kit 7.22.3, marked 18.1.1, idb 8.0.4, typescript 6.0.3, @types/node 24.19.2, hueful 0.8.1, satori 0.62.0. Satori now lays out with Taffy (layout.wasm replaces yoga.wasm) and defaults to content-box, which pushed the share cards' padding outside every fixed width (values off the right edge, footer cut, tiles two per row) — the card helper sets boxSizing border-box; THIRD_PARTY_LICENSES swaps yoga-layout for taffy. Held back: TypeScript 7 (svelte-check refuses it), @types/node 26 (runtime is Node 22), the atproto spaces-alpha SDK (must match the PDS build; separate task). Verified in the visible preview: a scripted 25s drag left 6 of 7 targets on the wrong row before and 0 after; both share cards render 1080x1350 and look right; hueful's normalizeHex/inkFor outputs unchanged; notes editor renders; pnpm check 0 errors 0 warnings; pnpm build ok (main chunk +17KB vs live); touch not exercised on a device
Copy SHA
Browse repository
committed
1w ago
tyler.fun
98dbd879
Copied
Failed
Copy SHA
app: changing a recurring task's rule from one of its days applies from that day forward, not back to the series start — editing the repeat on a later occurrence used to rewrite the whole template, so a new rule projected unfinished copies onto every past day since the anchor. The store's new setRecurrenceFrom splits the series at the open occurrence: the original keeps its rule capped at the day before (count folded into until), and a copy (title, color, notes, subtasks, position) anchored on that day takes the new rule plus the done/skipped overrides on and after it (re-homed as new occurrence records, old ones tombstoned). An unchanged 'after N times' count carries only the occurrences not yet used. Editing from the anchor, or with no day open, still edits in place. The details card follows the new series (lastId moved with it, so the picker stays open and later clicks don't split again) and the picker anchors on the open day. One undo step. Verified in the preview: daily→weekly split at 10/15 keeps daily through 10/14 and Mondays after, done marks stay on their days, undo restores the single series, count 10 with 4 used → 6, two picker clicks from 10/17 make one split; not exercised signed-in; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1w ago
tyler.fun
0e245f85
Copied
Failed
Copy SHA
deploy: a slow network no longer holds a PWA launch on a blank screen — navigations were network-first with no timeout, so a cold launch on bad cellular waited as long as chalky.town took to answer. The service worker now serves the cached shell if the network hasn't answered within NAV_TIMEOUT_MS (1s), and lets the request finish in the background (waitUntil) so SHELL is still refreshed; with no cached shell it keeps waiting, and a failed fetch falls back as before. A newer deploy still lands via the worker update the navigation triggers plus main.ts's controllerchange reload. Verified against dist on a delayable static server: 5s HTML delay → page at 1.0s from cache, shell re-cached 5s later; no delay → network response in 13ms; server stopped → cached boot; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1w ago
tyler.fun
fad16411
Copied
Failed
Copy SHA
app: a launch whose first sync pulls in changes from another device covers the board with a pulsing chalky mark until they land — the engine's onCatchUp fires only for the first pull of the page's first engine, and only when it will actually change the board: an op-log page with a task/occurrence op this device doesn't hold (own pushes echoed back, the config record, and ops a dirty local edit outranks don't count), or a snapshot (private: always; public: only a device's first sync, since the repo rev also moves for Bluesky writes). 300ms grace, 500ms minimum, 12s cap, 320ms fade; board is inert meanwhile. In practice an ordinary delta applies inside the grace, so it shows for new devices, expired cursors and >500-op catch-ups. sw precaches hashed SVGs so the mark is there offline. Verified: overlay rendered in light/dark in the preview; not exercised signed-in; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1w ago
tyler.fun
6831fce9
Copied
Failed
Copy SHA
app: sync pauses while the tab is hidden, so a frozen tab can no longer sign you out — the repeated "session expired" toasts (Orion constantly, Safari once overnight) were all the PDS revoking the grant for "Refresh token replayed": a background cycle started a token refresh, the browser froze the tab mid-request, the PDS rotated the token but the page only saw "Load failed" minutes later, and the next refresh presented the old token. The PDS (oauth-provider consumeRefreshToken) has no grace window, so cycle() now does no network work while document.hidden and doesn't rearm; wake() on visibilitychange runs one immediately when the tab is visible again, and local edits stay dirty meanwhile. Trail: one cycle.pausedHidden per hidden stretch, and cycle.error carries vis. Diagnosed from the bag.laugh.town PDS logs (a 200 refresh followed by no xrpc calls, then the replay) lined up with the Orion sync trail (cycle.error Load failed right before each auth.sessionDead). Not exercised signed-in; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1w ago
tyler.fun
1a3466b4
Copied
Failed
Copy SHA
app: tapping a link in notes opens it unless you're already editing — if the editor isn't focused (desktop: not clicked into; touch: keyboard down), the press is cancelled before ProseMirror sees it (mousedown on desktop, touchend on touch, so no focus and no keyboard) and the link opens in a new tab; a focused editor keeps the old caret-placing click. Only http/https/mailto/tel open; a pointercancel (scroll) drops the pending link. Verified: desktop preview both ways; iOS 18.6 simulator Safari both ways, and an unfocused tap opened example.com in a real new tab (not popup-blocked); pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1w ago
tyler.fun
8615e4bf
Copied
Failed
Copy SHA
deploy: the service worker installs again, so the PWA cold-loads offline — the precache list was read in generateBundle before Vite's CSS plugin deleted the empty JS stubs of pure-CSS chunks (the notes editor's lazy Milkdown stylesheets, since bc57114), so sw.js listed two style-*.js files that 404; cache.addAll is all-or-nothing, install failed, and an offline launch got the browser's no-internet page. The plugin now runs with order 'post' and sees the final bundle. Verified: 152 precache entries, none missing from dist; served dist from an nginx-like static server, SW installed with all 152 cached, killed the server, reload booted the app fully offline; prod assets send only Vary: Accept-Encoding; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1w ago
tyler.fun
bf672c80
Copied
Failed
Copy SHA
app: the month grid no longer shows a fall-back DST day twice — addDays stepped in 24h of ms, so on a 25h day (Oct 25 in Europe, Nov 1 in the US) midnight + 24h landed on 23:00 the same day: the grid printed it twice and every later cell was a day off, next-week navigation from that week landed back in it, and an "after N times" daily/weekly recurrence could compute its until bound a day early. addDays now steps by calendar date like shiftKey. Verified: October grid and +7d correct under Europe/Warsaw, America/New_York, Australia/Sydney and UTC; November grid in the LA preview shows Nov 1 once; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
1w ago
tyler.fun
f66c5e73
Copied
Failed
Copy SHA
app: a sign-in that comes back from the PDS but can't be completed now says so instead of looping to the session-expired toast — initSession checked isSessionDead before isCallback, so a refused code exchange (invalid_grant) was marked as an expiry, and any other callback error fell through to App's signed-out fallback, which raised the same toast; either way the user landed back on "Your session expired — Sign in" with no reason (reported on Chrome/Android, where it loops while Firefox signs in fine). Callback failures now log auth.callbackFailed and throw SignInFailedError, App hands its message to authNotice.signInError, and the toast reads "Sign-in didn't finish — try again." with the library's error underneath. Still silent: a page killed between the library stripping the callback params and finishing the exchange. Verified in the preview with a bogus ?state=&code= callback: the toast shows "Unknown authorization session"; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
2w ago
tyler.fun
86044a9a
Copied
Failed
Copy SHA
app: the Edit colors toolbar button uses a paintbrush instead of a pencil — Lucide's Paintbrush matches what color mode actually does (brushing a color onto tasks). Verified in the preview
Copy SHA
Browse repository
committed
2w ago
tyler.fun
4bf2b174
Copied
Failed
Copy SHA
repo: ignore .env and .env.local — local secrets like CHALKY_APP_PASSWORD for scripts/publish-lexicons.mjs live there, and .env wasn't ignored, so a broad git add could have committed the app password. .env.dev and .env.production (public OAuth client ids) stay tracked
Copy SHA
Browse repository
committed
2w ago
tyler.fun
0a5214b7
Copied
Failed
Copy SHA
app: a Stats page in the account menu, shareable as two images, and a setting to hide it — stats.ts computes usage from the local store (tasks created/done, current and best daily streak, active days, busiest day, median created→done and same-day share, peak hour, done per weekday, created/done per month, color counts, still open / in idk when, moved, notes, subtasks, recurring series and occurrence hits; tombstones skipped, local time). StatsModal shows it as tiles plus bar rows (palette-colored for colors) and is wired like Credits: ui.statsOpen, ?panel=stats, Escape. Share renders two 1080×1350 cards (headline tiles + odds and ends; months, colors, weekdays), both titled "chalky.town stats", with Satori in the chalkboard palette, pre-rendered when the modal opens so the tap hands them to navigator.share inside Safari's user activation; where files can't be shared (or the share is refused) an in-app menu previews both with Copy and Save per card and a Post to Bluesky draft — never a silent download (the first version downloaded two files and browsers dropped the second, which is why the shared image looked empty). Satori is pinned to 0.33.5 (the four releases from the last day skipped) via its standalone build, initialized with yoga.wasm by URL; harfbuzzjs fetches /hb.wasm from the page directory, so a small vite plugin serves it in dev and emits it in the build; Gluten comes from @fontsource/gluten 5.3.0's woff files since Satori can't read woff2. All of it lazy-loads on the first Stats open. Settings gains "Hide the Stats page" (settings.hideStats, synced through town.chalky.config's new hideStats field, lexicon updated but not yet republished), which drops the menu item and ignores ?panel=stats. THIRD_PARTY_LICENSES lists satori (MPL-2.0), yoga-layout and harfbuzzjs. Verified: page numbers match an independent count of the PDS space records; both cards checked visually; iOS 26 simulator Safari opens the native share sheet with the PNG; the preview pane falls back to the menu; hide toggles both ways; pnpm check 0 errors 0 warnings; pnpm build ok
Copy SHA
Browse repository
committed
2w ago
tyler.fun
cc41dbb9
Copied
Failed
Copy SHA
app: the phone sheet no longer shows a gap above the keyboard — iOS 26's keyboard accessory bar floats with see-through margins, so the board showed through the strip between the lifted sheet and the keys. A surface-coloured .kb-fill now runs from the sheet's bottom down behind the keyboard while it's up (narrow only, above the card so its drop shadow doesn't darken it, pointer-events none). Not verified on device — the sim's faked keyboard has no accessory bar; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
2w ago
tyler.fun
54c7a580
Copied
Failed
Copy SHA
app: drop the beta tag from the header — the title stands alone in its left cluster; the .beta pill and its styles are gone and the cluster/title comments now describe the ellipsis and right-pinned actions instead. Verified in the preview: header shows just the date title; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
2w ago
tyler.fun
f0e20bba
Copied
Failed
Copy SHA
app: the phone new-task sheet works again after the keyboard closes, and the notes field loads without the jumble — 91fb7a3's visualViewport sync read kbInset right after writing it inside its own effect, so with the keyboard up the effect depended on its output and looped (cleanup zeroed it, sync set it back) until Svelte threw effect_update_depth_exceeded, leaving the sheet frozen at its keyboard-up bottom/max-height and the UI wedged so the swatches and X stopped responding; sync now only writes. The sheet also scrolled sideways: [data-tip] bubbles are invisible on touch but still laid out, and the new + sat at the edge, so its tooltip widened the card ~23px — tooltips now aren't rendered under (hover: none), and the + tip is right-aligned for desktop; separately WebKit counts the card's end padding past the header/title negative-margin bleed as overflow (~18px), so the card is overflow-x: hidden. The notes loading state was a centred grid holding "Loading editor…" plus Crepe's half-mounted surface (placeholder one word per line); it's now a stand-in at the editor's finished size — an empty toolbar row and the note text or placeholder — with Crepe mounting into a hidden surface. Verified in the iPhone 16 Pro simulator over the Web Inspector protocol: faked keyboard open/close via visualViewport lifts the sheet to bottom 318px then back to the edge with no errors, card scrollWidth no longer exceeds its width, swatch tap selects salmon, X closes; loading stand-in screenshotted under a temporary 6s delay (removed); pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
2w ago
tyler.fun
4a5e24e2
Copied
Failed
Copy SHA
app: the details card's add-subtask row is full width again — 91fb7a3's save-and-add-another button took the class .add, and its 32×32 icon-button rule also matched the checklist's .sub.add row, squeezing it to 32px so the textarea was 10px wide and the grow action stretched it 226px tall, one placeholder letter per line spilling over Color. The header button is now .add-next. Verified in the preview at 375px: row 317px, textarea 286px wide and 32px tall, header + still 32×32; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
2w ago
tyler.fun
91fb7a31
Copied
Failed
Copy SHA
app: new-task card gets a save-and-add-another "+" and the phone sheet rides above the keyboard — tasks made from the "idk when" "+" or n now show a "+" by the close X (ui.newTaskFlow, set by addSomedayTask and cleared on any other open/close) that commits like close() and opens a fresh new task; on a still-empty card it just refocuses the title instead of discarding and recreating it. The move-to-today T is hidden on new-task cards. On narrow screens the sheet now tracks visualViewport: bottom lifts to the keyboard's top (net of Safari's pan) and max-height caps to the visible height, then the focused field is scrolled into view inside the card. iOS never resizes the layout for the keyboard, so the bottom-anchored sheet sat behind it and Safari panned just enough to bare the title, leaving it pinned at the keyboard edge with the sheet half-hidden. Verified: in the preview, + saves 'first one' and opens a blank focused card, empty-card + only refocuses, closing an empty card leaves no ghost, existing tasks show no +; in the iPhone 16 Pro simulator, before the title sat mid-screen with the sheet under the keyboard, after the sheet fills the space above it with the title at top, notes focus still fine, T gone on new cards; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
2w ago
tyler.fun
7f63a41b
Copied
Failed
Copy SHA
app: switching views scrolls the page back to the top — Week → Day can shrink main by thousands of px; iOS WebKit drops the scroller to 0 on its own, but macOS Safari was seen keeping the stale scrollTop past the new end (header and board above the fold, blank below "idk when") until the tab was hidden and shown again. An effect on ui.view now sets main.scrollTop = 0 outright. Verified in the iPad simulator: Week scrolled to 1500px → 3 Day (still 2669px tall) lands at 0; pnpm check 0 errors 0 warnings
Copy SHA
Browse repository
committed
2w ago
tyler.fun
ac2a83ae
Copied
Failed
Copy SHA
app: tapping a task on touch opens its details again — svelte-dnd-action dispatches a synthetic click on touchend when a press turns out not to be a drag, assuming touchstart was preventDefault'ed; with delayTouchStart it isn't, so Safari's native click followed, landed on the details scrim that had just mounted, and closed the panel the instant it opened (and double-toggled checkboxes). The patch now skips the synthetic click when delayTouchStart is on. Verified in the iPad Air simulator over the Web Inspector protocol: before, chip click → scrim click; after, one native click, panel stays open, checkbox toggles once
Copy SHA
Browse repository
committed
2w ago
tyler.fun
ccc116ca
Copied
Failed
Copy SHA
app: hueful replaces the hand-rolled colour math - palette.ts drops hexToRgb/relativeLuminance/contrastRatio for hueful's WCAG 2 contrast (inkFor keeps its rule: LIGHT_INK only when strictly higher than DARK_INK, anything unparseable gets DARK_INK), and a new normalizeHex turns any CSS colour hueful parses (3/4/8-digit hex, rgb(), hsl(), oklch(), names like rebeccapurple, bare hex without the #) into the lowercase 6-digit #rrggbb that stored records and --hl-<id> vars expect, rounding and clamping channels (hueful doesn't clamp, so out-of-gamut oklch lands on the gamut edge) and dropping alpha. The picker's color text field now routes through it, so typing rebeccapurple or oklch(0.7 0.15 30) works; the aria-label is Color value. hueful pinned at 0.7.0 (MIT, zero deps) and listed in THIRD_PARTY_LICENSES.md. Presets, defaults, ids and the lexicon are untouched. Verified: no test runner in the repo, so a throwaway node:test file checked inkFor (#ffa3a3 dark ink, #1a1a40 light ink, junk dark ink, every default and preset dark ink) and normalizeHex (#fa0 -> #ffaa00, rebeccapurple -> #663399, oklch(0.7 0.15 30) -> #ed7665, rgb(10 20 30 / 0.5) -> #0a141e, oklch(0.7 0.4 30) -> #ff0000, garbage and empty -> null); pnpm check 0 errors 0 warnings; pnpm build ok
Copy SHA
Browse repository
committed
3w ago
tyler.fun
18f9f1e4
Copied
Failed
Copy SHA
app: subtask rows show the pointer, not the open hand — svelte-dnd-action writes an inline cursor: grab on each row, which showed in the ~2.5px slivers above/below the 24px checkbox and the 7px gap before the label, so hovering onto the checkbox flickered through a grab hand. .sub:not(.add) now takes --cur-pointer (!important over the inline style) and .sub:active keeps --cur-grabbing; html.dragging's id-scoped rule still wins mid-drag. Verified in the preview with elementFromPoint: the row's top/bottom edges and the check→label gap all resolve to the pointer, the Add subtask row stays default
Copy SHA
Browse repository
committed
3w ago
tyler.fun
dfa18ee0
Copied
Failed
Copy SHA
app: right-click a subtask for Copy / Delete — the details panel's checklist rows open the shared ContextMenu (above the panel, z 45 over 41) via a new ui.subContext {taskId, subId, day}. Copy writes the subtask's plain text (autolink brackets unwrapped, no checkbox prefix); Delete goes through store.setSubtaskText(…, "", day) like the row's trash button, so one undo restores it. The edit textarea keeps the native menu and Ctrl+Shift stays the fast-delete. The menu's Escape now listens in capture and stops there, so it closes only the menu, not the details panel under it. Verified in the preview with synthesized contextmenu events: Copy → "second https://example.com", Delete drops the row and ⌘Z brings it back, body Escape leaves the panel open
Copy SHA
Browse repository
committed
3w ago
tyler.fun
fcbf09c8
Copied
Failed
Copy SHA
app: snooze a recurring occurrence — "Snooze 1 day" (the occurrence's day + 1) or "Snooze to other day" (reveals a date field, min = day + 1) in the right-click menu and the details panel's new "Snooze this day" row, offered only on unfinished occurrences. store.snoozeOccurrence skips that day from the series and drops a detached one-off copy (title, color, notes, subtasks with fresh ids) onto the target day, appended, as one undo step — no lexicon change, the occurrence record stays a pure done/skipped decision. New date.ts shiftKey steps by calendar date so DST can't double a day. The revealed date field focuses and calls showPicker() where allowed; the swap is height-pinned (menu row 29px like an item, panel row 38px) so neither the menu nor the card shifts. Verified in the preview: Mon→Fri via custom date and Tue→Wed via 1 day land the copy and empty the source day, one undo restores both, menu stays 188px and the card 561.5px across the reveal
Copy SHA
Browse repository
committed
3w ago
tyler.fun
3f183388
Copied
Failed
Copy SHA
app: swipe sideways to page the board — a one-finger flick over Day, 3 Day or Week steps it like ←/→ (a day, the window by a day, a week), with a light haptic and a 180ms slide-in (skipped under reduced motion). Touch events, passive, judged on lift: axis locks after 10px so a scroll that starts vertical stays one; needs ≥60px, |dx| ≥ 2|dy|, under 700ms. Ignored from the 24px screen edges (Safari's back swipe), text fields/editors, sideways scrollers, and while a drag, marquee, inline edit, details panel or context menu owns the touch. Month is excluded (its grid scrolls sideways on phones) and Someday sits outside the wrapper. Verified in the preview at 375px with synthesized touches: day 10-05→06→07→06, week 10-12→19→12, 3 Day 10-13→14; month, Someday, vertical-ish and edge swipes don't move
Copy SHA
Browse repository
committed
3w ago
tyler.fun
9536aaa7
Copied
Failed
Copy SHA
app: empty days' drop box no longer sags mid-drag — the add-line palette keeps its row reserved in flow (order 1), and while html.dragging lifts .tail out of flow the palette stayed put; on an empty day .list.empty is order 2, so the palette sat above the drop box and shoved it down a row while populated days' boxes started flush under the header. The palette now goes display: none during a drag (nothing can be composing then), and the body's pinned rest height (restH) keeps tiles from resizing. Verified in the preview's week view with html.dragging forced: every day's list starts 3px into its body, empty or not
Copy SHA
Browse repository
committed
3w ago
tyler.fun
dad82012
Copied
Failed
Copy SHA
app: notes toolbar puddles go edge to edge — bar padding 2px 3px and the 1px gap drop to 0, and .tb loses its border-radius, so the 24px squares tile flush against each other and the frame (whose radius clips Bold's corner). Row drops from 29px to 25px tall and ~210px to ~193px wide. Divider margin 4px 3px → 5px 0 so neighbours meet it while it stays a short rule. The coarse-pointer hit extender loses its now-pointless upward reach (inset 0 0 -8px). Verified in the preview: buttons at x 0/24/48/72 | 97/121/145/169, all flush at y 0, 24×24
Copy SHA
Browse repository
committed
3w ago
tyler.fun
8b1588a6
Copied
Failed
Copy SHA
app: tighter notes toolbar — buttons 28→24px, bar padding 4px 5px → 2px 3px, divider margin 3px 4px → 4px 3px, so the row drops from ~37px to 29px tall and ~245px to 210px wide with the 15px icons unchanged. Touch keeps a usable target via the app's invisible ::before hit extender under (pointer: coarse): no sideways reach (buttons sit 1px apart), up to the frame's overflow clip, and 8px down into the surface's 10px top padding, stopping short of the text; the toolbar gets position: relative; z-index: 1 there because ProseMirror is positioned and later in the DOM and otherwise painted over the reach. Verified at 375px with touch emulation: a tap 6px below Bold hits Bold, 9px below lands in the editor
Copy SHA
Browse repository
committed
3w ago
tyler.fun
55735779
Copied
Failed
Copy SHA
app: one 16px gutter on phones — header and toolbar drop from 22px to 16px a side, and the Day, 3 Day and Week grids from 18/24px to 14px (plus the rows' own 2px inset), all under max-width: 480px. Rows get ~8px more width for titles, and the title, view tabs and date badge now share one left edge instead of the badge sitting a few px left of the title. Desktop spacing is unchanged; ThreeDayView/WeekGrid's fixed 48px padding in the column-count math stays, since nothing under 480px fits two columns either way. Verified at 375px in the preview: every view lands its headers at 16px with no horizontal scroll
Copy SHA
Browse repository
committed
3w ago
tyler.fun
b1e08b08
Copied
Failed
Copy SHA
app: zoom rules reach the scrollers — pinch still zoomed the installed app because html/body/#app are pinned to 100% height, so nearly every touch lands inside an internal scroller (WeekGrid, DayColumn, MonthView, DetailsPanel) and touch-action stops intersecting at a scroll container: the html pan-x pan-y and body manipulation never reached them. Both now sit on * — manipulation everywhere (no double-tap zoom), pan-x pan-y under display-mode: standalone (no pinch) — and *'s zero specificity leaves the drag surfaces' scoped touch-action: none in charge. comdb gets away with the html/body version because its document is the scroller
Copy SHA
Browse repository
committed
3w ago
tyler.fun
7720385d
Copied
Failed
Copy SHA
app: no more accidental zoom on touch — body gets touch-action: manipulation, dropping double-tap-to-zoom so a quick second tap (checking off two tasks, a swatch twice) hits the control instead of zooming the page, while pan and pinch stay; it intersects down the ancestor chain so one rule covers the app, and drag surfaces already at none are unaffected. The installed app (display-mode: standalone) also drops pinch-zoom via html { touch-action: pan-x pan-y }, so the home-screen board can't be zoomed and stranded; a regular browser tab keeps pinch for accessibility. The 16px touch-input rule stays as the focus-zoom fix, and the viewport meta is untouched — maximum-scale=1 is the fallback if iOS ignores touch-action for pinch
Copy SHA
Browse repository
committed
3w ago
tyler.fun
9aca8787
Copied
Failed
Copy SHA
app: past completions stay put, series get an ending, and undo/redo joins the header — toggleDone's pull-to-today now fires only off a FUTURE day or Someday (done && day > today || day === null): checking off a task on a past day leaves it there as a record of when it happened, instead of dragging history forward. A new endRecurrence caps a series at today — until = today (keeping an earlier cap), count dropped as superseded — so every past occurrence record (completed, skipped, or untouched projection) keeps rendering exactly as it did while tomorrow onward stops; distinct from the X (remove repeat, orphans the overrides) and Delete (removes everything), and undoable like both since it rides setRecurrence's record() transaction. The details card offers it as 'End repeating' only when there's something to end (series started, doesn't already stop by today), sharing one footer row with Delete task — End flush-left, Delete pinned right via margin-left:auto so it holds the corner when End isn't offered. The header grows Undo/Redo buttons beside the account menu, mirroring ⌘Z/⌘⇧Z for mouse and touch where those don't exist — disabled (dimmed, not hidden, so the cluster never shifts) off canUndo/canRedo, with the app's coarse-pointer hit extenders. Verified in the preview: past/today completions stay, future/Someday move; ending a daily series kept yesterday's completed occurrence and today, dropped tomorrow, summary reads 'until …'; the header Undo restored the recurrence and lit Redo
Copy SHA
Browse repository
committed
3w ago
tyler.fun
903be41a
Copied
Failed
Copy SHA
app: bigger touch targets on mobile without moving anything — the small round controls (add-line color swatches at 18px, toolbar filter chips at 20px, task and subtask checkboxes at 24px, details-card swatches and icon buttons) all sat well under the ~44px a finger needs, so each grows an invisible out-of-flow ::before hit extender under @media (pointer: coarse): zero layout contribution means nothing can shift by a pixel, desktop hover/click is untouched, and where targets share a row the sideways reach stops at half the gap so neighbors meet without overlapping (the task checkbox stays modest sideways so it can't steal from the row's drag/tap surface). The one visible change is the add-line palette, whose dots go 18→22px with 10px gaps on coarse pointers only — its row is always reserved in the layout, so focusing the add line still shifts nothing. Verified at iPhone size with touch emulation: a tap 5px above a swatch's visible edge still commits the colored task through the pointerdown path, beating commit-on-blur
Copy SHA
Browse repository
committed
4w ago
tyler.fun
a07647c8
Copied
Failed
Copy SHA
app: remove shake-to-undo — the gesture can't be made trustworthy on iOS, where the whole point was: the WKWebView keeps a first-responder typing-undo stack for the add line (which stays focused through the keep-composing flow), so the OS answers most shakes with its own un-suppressible 'Undo Typing' dialog and any guard loose enough to dodge it left our undo firing on the same shake — one gesture, two competing undos, no web API to arbitrate. The module, its Settings toggle, and the motion-permission plumbing all go; the empty-draft historyUndo/Redo block in DayColumn stays, since a native undo resurrecting a committed title as a ghost draft is just as reachable from ⌘Z on desktop
Copy SHA
Browse repository
committed
4w ago
tyler.fun
ee47c008
Copied
Failed
Copy SHA
app: one shake, one undo — iOS's native 'Undo Typing' and the app's shake undo no longer answer the same shake — the activeElement check wasn't enough because presenting the system dialog can blur the field before our slower needs-three-swings detector fires, so a shake mid-composition undid the typing natively AND a whole task here; a capture-phase input listener now stamps the last real keystroke and any shake within 1.5s of it (or with a text field still focused) yields to iOS, surfaced as 'typing grace' in __shakeDebug. The other half of the double: after a commit the empty add line keeps its native typing-undo stack, so the system dialog (or ⌘Z focused there) could resurrect the just-committed title as a ghost draft — DayColumn's add field now preventDefaults historyUndo/Redo beforeinput while the draft is empty (costs the rare undo-of-select-all-delete), leaving mid-composition undo fully native
Copy SHA
Browse repository
committed
4w ago
tyler.fun
4840939b
Copied
Failed
Copy SHA
app: shake-to-undo hardening after it failed on the phone — call DeviceMotionEvent.requestPermission through the constructor instead of a detached variable (a detached static DOM method can throw 'Illegal invocation', which the catch silently read as a denial); the while-typing guard now only covers real text entry (TEXTAREA/contenteditable/text-ish inputs) so the focused Settings checkbox that just armed the feature can't swallow the very next test shake; detection loosened to the light-but-deliberate flick iOS's own gesture answers to (threshold 14→12 m/s², window 800→1000ms, still 3 distinct swings so a single bump can't fire); and a __shakeDebug() console hook in sync-log's __sync() spirit reports permission state, whether motion events flow at all, strongest jolt vs threshold, and why the last recognized shake did nothing — readable over the USB Web Inspector bridge for the next report
Copy SHA
Browse repository
committed
4w ago
tyler.fun
d8d73344
Copied
Failed
Copy SHA
app: color-submit hotkeys, shake to undo, and today-first 3-day view — ⌥1–⌥9 while composing in the add line commits the draft under that palette position (⌥0 uncolored, overriding a filter's tint; Alt because ⌘/Ctrl+digit is the browser's tab switcher, matched on e.code since macOS Option+digit reports '¡' as the key), same keep-composing flow as the swatch row and listed in the Controls panel; a new shake.svelte.ts gives mobile the ⌘Z it never had — three gravity-free jolts inside 800ms undo with a haptic bump and a cooldown, default-on where devicemotion is free (Android) and armed from a new Settings toggle on iOS whose tap is the gesture the native motion-permission prompt requires (silent re-arm on later loads' first pointerdown; stays quiet while a text field is focused so it never doubles iOS's own Undo Typing dialog); threeDays() now returns anchor/+1/+2 instead of centering, so jumping to today leads the window with today rather than burying it in the middle — title, arrows, URL collapse and rollover all follow from the anchor unchanged
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
87aeeb08
Copied
Failed
Copy SHA
app: quick-color add, fold-into-one, and a draft-based color editor — the add line grows a palette row below its squiggle (space always reserved so focus never shifts the column; pointerdown beats the commit-on-blur) where each swatch colors the draft and submits in one tap; a marquee selection's context menu gains an origami 'Fold into one task' that turns the selected titles into subtasks, preserves each source's notes/subtasks in the new task's notes under its bolded old title, lands on the first task's day, deletes the sources as one undoable step, and opens the new-task details card; the Edit color page now edits a draft with Cancel/Save (name required, presets fill the name but stay editable, presets+custom share one section, name lives in the footer row) and 'Add color' only creates the entry on Save so cancelling can't strand a hidden ghost entry
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
fb25d124
Copied
Failed
Copy SHA
app: an adopted config palette carries the record's stamp, not this device's clock — stamping now on every pull made the local copy look newer than a later edit from a device whose clock runs behind
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
287ca307
Copied
Failed
Copy SHA
app: don't let a stale config pull clobber a fresher local palette — the town.chalky.config record is one whole-record overwrite with no per-field merge, so a pull landing between a local palette edit and that edit's own push (signing in on a device that customized colors before ever syncing, or a reload racing the 800ms debounce) silently dropped the new color; stamp chalky:paletteUpdatedAt on every local palette change and have applyRemote keep the local palette (and hand config.svelte.ts a signal to re-push it once attached) whenever it's newer than the record just read
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
3639130c
Copied
Failed
Copy SHA
app: re-arm the carry-over sweep on day rollover, not just once per session — checkRollover() moved the today indicator forward at midnight for a tab or PWA that stayed alive, but applyCarryOver's one-shot-per-session gate never got a second call: a device that never cold-boots after midnight (backgrounded, resumed, or just left open) kept yesterday's incomplete tasks stuck on their old day indefinitely. The gate is now keyed by calendar day instead of a plain boolean, and checkRollover calls applyCarryOver on every real rollover it detects; the existing reconciled/carryOver guards inside applyCarryOver make that call a no-op whenever it's not actually safe to sweep yet.
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
e246055a
Copied
Failed
Copy SHA
app: reopenSession no longer leaks a failed replacement client or races itself — a failed client.restore() left clientP pointing at the new (also-dead) client with the old one dropped undisposed (a leaked BrowserOAuthClient + IDB connection per failed reopen, repeating every cycle while a WebKit glitch persists) and nothing stopped a second concurrent reopenSession from doing the same off a stale prev/installed pair; roll clientP back to prev on failure (guarded against a concurrent swap), dispose the failed client, and serialize all reopenSession calls behind one in-flight promise. Also retry restore() once after a confirmed false "deleted by another process" (reusing falseDelete) — the trail showed the replacement's first restore losing that same async confirmation race the original bug did.
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
1baae01f
Copied
Failed
Copy SHA
app: don't dispose the OAuth client a reopen replaces until it proves itself — reopenClient tore down the old IndexedDB connection before client.restore() on its replacement had succeeded, and on any failure (including the library's own unconfirmed "deleted by another process") SyncEngine.repo is never swapped away from the old session, so it kept running every request through a now-permanently-disposed connection: "Database has been disposed" on every cycle forever, a signature outside AUTH_DB_ERROR_SIGNATURE so no later cycle ever asked for another reopen. Confirmed on the phone trail: 5+ hours of dead sync until reload. Old client now only disposed after the new one restores successfully; "disposed" added to the signature regex as a backstop.
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
e1f02ecf
Copied
Failed
Copy SHA
app: marking any task done moves it to today — only Someday did this before, leaving a past/future completion stranded off-screen; toggleDone now relocates on any non-today day (recurring occurrences untouched, they stay anchored), in the same patch as the done flip so day/done/pin clears share one HLC stamp like a cross-day drag does
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
435a8aae
Copied
Failed
Copy SHA
app: cache the pfp locally so it paints on the first frame — the menu waited on session restore, then the AppView profile, then the CDN image; handle + avatar now live in localStorage (avatar as a ~5KB data URL pulled from the user's PDS via sync.getBlob, since cdn.bsky.app sends no CORS, re-downloaded only when its CID changes), shown before restore and cleared on sign-out/expiry; a cached handle no longer blocks sign-in on the profile fetch
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
8d341d0c
Copied
Failed
Copy SHA
app: reserve the page scroller's scrollbar gutter — a drag grows the target day while the origin stays pinned, tipping a just-fitting board into overflow and the classic desktop scrollbar popped in, shoving every column sideways
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
f8a85f7c
Copied
Failed
Copy SHA
app: WebKit's dead IDB connection also answers a still-stored session with undefined, not just null — the retry patch trusted that miss and "deleted by another process" kept signing the phone out; session-store misses are now confirmed on a one-shot connection (swap the shared one when it lied), a hung confirm fails transient, and onSessionDeleted/boot restore check the store before believing an unconfirmed delete while the engine reopens instead of idling; auth.falseDelete + auth.db.read.* trail events
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
cb4b6c8b
Copied
Failed
Copy SHA
app: WebKit's dead IDB connection answers a still-stored session with null — the null-as-miss patch turned that into "deleted by another process" sign-outs mid-session; BrowserOAuthDatabase now reopens its own connection and retries null/hung/dead-connection reads, trusting only a fresh connection's miss; hoist retryRestore (svelte-check)
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
24159753
Copied
Failed
Copy SHA
app: stop false "session expired" toasts — initRestore wipes the library's sub pointer on any restore error (offline, flaky launch) leaving a good session orphaned in IDB; put the pointer back and retry with backoff/online instead of booting signed out, restore orphaned sessions straight from the session store, and silence the onSessionDeleted fired by the sign-in callback's own revoke
Copy SHA
Browse repository
committed
1mo ago
tyler.fun
08e9db10
Copied
Failed
Copy SHA
app: week tiles leave paint room for the today ring — contain: paint clipped its top edge
Copy SHA
Browse repository
committed
1mo ago
Previous
1
2
3
4
Next