Commit Graph
37798 Commits
Author SHA1 Message Date
Claude 80bb3ca02b refactor(storage): swap delete-marker thumbnail icon to Archive
MinusCircle read as "removed / null" — Archive reads closer to the
delete-marker's actual role (a placeholder recording that a delete
was applied at this point in history).
2026-08-19 09:18:53 +00:00
Francesco Sansalvadore 25e4044161 update lifecycle policy warning copywriting 2026-08-19 11:15:35 +02:00
Claude ce403b9920 refactor(storage): shorten no-lifecycle-policy warning to one sentence
"Without one, every noncurrent version is retained indefinitely and
keeps counting toward storage costs." — replaces the previous two-
sentence description.
2026-08-19 09:01:41 +00:00
Claude cbc7887ad5 fix(storage): keep overflow visible on idle Collapse
The Collapse wrapper's fixed overflow-hidden was clipping focus rings
and outlines on nested inputs at rest. Switch to a variants-based
approach where overflow: hidden is only in effect while the height is
actually animating — transitionEnd flips it back to visible once the
enter animation settles.
2026-08-19 08:51:52 +00:00
Claude 65605d87ca feat(storage): animate lifecycle form sections in/out
Wrap conditional sections in framer-motion AnimatePresence so height +
opacity ease in and out instead of snapping — subtle (180ms), applied
to:

- The suspension notice / upgrade prompt / public-bucket + tightening
  warnings / whole LifecyclePolicySection (versioning toggle).
- The both/either mode row (both retention conditions set).
- The "No lifecycle policy" empty-state warning.

`initial={false}` on each AnimatePresence so a section that's already
open on modal-open doesn't animate in on first paint — only actual
user-driven toggles animate.
2026-08-19 08:44:55 +00:00
Claude e05617ba88 refactor(storage): tighten lifecycle blurb, dim disabled retained-versions row
- Trim the section description to "Automatically expire noncurrent
  versions".
- Dim the entire Retained noncurrent versions row (label + hint +
  input) at opacity-60 when the age field is empty, so the dependency
  reads at a glance rather than only the input being greyed out.
2026-08-19 08:23:35 +00:00
Claude 93d67ab446 feat(storage): surface delete markers, gate cap on age, add policy blurb
Version history:
- Add "delete marker" as a first-class ObjectVersion action and seed
  one into the mock so a stale marker shows up in the history.
- Render it with a struck/dashed thumbnail glyph and a "Delete marker"
  label, and drop the download/URL/restore actions — the only useful
  action on an empty placeholder is permanently removing it.
