Safari does not treat `/` as a soft break opportunity inside inline
`<code>`, so long paths overflow the fixed-width stacked note column.
Use `overflow-wrap: anywhere` so the column can shrink to its max-width
instead of being pushed wider by unbreakable inline code.
The singleton `md` instance is mutated by `useShikiji()` asynchronously,
but `content` computeds had no reactive dep on that loading, so the
initial render on page reload never picked up syntax highlighting.
Link-click navigation appeared to work only because Shikiji was already
installed in `md` by an earlier render.
Adds specs for the files added or modified in the recent merge that
weren't yet covered: oauthReturnPath, withATProtoImages, link,
displayLanguage, and the useGitHubContent / useImageUpload /
useNoteFreshness hooks. Locks in the new pullLatest contract
({raw, failureStatus}) and the fetchLatestSha branch semantics
(ok/unauthorized/offline).
The Gitea cache server at 10.0.23.3:42951 is unreachable from the
job container. Each cache restore and save hangs ~4m40s on TCP
timeout before falling back to a cold run, costing ~9m per job.
Removing `cache: pnpm` brings total CI time down to ~45s.
Resolves conflict in repo.spec.ts by combining getRepoPermission
tests from main with the getFiles and queryFileContent tests from
the local test pass. Adds getRepoPermission to the userRepo.store
spec mocks so the new permission probe added upstream doesn't break
the store tests.
Fetch the viewer's push permission via GET /repos/{owner}/{repo} when
entering a repo and gate every write affordance behind it: edit and
image-upload buttons in StackedNote, new-fleeting-note and YouTube
buttons in FleetingNotes, and interactive checkboxes in TodoNotes
(rendered disabled when read-only). Anonymous viewers get the same
treatment because the permissions field is absent without auth, which
collapses to canPush = false.
Previously every write button was visible regardless of role, so
read-role collaborators and anonymous viewers could enter edit mode and
type before the save failed with 401/403.
Vue's defineEmits generates per-event overloads, so emit(action) with
a union of literal types failed to type-check. Split the single
choose() helper into one handler per action.
Callers had to read back the side-effected status ref to know why a
pull failed, which also broke TS narrowing on the freshness badge
handler. Return { raw, failureStatus } so the reason flows through
the return value.
Show a cloud-lock badge with a re-sign-in tooltip when the freshness
check resolves to unauthorized, and toast on badge click so the user
gets explicit feedback instead of a silent retry-loop.
fetchLatestSha and queryFileContent were silently catching every error
(including expired-token 401s) and returning null, so the freshness
badge could stay on "offline" with no UI hint that re-auth was needed.
Wrap both calls in runWithAuthRetry to recover from refreshable tokens
transparently, and switch fetchLatestSha to a tagged result so the
freshness hook can distinguish auth failure from network failure.
Moves the user identity element out of the tabs bar (where it was
absolutely positioned over the tabs) into a proper header row next to
the home button, and switches it from SignInAtproto to the shared
UserPill so the same atproto-first identity shows everywhere.
Replaces the duplicated profile-chip button and profile_modal dialog in
WelcomeWorld and RepoList with the shared components, and removes the
now-orphaned profile-chip, hw-modal, hw-ms-label, hw-btn-ghost, and
hw-rule styles. As a side effect the displayed identity now prefers the
ATProto handle over the GitHub username.
UserPill renders an avatar + display name button, preferring the ATProto
handle over the GitHub username, and emits @click so each view decides
what to open. ProfileModal extracts the previously duplicated
profile_modal dialog with self-contained DaisyUI-token styling.
Use RFC 6570 reserved expansion ({+path}) so slashes stay literal in
the request URL, and pin the contents call to API version 2026-03-10
to clear the Sunset: 2028-03-10 header on the deprecated 2022-11-28
version. The only breaking change in 2026-03-10 (submodule type in
directory listings) doesn't affect us — fetchLatestSha is per-file
and the array branch already short-circuits.
Starred section used to intersect favorites with the paginated GitHub
repo list, so favorites past the first page only appeared after the user
scrolled. Derive RepoBase directly from PouchDB favorites and exclude
them from "all repos" by both id and name.
Stale FavoriteRepo docs with old GitHub IDs (e.g. from deleted or
recreated repos) surfaced as duplicates once the prefix scan started
returning every doc.
Expose a failures list alongside the existing failedNotes counter so each
failure carries kind/path/sha/message. Also catches throws from
buildNoteDocs and bulkUpdate that previously aborted the whole loop.
Replace the failure counter with a list of typed failures (fetch,
build, save, readme, unknown) carrying path, sha, message, and
affected doc ids so callers can surface diagnostics instead of just
a count.
Switch from /search/repositories (best-match order) to /user/repos
with sort=full_name so infinite scroll appends in A–Z order instead
of injecting new pages into earlier letters.
Fetch blobs with a concurrency of 8 instead of one at a time, batch
PouchDB writes via bulkUpdate, filter already-cached files up front,
and run the README fetch in parallel with the worker loop.
The home page reads README via getCachedMainReadme, which uses
Note-{owner}-{repo}-README, not the blob SHA. Cache-all only wrote
SHA-keyed entries, so the homepage README was empty offline.
A single null from queryFileContent silently exited cacheAllNotes,
leaving every later note uncached while the success toast still fired.
Skip the failing file, count failures, and surface them via errorMessage.
Before, every GitHub call awaited indefinitely with no AbortSignal,
so a "lie-fi" connection (technically online, effectively dead) left
FluxNote stuck on the skeleton loader and the freshness badge spinning
forever. Inject an 8s AbortSignal.timeout via octokit.hook.before
(20s override on the recursive tree fetch), classify failures in the
userRepo store as auth vs network, and surface a "Couldn't reach
GitHub" retry button when no cached content is available.
Cached GitHub responses resolved so fast that the spinner state was
overwritten before Vue could render it, so clicking the badge from
"Not checked" looked like nothing happened.
The exported SVG carries font-family="cmr7"/"cmsy7"/… attributes but
no @font-face, so standalone viewers fall back to system fonts. That
scatters TikZ glyphs (precomputed x positions assume Computer Modern
metrics) and turns cmsy7 minus signs into ¡ (custom symbol encoding).
Inline only the rules for font-families the SVG actually uses, and
await document.fonts.ready before rasterizing to PNG.
Attach SVG and PNG (×2) download buttons to every rendered SVG (TikZ,
Mermaid, …) once post-render finishes. Exports always use the original
colors with a white background baked in, regardless of theme.
useMarkdown.hook.ts imports types from shikiji-core, but it was only
present transitively via markdown-it-shikiji. pnpm's strict resolution
blocks importing undeclared transitives, breaking type-check.
Pinned to 0.10.2 to match the version markdown-it-shikiji@0.10.2 pulls
in. The shikiji line is deprecated upstream in favor of shiki; a real
migration is out of scope here.
pnpm resolves every package via a symlink into .pnpm/; preserveSymlinks
pinned resolution to those symlink paths, blocking the vue ->
@vue/runtime-dom -> @vue/runtime-core re-export chain and surfacing
"has no exported member" errors for Ref, toValue, computed, etc.