Commit graph

2 commits

Author SHA1 Message Date
4b04f61e4b feat(zddc): Phase 4a — drop_target cascade key, browse upload zone migrated
The last hardcoded client-side knowledge of the canonical convention
was the upload-zone regex in browse:

    var UPLOAD_SCOPES = /\/(working|staging|incoming)(\/|$)/i;

Now declared in the cascade:

  Schema:
    drop_target: true|false   leaf-only; describes THIS dir
                              (not propagated to descendants)

  Lookup:
    zddc.DropTargetAt(root, dir) bool

  Surfaced to clients:
    Directory listings carry an X-ZDDC-Drop-Target: true response
    header when the cascade declares this leaf as an upload zone.
    No header = no drop target.

  Defaults populated:
    working / working/* / staging / archive/<party>/incoming
    all carry drop_target: true. Operators can extend (e.g. drop
    files on archive/<party>/received via override) or disable
    (e.g. drop_target: false at a specific staging subtree) without
    touching code.

  Browse migration:
    loader.fetchServerChildren reads the response header and stamps
    state.scopeDropTarget on every listing fetch. upload.js's
    currentScopeAllows now reads that flag instead of regex-
    matching the URL. Initial value is false in init.js so a
    listing failure (offline / server doesn't emit the header)
    safely defaults to "no drop zone".

Phase 4a closes the most visible asymmetry between server-side and
client-side cascade knowledge. The remaining client hardcodes
(browse grid-mode regex, archive source heuristics, shared/nav
stage strip) follow the same pattern when needed — Phase 4b/c/d.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:12:41 -05:00
4af0d8ca7c feat(browse): drag-drop upload into working/staging/incoming
Drop files anywhere on a browse page; if the current scope is inside
a working/, staging/, or incoming/ subtree the files are PUT to the
current directory via the existing file API. Per-file ACL is enforced
server-side (authorizeAction); a 403 surfaces as a per-file error
toast and the rest of the batch proceeds.

UX:
  - dragenter → semi-transparent overlay with a dashed-border panel
    showing the destination path. Hides immediately on dragleave or
    drop.
  - drop → "Uploading N files…" toast, then per-file failure toasts
    inline, then a summary toast (success / partial / all-failed).
  - listing auto-refreshes after the batch so new files appear in
    the tree without a manual reload.

Scope:
  - upload-eligible paths are matched by /\/(working|staging|incoming)
    (\/|$)/i — same convention as the new grid-mode URL token.
  - 256 MiB per-file cap (UPLOAD_MAX_BYTES) since browse's single-
    body PUT loads the file as a Blob in the tab; larger uploads
    should use a dedicated client.
  - Outside the upload-eligible set the overlay never appears; drops
    are silently ignored (drag effect = none).

Sequential uploads keep progress predictable; parallel batching can
land later if needed. The module hooks document-level dragenter/leave
/over/drop so it works regardless of which pane the user drags over.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:56:15 -05:00