Repos keeps local mirrors of git repositories, cloned over https and kept up to date on a schedule.
- A Repo is a Collection (kind='repo') plus a `repository` row. The new `git` downloader clones it, then updates it
(weekly by default, or Never: the clone is kept as it is and only updated on request).
- Repos are saved in `repos/<tag>/<name>` (the Repos Directory, ignored by default) or a directory the user chooses.
An existing clone can be imported where it is. repos.yaml is the source of truth.
- A GitHub-like page shows each repo's README, files, and history, with a ZIP download. READMEs are searchable
under the global search's Other tab.
- git is hardened against hostile repos and configs: https only, pinned addresses with no private addresses (for the
repo and its submodules), sanitized configs, no hooks or links, and only clones WROLPi made or imported are
updated.
- Tagging a Repo moves its clone as one directory. Repo changes wait for repos.yaml to be imported at startup.
- The file mover groups unindexed files with no primary file as one FileGroup, and never replaces a file at the
destination. A failed tag move restores the Collection's tag. A Collection's directory cannot leave the media
directory.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Specs had no shared vocabulary for the app's data or its contexts, so each one
rebuilt both. A settings object was written out by hand wherever one was needed,
twenty-odd keys at a time, and nothing held those copies to each other or to the
API. Contexts were worse: to render a consumer a spec mocked the whole context
module, which replaces every export including the providers, so each spec
reinvented the pieces it still needed. One supplies a `Media` that always
reports desktop -- quietly leaving that component's mobile branch untested -- and
one hand-writes a context as a bare `_currentValue` with no provider, working only
because useContext reads that React internal.
test-fixtures.js is now the single definition of both, shared by jest, the
Cypress component specs, and e2e intercept bodies, so the three cannot disagree
about what the API returns. Every fixture is a function taking overrides; a
shared mutable object lets one test change what the next one sees.
The drift this prevents was already real. The theme fixture supplied `i`, `s`
and `t` holding hardcoded Semantic greys for months after ThemeProvider stopped
supplying them, and ThemeContext still declared all four. So the fixture agreed
with the tests, and with nothing else: FileBrowser was reading `inverted` off the
context and pasting the resulting `undefined` into a className, which is why the
file browser's footer -- near-white #f9fafb, with a `.inverted` dark variant that
nothing could select any more -- stayed light in dark, night and amber. A white
bar pinned to the bottom of the screen in night mode costs the user exactly the
dark adaptation the theme exists to protect. It takes tokens now.
test-fixtures.test.js holds each fixture to its context's declared shape in both
directions, so a property added to one and forgotten in the other fails a test.
Two failed attempts are recorded in test-utils.js because both were instructive.
Importing Tags.js and FileWorkerStatusContext.js so the harness could supply
those providers broke eight suites: it put both into the module graph of every
spec ahead of that spec's own mocks, and components bound to a second instance of
a mocked module -- a different jest.fn() than the test had configured, so a hook
mocked to return a value returned undefined, in a file no test mentions.
Requiring them lazily fixed that and not its mirror image: useContext read a
different TagsContext than the harness had required, so the provider was ignored
and the context default won. A harness must not change what a spec imports, so
it owns only contexts/contexts.js and a spec passes any other context itself.
DomainTagging is converted off the `_currentValue` fake as proof, and now asserts
the tag name a user sees rather than a test id its own mock invented. The five
remaining specs are listed in a ratchet so the count can only fall.
974 jest, 15 browser component, 46 e2e, clean cache-busted build, tsc clean.
Fixtures are confirmed absent from the shipped bundle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Greptile's three findings on #590, all confirmed real before fixing, and each one
verified red against the original code afterwards.
P1, the directory fields had no accessible names. All five rendered their
visible label as a bare <label> with no htmlFor while passing no label prop, so
their inputs had a description ("/media/wrolpi/") and no name at all -- five
fields in one grid, holding similar relative paths, indistinguishable to a
screen reader. The labels now belong to the inputs, InfoPopup content and all.
This predates PathInput; the old TextInput call sites had the same gap, and
moving to a component that supports labels is what made it fixable.
P1, description and error were rendered but never referenced. aria-describedby
pointed only at the prefix, so the help text listing the variables a path
accepts, and the reason a path was rejected, were both invisible to assistive
technology. Both are now in the description, and an errored field reports
aria-invalid.
P2 was worse than reported: at `flex: none` a long prefix took the entire row
and left the input eighteen pixels wide -- not "consumes the editable area" but
a field nobody can type in. The media directory is whatever the API reports, so
its length was never ours to assume. The prefix now shrinks and ellipsises at
half the control, with the full path on `title` and in the accessible
description.
The naming fault could not have been caught where the others were. jsdom cannot
see layout, and the Settings page needs enough mocking that it had no test at
all -- which is why these five fields were uncovered when they broke. The new
e2e spec intercepts the API and lets the real page render, so it measures the
names and the geometry a user actually gets. Reverting one field to a bare
label fails it; a long prefix fails the browser spec at 18px.
962 jest, 15 browser component, 46 e2e, clean cache-busted build, tsc clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>