Commit Graph

14 Commits

Author SHA1 Message Date
Claude fdf445d2b0 perf: round 9 — decorator PUT reactivation, session/search/dedup alloc purges, PROPFIND join!, folder-level cascade
Benchmark-gated round (benches/ROUND9.md): every change carries a
BEFORE/AFTER bench with equivalence/safety gates; verdicts below are from
the committed harnesses on 4 cores / local PG 16.

Backend:
- Blob decorators (Retry/Cached) now forward put_blob_from_bytes_unsynced
  + sync_blobs — the trait default had silently reinstated HEAD-before-PUT
  per chunk on decorated remote stacks, undoing ROUND3 §8. Full production
  stack: 500 probes -> 0, 1.9x wall at 10 ms RTT (bench_s3_put §3).
- NC PROPFIND per-page enrichment triple (favorites / oc:fileid / dead
  props) overlapped with tokio::join!: 2.07x local, 2.86x at 5 ms RTT
  (bench_nc_enrich_join, injected-latency decide-by-bench).
- Search enrichment consumes its DTOs and carries the interned Arc<str>
  display fields end-to-end (SearchFileResultDto type change, OpenAPI
  shape preserved): enrich_file 2.0x, 11.6 -> 2.2 allocs/row; the NC
  REPORT conversion stops re-running all three classifiers per row
  (bench_search_enrich).
- NC session Arc end-to-end: SharedNcSession extractor (8 -> 0 allocs),
  Arc<FolderDto> chroot cache (4 -> 0/hit), single shared Arc<CurrentUser>
  + lazy span render (11 -> 6/build) (bench_nc_session).
- Storage micro-pack: atomic create_new chunk writes (2.1x fresh),
  stream_chunks over the manifest Arc (4097 -> 0 allocs/read incl. the
  Range path), manifest single-flight (herd 64 -> 1 loads), hex_lower for
  chunk Content-MD5 (18 -> 1 allocs) (bench_storage_micro).
- OCS capabilities memoized into OnceLock<[Bytes;2]>: 237x, 102 -> 0
  allocs/poll, byte-identical (bench_capabilities_static).
- Drive::is_empty COUNT(*) sum -> EXISTS: 34.4x on a 100k-file drive
  (bench_drive_is_empty).
- favorites/recents row-map ROUND7 port: path/name/blob_hash moved,
  -2.75 allocs/row (bench_resource_row_map §2).
- Folder rows decode binary UUIDs (ROUND6 §10 port): 1.03-1.07x page
  fetch, honest verdict incl. one noise-band wash documented
  (bench_folder_uuid_decode).
- Authz: file cascade decision decomposed into memoized folder-level
  decision + direct-grant lookup (ROUND8 deferred item). Cold shared-album
  first view 592 -> 418 µs/thumb; warm path unchanged; safety gates incl.
  new direct-grant sibling isolation, revoke-flush re-verified, full
  integration authz suite green (bench_thumbnail_cascade_cache).

Frontend (vitest gates committed beside the code):
- resolveLabel/resolveRecipient O(directory) scan -> id-keyed Map: 13.9x
  (recipients.bench.test.ts).
- ResourceList selection-prune effect skips when nothing is selected
  (100 -> 0 Set builds per drain) and the photos timeline reads a
  listener-fed mobile flag instead of matchMedia per recompute
  (listDerives.bench.test.ts).

Verification: cargo fmt + clippy --all-features --all-targets -D warnings
clean; 524 unit + 554 integration (--cfg integration_tests) tests pass;
frontend npm run check clean with 293 vitest tests green.

Deferred with rationale in ROUND9.md: CalDAV authz-before-fetch reorder
(maintainer sign-off), per-page batched parent resolution, JWT-claims
Arc<str>, batch_operations signature widening.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDc9VtXvskJ6dnMRraSndn
2026-07-18 16:12:04 +00:00
Edouard Vanbelle b9d6fa39c0 feat(user-pref): revert view mode as user-prefs serverside
previous change is breaking playwright tests, need to check later changes
2026-07-13 23:16:09 +02:00
Edouard Vanbelle 5aaf49859e feat(user-perf): add ui user-perf + dotfile filter
- add resource kind filter (file, folder, drive) in shared section (localStorage stored)
- add user preferences serverside store
- add client side dotfile filter (show/hide dotfiles) (user perf stored, default: dotfiles are shown)
  for security trashed dotfile are always displayed

  protection added: if a folder has only hidden items, a notification invite user to display it
  if a user rename or create a hidden item, a notification tells it to user