- Row is non-clickable (nothing to preview) and dimmed.
- Confirmation copy is tailored to markers (no version id shown, no
  "current version is not affected" — there's no meaningful content).

Bucket lifecycle policy form:
- Add a one-line description under the "Lifecycle policy" heading
  explaining what these policies do.
- Disable the "Retained noncurrent versions" input when
  "Noncurrent version expiration" is empty (S3 requires the
  noncurrent-days condition on any noncurrent-count rule). A short
  description under the field explains the dependency.
- Clear the retained value on the exact age→empty transition so a
  save can never persist an orphaned cap, but keep any pre-existing
  orphaned value visible (and editable once age is set again) rather
  than silently discarding it on modal open.
2026-08-19 08:03:52 +00:00
Claude bbfb4de321 refactor(storage): guarantee vertical + full-width mode row layout
Replace the FormItemLayout wrapper for the both/either row with a
plain label + RadioGroupStacked stack — the previous default-vertical
FormItemLayout wrap was producing the correct DOM but the effect
wasn't visible depending on cache/reload state. Explicit
`flex flex-col gap-y-2 w-full` removes any ambiguity: label above,
radio group below, spanning the full section width.
2026-08-17 15:29:11 +00:00
Claude 9710563a01 refactor(storage): stack mode selection vertically, move to end of section
Move the both/either RadioGroupStacked to the bottom of the lifecycle
policy section and drop layout="flex-row-reverse" — with the default
vertical layout the label sits above the radio group and the group
spans the full section width, so the two-line option descriptions have
room to breathe. Descriptions tightened to fit the wider footprint.
2026-08-17 15:13:13 +00:00
Claude 970535a8fb refactor(storage): align lifecycle policy mode row with adjacent inputs
Wrap the both/either RadioGroupStacked in FormItemLayout with
layout="flex-row-reverse", matching the two number inputs above and
below — so the label sits on the left and the radio group aligns with
the input controls above/below.

Options relabelled to read naturally with the new "Expire when" left-
hand label ("Both conditions are met" / "Either condition is met").
2026-08-17 15:03:14 +00:00
Claude fe18c6c608 refactor(storage): use stacked radio group for lifecycle policy join mode
Swap the ToggleGroup BOTH/EITHER control for a stacked RadioGroup from
the design system — each option gets a concise one-sentence
description of which logic it applies, matching the way similar
mutually-exclusive choices are surfaced elsewhere in Studio.
2026-08-17 14:50:53 +00:00
Claude 598ada9937 refactor(storage): trim lifecycle policy section, confirm suspension on save
Lifecycle policy section:
- Drop the descriptive intro sentence, the broom icon on the heading,
  and the collapsible "See how this works" example — three ways of
  saying the same thing, per feedback.
- Move the BOTH / EITHER join-mode toggle out of the sentence and into
  a proper ToggleGroup that sits between the two condition inputs,
  visually joining them (only rendered when both conditions are set —
  a single condition has nothing to combine).

Suspension flow:
- Trim the inline admonition down to a one-liner heads-up.
- The full "already-retained data stays, no new versions get created,
  re-enable any time" explanation moves to a ConfirmationModal that
  fires on save when the user is suspending a live-versioning bucket;
  confirming replays the write, cancelling bails.
- No confirmation for buckets that were already suspended.
2026-08-17 13:56:53 +00:00
Claude f732560823 refactor(storage): rename "expiration policy" to "lifecycle policy"
Renames the noun phrase that names the policy object — the section
heading in the create/edit bucket modals, the file preview panel
tooltip title, the empty-state copy in the archived / trash views, and
the internal ExpirationPolicySection component — to "lifecycle policy",
matching S3's own terminology.

Preserves verb / action-form uses of expire/expiration where they
describe what happens when the policy fires (ExpirationSentence,
ExpirationExplainer, ExpirationMode, version_expiry_days, "Noncurrent
version expiration" label, etc.) — those name the action, not the
policy.
2026-08-17 13:31:54 +00:00
Claude eb64004f4c feat(storage): prefill expiration policy inputs with 30 days / 10 versions
Create modal: expiration inputs default to 30 days and 10 versions
instead of empty, so toggling versioning on surfaces a policy that's
ready to save. Clearing a field still removes that condition from the
policy.

Edit modal: same defaults, but only when the bucket has never had
versioning enabled — buckets already versioned keep their existing
values (including explicit null == "no limit on this condition"), so
the modal never silently retro-adds a condition the user removed.
2026-08-13 13:23:31 +00:00
Claude 2977c62c34 refactor(storage): softer archived stripes, sort archived rows with live items
- Thinner + fainter skewed stripes on archived rows (1px lines at 5%
  alpha with 7px spacing, was 6px lines at 9% alpha with 12px spacing).
- Sort merged archived + live rows together using the explorer's current
  sortBy/sortByOrder preference instead of always appending archived
  items at the end. Falls back to deletedAt for archived items that
  don't have their own created_at/last_accessed_at so they still land
  somewhere sensible; live-item nulls-last behavior is preserved.
2026-08-13 10:13:53 +00:00
Claude 5ec4bd18b5 feat(storage): overlay archived files inline in the normal file explorer
Adds a "Show archived" option to the view-options dropdown in the file
explorer header (versioned buckets only). When enabled:

- Archived (soft-deleted) files show up in the folder they used to live
  in, alongside live objects — coalesced under a synthesized parent
  folder when the whole path is gone (an ancestor with no live
  descendants at that level). A live item of the same name always wins.
- Archived rows and synthesized folders render with a faint skewed
  stripes background + dimmed foreground so they read as
  "recoverable-not-active" at a glance, and their row-level actions
  dropdown is replaced by an "Archived" tag.
- Clicking an archived row swaps the normal preview slot for the
  archived-file preview panel (already used in the standalone Show
  archived view), so restore + permanently-delete actions live in one
  consistent place. Clicking a live row or opening a folder clears it.

The standalone "Show archived" view is unchanged and remains the
destination for bulk selection and cross-folder browsing.

Backed by getArchivedOverlayItems (with unit tests) which handles the
tolerance for the mock trash data's inconsistent originalPath/name
duplication, and skips synthesizing archived stand-ins when a live
item of the same name is already at that level.
2026-08-13 09:54:39 +00:00
Claude e858bbcf9a refactor(storage): show version id instead of "Was current" badge/action label
Both the archived files table and the redesigned preview panel now show
the version's own id on every row, dropping the "Was current" badge and
the "(overwrite)"/"(initial upload)" action label.
2026-08-12 19:59:02 +00:00
Claude 8fd8f600e2 feat(storage): rework archived files view — terminology, group/version semantics, redesigned preview
- Rename "Show deleted versions" toggle to "Show archived" and align copy
  throughout this surface to archive/archived/soft-deletion terminology,
  consistent with the S3 delete-marker model without exposing that term to
  users.

- Fix restore semantics: restoring any row — the top-level group or an
  individual version — now fully un-archives the whole file (removes the
  delete marker), rather than the previous mock behavior of just filtering
  a version out of the list without actually restoring anything.

- Abstract the delete marker away: the version that was live at the moment
  a file was archived is folded into the same merged, expandable version
  list as its retained noncurrent versions (tagged "Was current"), instead
  of being implicit in the top-level row's own fields. Permanently deleting
  that specific row promotes the next most recent version in its place via
  a new deleteCurrentTrashVersionPermanently mock + mutation hook.

- Fix bulk restore/delete to correctly split a mixed selection of top-level
  object ids and nested version keys and route each to the right endpoint
  (new DeletedFilesList.utils.ts, with unit tests) — previously a bulk
  action on a selected nested version silently did nothing.

- Redesign DeletedFilePreviewPane to match the live file preview panel:
  same header/close-button chrome, a version-select swaps the top slot for
  a restore widget (mirroring the live compare-and-restore widget, minus
  the comparison since there's no live "current" to compare against), and
  a collapsible Versions section reusing VersionThumbnail row styling.
2026-08-12 19:31:31 +00:00
Claude acc4341fa9 feat(storage): scroll preview panel to top on version compare
Selecting a version swaps the top preview slot for the compare
widget, which is easy to miss if the version list further down is
where the click happened. Scroll the panel's own scroll container
back to the top when a version is selected for comparison.
2026-08-12 16:09:00 +00:00
Claude 1d3bff6f7c Revert "feat(storage): sticky version-comparison mode in preview panel"
This reverts commit 14cba432fa.
2026-08-12 16:03:22 +00:00
Claude 14cba432fa feat(storage): sticky version-comparison mode in preview panel
The compare widget no longer just swaps the default preview slot - it
sticks to the top of the panel's own scroll area while the version
history scrolls underneath it, and the "Added on"/"Last modified"
fields hide while comparing since they aren't relevant to that
decision. Thumbnail height now shrinks with viewport height using the
same clamp() pattern as the default preview.
2026-08-12 15:48:38 +00:00
Francesco Sansalvadore 2c9ccef68b no font-mono on file preview name 2026-08-12 17:34:50 +02:00
Francesco Sansalvadore fff3ec57d8 file preview panel actions refinements 2026-08-12 17:13:16 +02:00
Francesco Sansalvadore c0f7a4a27f file versioning previews 2026-08-12 16:39:17 +02:00
Claude 01e72ecec9 refactor(storage): tighten preview panel per feedback
1. Reduce the gap between the file name and its mime/size meta line in
   the header, and drop the redundant "· N versions" suffix (already
   shown by the Versions section header below).
2. The expiration-policy info tooltip now has a title ("Expiration
   policy") and a link to open the Edit bucket settings modal (via the
   shared `edit` query-state key), so there's a path from "what's the
   rule" to "where do I change it".
3. Drop the "Kept" label on version rows — it's the default state, so
   showing nothing is quieter than repeating it on every row.
4. Under an EITHER policy, a version at the cap boundary now shows its
   age countdown alongside "Expires on next upload" (e.g. "+ expires
   in 29d"), since that date is still meaningful context even though
   the cap would evict it sooner.
5. Renamed the "N kept" chip to "N retained", and unified all policy
   chips (day/retained tokens + BOTH/EITHER) to share one visual style
   instead of two different treatments.
2026-08-12 10:36:17 +00:00
Claude 827f4ed7d5 feat(storage): apply design handoff (7d) to the file preview panel
Implements the "File preview panel — reorganized" handoff:

- Header consolidates to file name/meta + a single kebab (Download, Get
  URL, divider, Delete file) plus the close button, separated by a
  divider — no more standalone action row further down the panel.
- The panel is one scrolling column; the preview is no longer pinned.
- Every version row gets a small type-glyph thumbnail; the current
  version gets a distinct brand-colored restore glyph.
- Selecting a noncurrent version turns the top slot into an inline
  compare-and-restore widget (selected → current, with a right arrow),
  instead of swapping the big preview or opening a modal. The selected
  row is tinted and shows a "Comparing" pill.
- Retention policy collapses to one inline row: a broom-and-sparkles
  icon, mono tokens (30d / N kept), a greyscale BOTH/EITHER badge only
  when both rules are configured, and an info icon with the full rule.
- Per-row state is now one of exactly four inline labels — Kept /
  Expires in Nd / Expires on next upload / Expiring now — derived by
  computeVersionFate() per the "never promise a date that isn't certain
  yet" rule, replacing the old tooltip-only indicator and #N-of-M badge.
- The soft-delete explainer moves out of the always-visible panel and
  into the delete confirmation step, shown only for a single-file
  delete on a bucket with versioning history.

Added VersionHistory.utils.ts + tests covering the 7 reference policy
cases from the handoff's operator-notation matrix.
2026-08-12 08:42:48 +00:00
Claude 9df6ae958a refactor(storage): tighter version list, icon-only expiry, dropdown padding
- Preview thumbnail max height dropped from 230px to 180px
  (formula: clamp(120px, calc((100vh - 144px) * 0.4), 180px))
- Actions dropdown menu items now use gap-x-2 so the icon and
  label have breathing room instead of butting up against each
  other
- Per-version expiry indicator collapses into a single broom-
  sparkles icon next to the version date; the previous text +
  badge combo ("Expires in 5d" + "2/3") is now the tooltip
  content, keeping the row compact while preserving all the
  policy detail on hover
- Remove the dot column and its wrapping flex so each version
  sits flush to the left edge of the panel
- Rewrite the header both/either explainer as a full sentence
  so the logic is self-explanatory ("Versions are removed only
  when they exceed both..." / "...as soon as they exceed
  either...")
- Move the broom-sparkles icon to the right of the 'Expiration
  policy' section title in the bucket modal

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 16:55:39 +00:00
Claude 79d6718996 fix(storage): tighten preview panel height clamp
Cap the preview thumbnail at 230px (was 320px) and derive the
target height from what's left of the viewport instead of raw
vh — ~144px of chrome sits above (close button + padding +
filename summary), so a plain vh clamp waited too long to shrink.
Now uses clamp(120px, (100vh - 144px) * 0.4, 230px) so the
preview starts giving room to the sections below noticeably
sooner as viewport height drops.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 16:36:27 +00:00
Claude 6239581b7d feat(storage): redesign file preview panel
- Move filename and summary details right below the preview
  thumbnail, above the sections
- Preview thumbnail is now viewport-height aware via
  clamp(120px, 40vh, 320px): stays visible above scrollable
  sections on tall viewports, and shrinks to a 120px floor on
  short ones so the sections always have room to scroll
- Replace the Details/Versions tabs with vertically-stacked
  collapsible sections (PreviewSection) — both default open
- Remove the connecting vertical line between version dots
- Remove the truncated version id from each row (it's cognitive
  overhead; the timestamp is the visible handle)
- Remove the action label ('overwrite', 'initial upload') from
  each row — no meaningful information gain
- Move per-version actions (Download, Get URL, Restore, Delete)
  into a MoreVertical dropdown; delete permanently uses the
  destructive text color
- Add a shared inline BroomSparklesIcon (Lucide broom-sparkles
  isn't in the pinned lucide-react version) and use it as the
  expiration indicator icon in the version list and next to the
  'Expiration policy' section title in the bucket modal

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 16:15:35 +00:00
Claude 3b55b3fb2b refactor(storage): use Trash2 icon for expiry indicator
Swap the Brush icon for the Trash2 icon already imported in this
file — cleaner semantic ('this version will be deleted') and one
fewer imported symbol.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 15:33:40 +00:00
Claude 099c9dace6 fix(storage): use Brush icon (BrushCleaning not in lucide-react 0.436)
Studio pins lucide-react ^0.436.0 which predates the BrushCleaning
icon (added later). Swap for the closest available Brush icon so
the version-history page builds again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 15:13:01 +00:00
Claude 1720b297f5 feat(storage): flip version numbering, fix edit-bucket persistence
- Flip noncurrent version numbering so the oldest (closest to
  eviction, at the bottom of the list) is #1 and the newest is
  the highest number — matches how the list reads top-to-bottom
- Change the '#N of M' text label to a compact 'N/M' Badge
- Add a brush-cleaning icon in front of the 'Expires in Nd' line
  to signal auto-cleanup at a glance
- Remove the 'At cap — ' prefix from the cap warning
- Fix EditBucketModal persistence: emptying a versioning input
  and saving now correctly clears the setting (was silently
  falling back to the prior stored value); also switch the write
  key to bucket.name for consistency with the read path
- Add id={field.name} + inputMode='numeric' on the versioning
  inputs so label clicks focus the input and mobile numeric
  keyboards surface

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 15:06:31 +00:00
Claude eeeae037da feat(storage): make version list mocks adapt to bucket policy
- Remove the 'Restoring is non-destructive' admonition from the
  bottom of the Versions tab
- Refactor getMockObjectVersions to accept the bucket's current
  cap and expiryDays and adapt its output so version-list labels
  are always consistent with the policy:
    * Never returns a version older than the retention window
      (no 'Past Nd limit' surprises when the user set fewer days)
    * Never returns more noncurrent versions than the cap
      (no '#3 of 2' when the user tightened the cap)
- Anchor all version timestamps to the real clock via
  daysAgoFromNow so the mock stays fresh regardless of when the
  prototype is opened, instead of drifting away from the fixed
  BASE_DATE
- Wire useObjectVersionsQuery to look up the bucket's protection
  and pass it into the mock on every fetch, so policy edits from
  the bucket modal reflect in the panel next time it opens

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 14:40:45 +00:00
Claude b3d683628e feat(storage): add per-version expiry indicators to version history panel
Show expiration policy context directly in the Versions tab:
- Cap badge and retention badge in the header section
- Combined mode indicator (both/either) when both policies are set
- At-cap and nearing-cap warnings
- Per-version VersionExpiryIndicator showing days remaining and
  cap position (#N of M) with warning colors based on combined
  and/or policy logic
- Pre-computed noncurrent indices for efficient lookup
- Versioning-suspended admonition banner

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 14:21:44 +00:00
Claude 93ee4b82b8 fix(storage): validate expiration inputs inline on change, simplify error
- Call trigger() after each field.onChange so zod validation runs
  immediately as the user types, surfacing errors via FormMessage
- Simplify the S3 cap error message to 'Cannot exceed 100 versions'

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 14:05:54 +00:00
Claude 54c9f84cb3 fix(storage): enforce S3 max 100 versions via zod, remove native HTML validation
- Add S3_MAX_NONCURRENT_VERSIONS constant (100) and enforce it in
  superRefineBucketProtection independently of plan limits
- Remove HTML min attributes from both inputs so only Supabase inline
  errors (FormMessage via FormItemLayout) appear — no browser tooltips
- Plan-specific bounds still enforced as before; the S3 cap is checked
  first with a distinct error message

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 13:53:56 +00:00
Claude 57b359af4e fix(storage): rewrite explainer examples in active voice with full sentences
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 13:05:10 +00:00
Claude 690f6dabdf fix(storage): improve explainer examples for both/either scenarios
- 'both' mode: show a second example where one condition isn't met,
  making it clear the version is kept because both must be exceeded
- 'either' mode: show two examples — one where only the version cap
  is exceeded, one where only the age limit is exceeded — to
  illustrate that either condition alone triggers deletion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 12:44:58 +00:00
Claude 6c5622c4a5 fix(storage): simplify explainer examples to +1 above the set values
- Days only: show a version at days+1 (deleted) vs days-1 (kept)
- Versions only: show versions+1 total, oldest one gets deleted
- Both conditions: show versions+1 at days+1, explain the and/or logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 12:16:27 +00:00
Claude f2713c4214 fix(storage): use 'versions' suffix and show explainer for single conditions
- Change InputGroupText suffix from 'max' to 'versions' on the retained
  noncurrent versions input
- Show 'See how this works' explainer when either expiration field has a
  value, not only when both are set
- Add context-specific explainer text for days-only, versions-only, and
  both-conditions cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 11:04:12 +00:00
Claude b9b8a7cb12 refactor(storage): use InputGroup design system pattern for expiration inputs
- Replace custom raw <input> elements with FormItemLayout + InputGroup +
  FormInputGroupInput + InputGroupAddon pattern from the design system
- Use layout='flex-row-reverse' for label-left, input-right alignment
- Both version_expiry_days and max_noncurrent_versions default to empty
  (no prefill on toggle)
- Add warning Admonition when no expiration policy is configured to
  alert about ongoing storage costs
- Remove unused useFormState import and prefill logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 10:53:41 +00:00
Claude 98e9e12a05 Redesign expiration policy section with sentence builder and inline toggle
Replace the standard form-field layout with the design-handoff UI:

- Sentence builder paragraph that adapts to which fields are filled
  (both empty → 'retained indefinitely', one filled → describes that
  condition, both filled → sentence with inline toggle)
- Inline [both | either] pill toggle embedded in the sentence text
  when both conditions are set (maps to ExpirationMode 'and' / 'or')
- Compact number inputs with suffix labels ('days' / 'max'),
  right-aligned text, and em-dash placeholder
- Collapsible 'See how this works' section with a dynamic example
  that uses the actual input values and updates on toggle
- Both fields are now optional — empty means 'no limit' for that
  condition. Validation only checks min/max when a value is provided.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 10:27:06 +00:00
Claude 6e8a01d7cb Use solid HSL colors for tree connector lines to avoid overlap artifacts
Replace bg-border-stronger (oklch with alpha) with solid hsl values:
- Light mode: hsl(0 0% 80%) — neutral mid-gray
- Dark mode: hsl(0 0% 30%) — neutral mid-gray

This prevents the accentuated color at the intersection where
the vertical and horizontal lines overlap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 10:06:22 +00:00
Claude c20faa7ff7 Move expand/collapse to header bar, dim tree lines, increase gap
- Lift expandedVersionIds state to DeletedFilesContext (shared between
  DeletedFilesList and FileExplorerHeader)
- Move 'Expand all' / 'Collapse all' buttons from the table header into
  the FileExplorerHeader top bar with outline button variant
- Change tree-connector line color from bg-foreground-muted to
  bg-border-stronger for a subtler appearance
- Increase gap between horizontal tree branch and version title text
  (pl-[22px] → pl-[36px], w-[11px] → w-[8px])
- Apply same tree line changes to standalone Trash page (TrashList)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-07 14:59:12 +00:00
Claude 889146ee06 fix(storage): stronger tree lines, gap before text, text expand/collapse buttons
- Tree connector lines use bg-foreground-muted instead of bg-border for
  better visibility
- Horizontal branch shortened from w-[15px] to w-[11px], creating a 4px
  gap between the line end and the version text (text still aligns with
  parent name)
- Replaced the icon-only expand/collapse toggle in the table header with
  two separate text buttons: 'Expand all' and 'Collapse all', separated
  by a dot divider

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-07 14:35:06 +00:00
Claude cd47cebb5a feat(storage): tree-line connector for nested version rows
Replace CornerDownRight icon with CSS tree-connector lines for nested
version rows. Each version draws:
- A vertical line from the top border to the junction point (center)
- A horizontal branch from the junction to the version text
- For non-last versions, the vertical line continues to the bottom
  border, connecting seamlessly with the next row
- For the last version, the vertical line stops at the junction (└ shape)

Lines are positioned at left-[23px] to align with the parent row's
chevron center (16px cell padding + 7px icon center). Version text
starts at pl-[22px] to align with the parent row's name text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-07 13:21:01 +00:00
Claude 4b3960cbc4 feat(storage): deleted files table — version selection, preview, mutations, expand/collapse all
- Version rows are now selectable via checkboxes for batch multi-selection,
  using composite keys (objectId::versionId) in the shared selection state
- Clicking a version row opens the preview panel with version-specific
  metadata (version ID, action, created date, size) and a 'no preview
  available' placeholder
- Aligned CornerDownRight icon with parent row chevron and version text
  with parent name text (both use gap-x-2 and size-14 icons)
- Added expand/collapse all toggle button in the table header top right
  (ChevronsUpDown / ChevronsDownUp icons)
- Restore and delete actions on version rows now perform real client-side
  effects: version-level mock store mutations + query cache invalidation
- Removed the Expires column from both DeletedFilesList and TrashList
- Extended DeletedFilesContext with selectedDeletedVersion state
- Added version-level mutation functions to protection-mocks.ts
- Added useTrashVersionRestoreMutation and useTrashVersionDeleteMutation
  hooks to bucket-trash-query.ts
- Updated Trash page (standalone) with version-level mutation wiring

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-07 12:55:51 +00:00
Claude 7c2b5bbce3 feat(storage): versioning hints, version URLs, nested deleted versions
- PreviewPane: add Info hint below Delete button when versioning is
  active, explaining that deleting soft-deletes the object and all
  versions and they can be restored from the deleted versions view
- VersionHistory: add Get URL (Copy) icon button on every version row
  alongside existing Download/Restore/Delete actions
- DeletedFilesList & TrashList: implement collapsible nested grouping
  for noncurrent versions under parent objects with ChevronRight/Down
  expand toggle, version count badge, and per-version Restore/Delete
  actions on hover
- protection-mocks: expand mock data from 8 to 12 trash objects with
  richer version histories (up to 6 noncurrent versions per object)
  to properly showcase the nested deleted versions UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-07 12:02:09 +00:00
Claude 3a0530d2d7 Add AND/OR expiration toggle, move deleted-versions toggle into header, update terminology
Task A: Add ExpirationMode (and/or) toggle between retention-days and
max-versions fields in bucket settings. 'and' = single S3 lifecycle policy
(both conditions must be met), 'or' = two independent rules (either triggers).
Wired into form schema, create/edit bucket modals, and mock store.

Task B: Move 'Show deleted versions' toggle from breadcrumb bar into the
file explorer table header, right of the search input. Switch is on the left
of the label text.

Task C: Add noncurrent version rows beneath each deleted object in TrashList.
Nested rows have left indentation with a CornerDownRight icon and lighter
background. Added DeletedObjectVersion interface and mock data to
protection-mocks.ts. Updated all user-facing terminology from 'files' to
'versions' across Trash, TrashList, TrashSelectionBar, DeletedFilesList,
DeletedFilesHeaderSelection, DeletedFilePreviewPane, and bucket page tabs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-07 11:03:16 +00:00
Claude 7ed23e9c43 Fix restore action placement, model versioning-suspended as a third state
- Reverted the timeline row's Restore action back to its icon-only
  text-variant button — the ask to make Restore a default-variant
  button was about the "Back to latest" action in the version
  preview banner, not the per-row action in the Versions tab
- The version preview banner's action is now labeled Restore, styled
  as a default-variant button, and actually restores the previewed
  version as current (instead of just clearing the preview)
- Versioning can no longer be turned fully "off" on a bucket that's
  ever had it enabled — it moves to a new suspended state instead,
  matching how bucket versioning actually behaves. Suspending only
  stops new noncurrent versions from being created; nothing already
  retained is deleted, so the destructive typed-confirmation flow for
  disabling versioning is gone
- Added `hasVersioningHistory` (enabled OR suspended) alongside the
  existing `isBucketVersioned` (enabled only) and repointed every
  surface that manages or warns about retained version/trash data to
  the former, since a suspended bucket can still be sitting on plenty
  of it
- Bucket list "Versioning" column, plan-downgrade admonition, and the
  Versions tab now all reflect the three states (Enabled / Suspended /
  never enabled)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-06 14:11:37 +00:00