2026-07-13 20:27:52 +02:00
Edouard Vanbelle b73f176024 feat(drive): permanent deletion per drive 2026-06-24 21:56:27 +02:00
Bradley Nelson e3823ce470 test(e2e): Playwright + Vitest coverage harness and test instrumentation
Add an end-to-end and unit test suite for the SvelteKit frontend:

- Playwright e2e specs (tests/e2e/spa) with a throwaway container stack,
  codegen scenarios, and an Istanbul-based coverage report pipeline.
- Vitest unit tests across API endpoints, components, stores and composables.
- `data-testid` hooks on interactive elements (AppShell, FileViewer,
  ShareDialog, search, photos, files breadcrumbs, login/Nextcloud flows,
  public share pages) so the e2e suite can target them deterministically.
- Serve the SPA app-shell CSP from a <meta> policy (svelte.config.js) plus a
  middleware that skips the CSP header on HTML; move the Nextcloud Login Flow
  v2 grant page to the SvelteKit /nextcloud/login route.
- `just front-codegen` recipe and start-server-spa.sh harness.

Make the test environment robust and consistent:
- Install a deterministic in-memory localStorage/sessionStorage in the Vitest
  setup so storage behaves identically across Node versions (Node 26 ships a
  native Web Storage global that otherwise shadows jsdom's).
- Pin devenv to Node 26 + PostgreSQL 18 and pin every CI job to Node 26.3.0
  so the dev shell and CI run the same toolchain versions.

Repair the API/WebDAV (hurl) suite, which had drifted from the backend:
- Migrate the removed `/api/folders/{id}/listing` endpoint to `/resources`
  (cursor-paginated `{items:[{resource_type,resource}]}` shape) across the
  batch-copy, grants, nested-group, and WebDAV NC tests + the dav_helpers
  wipe routine.
- Stop photos_etag from uploading the dedup-tracked fixture so the dedup
  blob-lifecycle test can own its content-addressed blob exclusively.
- dedup_create now asserts the idempotent same-content re-upload (201 +
  existing file id) instead of the stale 409 expectation.

Generated coverage reports, nyc output and the e2e server runtime data dir
are gitignored rather than committed.
2026-06-22 00:05:06 -06:00
DioCrafts eef0ef5522 chore(frontend): toolchain migration checkpoint + UI perf optimizations
Checkpoint of the in-progress frontend toolchain work (Vite pinned to ^6 after
the 7/8 rolldown build break, eslint-plugin-svelte v3 navigation/reactivity
fixes, CI/Dockerfile/manifest updates) together with three UI performance
optimizations (verified on the Vite 6 build):

- Critical CSS: move auth.css/music.css off the global path into their route
  chunks (login/device/nextcloud-login, music) -- -25% gzipped critical CSS
  (~5.4 KB) on every non-auth/non-music page load.
- relativeTimeAgo: cache the Intl.RelativeTimeFormat (was rebuilt per call, once
  per row per render) -- 22.7x faster date formatting in large lists.
- Virtualize search results and grouped trash (list view) via VirtualList -- DOM
  rows mounted stay ~constant (~27) instead of O(N) (94.6% fewer for 500 hits).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:03:07 +02:00
DioCrafts edfbd68e6c fix 2026-06-20 18:15:30 +02:00
DioCrafts d98e3117b2 feat: delta/instant upload + frontend UI/UX polish
Bundles the backend+frontend delta-upload (content-dedup) feature with a
batch of frontend fixes from this session.

Upload / dedup:
- Client-hashed delta & instant upload (deltaUpload, hashWasm vendor shim)
- Backend dedup batch endpoint (dedup_service, dedup_handler, routes)
- session store owned-hash helpers; unit tests + upload-strategy bench

Frontend UI/UX:
- Colour file-type icons in grid/list (per-type tinted tiles + glyph hue)
- Robust thumbnail fallback; PDFs now show their type icon (backend
  generates no PDF thumbnails) instead of a blank tile
- Fix PDF preview: load via a same-origin blob: iframe — the API URL is
  blocked by the global X-Frame-Options: DENY in the browser's framed
  PDF viewer, matching the existing CSP `frame-src blob:` design
- Groups: localized virtual-group description (no DB schema-note leak),
  add nav.groups to the 15 missing locales, fix primary-button contrast
- Repoint --color-text-light → --color-on-accent (was faint grey on accent)
- Nudge the admin role badge off the user-menu header divider

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:33:08 +02:00
Edouard Vanbelle b1e472224d refactor(frontend): apply formatter, linter 2026-06-20 02:47:07 +02:00
Edouard Vanbelle 77521eb913 feat(shares): restore userVignette 2026-06-20 02:13:23 +02:00
Claude 8794324c3d perf(frontend): virtualize grid views + the files browser (list & grid)
Extends windowing to the remaining O(n)-DOM surfaces: the card-grid view of
ResourceList (recent / favorites / shared / shared-with-me / trash / search) and
the main file browser (`files/[...path]`), in both list and grid layouts.

- VirtualList gains a real grid mode: its inner window carries the caller's grid
  class (`files-grid-view`) and lays out `columns` cards per windowed row. The
  row pitch is auto-measured (grid card height tracks column width via the 4/3
  aspect-ratio thumbnail) and re-measured on resize.
- `useVirtualWindow` now distinguishes scroll from resize and exposes a
  `resizeTick`, so size-dependent layout (the grid pitch) is recomputed only when
  it can actually change.
- `gridColumns(width)` (new util) mirrors the CSS `auto-fill` / `--grid-card-min`
  / gap so the windowed row count matches the browser's real wrapping exactly;
  shared by both grid callers.
- The files browser flattens folders-then-files into one discriminated `entries`
  list rendered through VirtualList (list: columns=1; grid: columns from width).
  Grouped (swimlane) views stay fully rendered, as before — they're bounded.

ResourceList GRID, headless Chromium (1280x900), synthetic rows, before/after:

  rows  | mount→paint | DOM nodes | JS heap | scroll frame | jank frames
  ------+-------------+-----------+---------+--------------+------------
   1000 | 979→75 ms   | 19k→879   | 16→3 MB | 16→17 ms     | 0→0
   5000 | 4005→67 ms  | 95k→879   | 72→3 MB | 37→17 ms     | 26→0
  20000 |12015→65 ms  | 380k→879  |281→7 MB |197→17 ms     |1249→19

Rendered DOM and heap are flat (O(visible)) regardless of dataset size; mount is
~185x faster and scroll holds ~60fps. Verified visually mid-scroll (5 columns,
4/3 thumbnail tiles, cards land at the expected indices). The files browser
shares the same VirtualList path (it can't be mounted headless — it depends on
$app routing/session — so it's validated via svelte-check + the production build
+ the shared, separately-benchmarked component).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8Vb9QHmLZnEMzHz7MrFy6
2026-06-19 14:48:37 +00:00
Claude 5fdcf8cb56 perf(frontend): virtualize ResourceList rows (list view)
Large folders/collections rendered every row into the DOM, so memory and
scripting time grew linearly with item count. A 20k-row list took ~12.5s to
first paint, held ~380k DOM nodes / ~281MB JS heap, and scrolled at ~4fps.

Add a reusable, dependency-free `VirtualList` that windows rows against the
nearest scrollable ancestor (the existing `.content-area`), so the
single-scrollbar UX, sticky header and end-of-list sentinel are unchanged. It
reserves the full scroll height with a sized spacer and translates a small
window of rows; row height is auto-measured for the single-column case.

Wire it into ResourceList's flat list view (recent, favorites, shared,
shared-with-me, trash, search). Grouped sections and grid view are unchanged
for now and are the next callers of the same primitive.

Measured in headless Chromium (1280x900) with synthetic rows, before/after:

  rows  | mount→paint | DOM nodes | JS heap | scroll frame | jank frames
  ------+-------------+-----------+---------+--------------+------------
   1000 | 632→70 ms   | 19k→351   | 16→2 MB | 17→17 ms     | 0→0
   5000 | 4401→54 ms  | 95k→351   | 72→3 MB | 47→17 ms     | 136→0
  20000 | 12577→56 ms | 380k→351  |281→6 MB |258→17 ms     | 1443→0

Rendered DOM and heap are now flat (O(visible)) regardless of dataset size;
scroll holds 60fps with zero jank. Correctness verified by probing a mid-list
scroll (rows land at the expected indices, positioned within the viewport).

eslint: disable core `no-undef` for `.svelte` (TypeScript/svelte-check already
resolve identifiers, including `<script generics>` type params it can't see).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8Vb9QHmLZnEMzHz7MrFy6
2026-06-19 14:00:56 +00:00
Bradley Nelson e6279da5ce svelte refactor 2026-06-17 22:30:56 -06:00
Bradley Nelson 89e14f8f9e visual continunity 2026-06-17 22:07:18 -06:00