Commit Graph
2743 Commits
Author SHA1 Message Date
Vance IngallsandClaude Sonnet 5 475f764e5a test(studio): assert stopPropagation prevents onSelect on remove click
Review proved the existing test didn't catch a broken stopPropagation
by temporarily removing it and confirming the suite still passed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:51:54 -07:00
Vance IngallsandClaude Sonnet 5 b0093835fd feat(studio): add FlatTextLayerList for multi-field text
Originated layout, no design mock exists — flag for design review.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:51:54 -07:00
Vance IngallsandClaude Sonnet 5 e6cf075363 test(studio): rename unpin test to match what it actually verifies
Review of the pin-aware group list refactor flagged the test name
claiming the group "closes" on unpin — it doesn't assert that, and
structurally the group re-opens (togglePin never touches openGroupId).
Retitled to describe only the return-to-stack behavior actually tested.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:51:54 -07:00
Vance Ingalls b2384fad67 feat(studio): render pinned groups first with a persisted, per-element-kind pin zone 2026-07-14 15:51:54 -07:00
Vance Ingalls 31849d62e2 feat(studio): add PinnedGroupRow always-open pinned-group primitive 2026-07-14 15:51:54 -07:00
Vance Ingalls 4bff97090b feat(studio): add usePersistedPinnedGroups hook
Reads/writes the per-element-kind pinned-groups map added to
studioUiPreferences in the prior task, read-modify-writing the whole
map since writeStudioUiPreferences only shallow-merges top-level keys.
2026-07-14 15:51:53 -07:00
Vance IngallsandClaude Sonnet 5 0d7a60a525 feat(studio): persist per-element-type pinned groups in studioUiPreferences
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:51:53 -07:00
Vance IngallsandClaude Sonnet 5 384fe5934a feat(studio): wire the flat Grade group into the one-open accordion
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:51:53 -07:00
Vance IngallsandClaude Sonnet 5 cc07c84a31 feat(studio): add HDR warning banner and Apply-to-scope row, completing FlatColorGradingSection
Also adds a not-busy click assertion for the Apply button so
onApplyToScope is exercised end-to-end, not just its disabled state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:51:52 -07:00
Vance Ingalls 80dc7c49f6 feat(studio): add Blur and Pixelate effect sliders 2026-07-14 15:51:52 -07:00
Vance IngallsandClaude Sonnet 5 93f3bf5945 feat(studio): add Vignette and Grain rows with tuned-settings sub-panel
Also hoists the pointerdown-drag/reset-click test scaffold shared by the
new Roundness tests and the existing Contrast/Exposure tests into helpers
(findRowByText/dragSliderTrack/clickSliderReset), and exempts pre-existing,
branch-inherited fallow findings unrelated to this task (TextFieldEditor
complexity from earlier Text-inspector commits; test-scaffold duplication
across four flat-inspector-series test files from Plans 2-4) via
.fallowrc.jsonc, per this repo's established convention for line-shift/
inherited findings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:51:51 -07:00
Vance IngallsandClaude Sonnet 5 82cb7b8979 refactor(studio): collapse dead-branch scale ternary in Adjust sliders
Review of Task 6 flagged the exposure/other-key scale ternary as always
resolving to 100 on both arms.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:50:52 -07:00
Vance Ingalls 96d90feb50 feat(studio): add the 10 Adjust sliders to FlatColorGradingSection 2026-07-14 15:50:51 -07:00
Vance Ingalls 9b6bed2824 feat(studio): add FlatColorGradingSection Preset and Custom LUT rows 2026-07-14 15:50:51 -07:00
Vance Ingalls 160193d833 feat(studio): add FlatColorGradingAccessory (compare, status dot, reset) 2026-07-14 15:50:50 -07:00
Vance Ingalls 18e4aef7dc feat(studio): widen FlatSelectRow options to support label!=value entries 2026-07-14 15:50:49 -07:00
Vance Ingalls afafeccf4c feat(studio): add centerTick and reset-slot to FlatSlider for bipolar grade rows 2026-07-14 15:50:49 -07:00
Vance Ingalls 22b3f07c56 refactor(studio): extract useColorGradingController from ColorGradingSection 2026-07-14 15:50:48 -07:00
Vance Ingalls 5a3abdef36 feat(studio): wire the flat Media group into the one-open accordion 2026-07-14 15:50:45 -07:00
Vance Ingalls ea463cdf8f feat(studio): add fit/position rows to FlatMediaSection 2026-07-14 15:50:45 -07:00
Vance Ingalls 79fdff8701 feat(studio): add loop/muted/has-audio-track toggles to FlatMediaSection 2026-07-14 15:50:45 -07:00
Vance Ingalls ab263f25b4 feat(studio): add volume/rate/media-start sliders to FlatMediaSection 2026-07-14 15:50:45 -07:00
Vance Ingalls 138d45faac feat(studio): add cutout sub-block to FlatMediaSection 2026-07-14 15:50:44 -07:00
Vance Ingalls 0e0cb4846d feat(studio): add FlatMediaSection scaffold + source/copy row 2026-07-14 15:50:44 -07:00
Vance Ingalls 14d8de7492 feat(studio): add FlatToggle primitive 2026-07-14 15:50:44 -07:00
Vance IngallsandClaude Sonnet 5 4b592832ab fix(studio): recompute keyframe currentPct from the corrected timing basis in the flat Layout group
Follow-up to 684ec4e87: that fix corrected the seek target for Layout's
keyframe gutter via deriveElementTiming, but currentPct — which drives
KeyframeNavigation's diamond active/inactive state and prev/next arrow
targeting — still used PropertyPanel's naive elStart=0/elDuration=1
basis. For an element with animations but no explicit data-duration,
seeking to a keyframe's real absolute time no longer lit that
keyframe's diamond as active, and the prev/next arrows targeted the
wrong keyframes.

Thread currentTime into PropertyPanelFlat (swapping the now-redundant
currentPct prop 1-for-1, so PropertyPanel.tsx's line count is
unchanged) and recompute currentPct there from the same
deriveElementTiming basis already used for the seek fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:50:43 -07:00
Vance IngallsandClaude Sonnet 5 b155ed46b6 fix(studio): reconcile keyframe-seek timing basis between flat Layout and Motion groups
Whole-plan coherence review (Plan 3a Layout + Plan 3b Motion) found that
Layout's keyframe gutter and Motion's Timing row independently derived an
element's start/duration and disagreed whenever an element had animations
but no explicit data-duration: Motion correctly inferred the range from the
element's GSAP tweens, while Layout's keyframe gutter fell back to a naive
`duration ?? 1`, so clicking a keyframe percentage in Layout could seek to a
different absolute time than what Motion's Timing row displayed.

Extract deriveElementTiming (propertyPanelFlatTimingDerivation.ts) as the
single shared basis both paths now consume: FlatTimingRow (Motion) and
PropertyPanelFlat's own elStart/elDuration (Layout's keyframe gutter and 3D
Transform block). PropertyPanelFlat now recomputes this basis itself from
its own element/gsapAnimations props instead of trusting the parent's naive
value, so PropertyPanel.tsx (and its legacy non-flat panel) is untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:50:43 -07:00
Vance Ingalls 47591f07a6 feat(studio): wire the flat Motion group into the one-open accordion 2026-07-14 15:50:42 -07:00
Vance Ingalls 9676382bb5 feat(studio): compose FlatMotionSection from Timing and the flat effect-card list 2026-07-14 15:50:42 -07:00
Vance Ingalls a3d092ee0f feat(studio): add flat collapsed-header variant to AnimationCard 2026-07-14 15:50:42 -07:00
Vance Ingalls fb1373905f feat(studio): add FlatTimingRow for the flat Motion group 2026-07-14 15:50:42 -07:00
Vance IngallsandClaude Sonnet 5 8602e1a8c9 feat(studio): wire the flat Layout group into the one-open accordion
Thread the Layout-group values through PropertyPanel -> PropertyPanelFlat
and add the third FlatGroup to the one-open/pin accordion (unconditional,
matching legacy Layout). Default-open Layout when neither Text nor Style
applies.

Fix the Flex double-render: the legacy StyleSections still renders its own
Flex Section, and the new flat Layout group renders its own LayoutFlexBlock.
Add an additive optional hideFlex prop to StyleSections and pass it on the
flat path so Flex renders exactly once (from the flat Layout group). Non-flat
callers omit it and are unchanged.

Extract the shared onLivePreviewProps closure into gsapLivePreview.ts (it was
duplicated inline in the legacy path) so PropertyPanel.tsx stays within the
600-LOC studio gate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:50:41 -07:00
Vance Ingalls 0369865ddb feat(studio): compose FlatLayoutSection from geometry, z-index, flex, and 3D blocks 2026-07-14 15:50:41 -07:00
Vance Ingalls 5edaf6db7a feat(studio): nest the existing 3D transform sub-view in a flat wrapper 2026-07-14 15:50:41 -07:00
Vance IngallsandClaude Sonnet 5 3b958aeb77 feat(studio): add flat Layout Z-index row and Flex sub-block
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:50:41 -07:00
Vance Ingalls 588895f317 feat(studio): add flat Layout geometry rows (X/Y/W/H/Angle) with keyframe gutters 2026-07-14 15:50:40 -07:00
Vance Ingalls fea582f2b4 feat(studio): wire the flat Style group into the one-open accordion 2026-07-14 15:50:39 -07:00
Vance Ingalls ff7a9c593a feat(studio): add flat Opacity slider, completing the Style group 2026-07-14 15:50:39 -07:00
Vance Ingalls 11d1cc7499 feat(studio): add flat Overflow and Mask rows to the Style group 2026-07-14 15:50:39 -07:00
Vance Ingalls cb8962652b feat(studio): add flat Layer blur and Backdrop sliders to the Style group 2026-07-14 15:50:39 -07:00
Vance Ingalls 9888968219 feat(studio): add flat Shadow and Blend rows to the Style group 2026-07-14 15:50:38 -07:00
Vance Ingalls b86717d758 feat(studio): add flat Stroke and Radius rows to the Style group 2026-07-14 15:50:38 -07:00
Vance Ingalls 2d9989db3b feat(studio): add FlatStyleSection with the flat Fill sub-block 2026-07-14 15:50:38 -07:00
Vance Ingalls 760aa09a43 feat(studio): add stroke-summary format/parse helpers for the flat Style group 2026-07-14 15:50:38 -07:00
Vance Ingalls f71fcf4da0 feat(studio): add FlatSelectRow primitive for the flat inspector 2026-07-14 15:50:37 -07:00
Vance Ingalls 9854c6de71 feat(studio): add FlatSlider primitive for the flat inspector 2026-07-14 15:50:37 -07:00
Vance Ingalls 8224cb2060 feat(studio): flat inspector foundation + Text group (#2120)
## What

First PR in a 6-PR stack migrating Studio's right-panel property inspector from an always-expanded stacked-sections layout to a "flat" one-open-at-a-time accordion. This PR lays the foundation: the `STUDIO_FLAT_INSPECTOR_ENABLED` feature flag, the accordion primitives (`FlatRow`, `FlatSegmentedRow`, `FlatGroup`, `PinnedZoneDivider`), the flat identity header/footer, and the first migrated group — Text.

Stack: #2120 (this) → #2121 (Style) → #2122 (Layout+Motion) → #2123 (Media) → #2124 (Grade) → #2125 (Pinning + multi-field Text).

## Why

The legacy inspector renders every applicable section expanded at once, which gets unwieldy as an element accumulates properties across style/layout/motion/media/grade. The flat redesign shows one section at a time (plus pinned sections), matching a design handoff mock.

## How

- `FlatGroup` owns the one-open accordion state (`openGroupId`/`onToggleOpen`) and pin affordance (`onTogglePin`), styled per the design mock.
- `FlatTextSection` is the first migrated group and the reference implementation every later group's task followed for the `isOpen`/`onToggleOpen`/`onTogglePin`/`summary` wiring pattern.
- Includes a same-PR bugfix (found via live browser testing, not caught by any automated test): the Text `FlatGroup` was rendering unconditionally regardless of element type (empty for non-text elements), and the multi-field fallback doubled the "Text" heading. Fixed by gating on `isTextEditableSelection` and adding a `hideOwnHeading` prop to the legacy `TextSection` fallback.
- Entirely gated behind `STUDIO_FLAT_INSPECTOR_ENABLED` (default off) — the legacy panel is untouched and remains the default for all users.

## Test plan

- Every primitive and the Text group have dedicated Vitest suites using real DOM events (click/pointerdown) with exact assertions, not shallow snapshots.
- Manually verified in Studio via live browser testing against the design mock (this is what caught the bugfix above).
- Full monorepo test suite green; `oxlint`/`oxfmt` clean; this repo's `fallow` complexity/duplication gate passes.
- [x] Unit tests added/updated
- [x] Manual testing performed
- [ ] Documentation updated (not applicable — internal Studio UI behind an off-by-default flag)
2026-07-14 15:47:14 -07:00
Vance Ingalls c41f2e6313 feat(cli): give stale-pinned projects a path to the latest CLI (#2304)
## What

Already-scaffolded HyperFrames projects pin an exact `hyperframes@X.Y.Z` in `package.json` scripts (`init.ts`), frozen at scaffold time — and the update-available notice is suppressed on non-TTY shells, exactly how agents invoke the CLI. So a large tail of projects sits on months-old versions, invisible and stuck, never seeing later render/router fixes.

The pin itself is deliberate (a video project should re-render identically across CLI versions), so this PR keeps it and instead gives projects a path off it:

1. **`rewriteProjectPinnedScripts` / `readPinnedHyperframesVersions`** (`packages/cli/src/utils/projectPin.ts`) — pure helpers that rewrite/read `hyperframes@<version>` pins in a `package.json` scripts object.
2. **`hyperframes upgrade --project [dir]`** — bumps a project's pinned scripts to npm-latest in one command (`--check` reports the delta without writing, `--json` for `{ changed, from, to, path }`).
3. **`printStalePinNotice`** — a throttled (once/24h), non-TTY-visible notice (unlike the existing update notice, which non-TTY shells suppress) that fires when the *current* project's pin is stale, pointing at `upgrade --project`.
4. **Skill instruction** (`skills/hyperframes-cli/SKILL.md` + reference) — tells agents to check for and bump a stale project pin via the **unpinned** `npx hyperframes@latest upgrade --project`.
5. **Scaffold templates** (`CLAUDE.md`/`AGENTS.md`) — new projects get the same guidance baked in from day one.

## Why

Only the global skill (piece 4) invoking the unpinned `npx hyperframes@latest upgrade --project` (piece 2) reaches projects that are *already* frozen on an old pin — a project pinned to an old CLI version never runs the new notice code (piece 3) or sees the new template text (piece 5). Those two are forward-only: they stop the bleed on projects scaffolded from here on, but the skill instruction is the only lever that reaches the existing backlog.

## How

`isSafeVersion` was extracted out of `updateCheck.ts` into its own `safeVersion.ts` module — `projectPin.ts` needs it and `updateCheck.ts` needs `projectPin.ts`'s `readPinnedHyperframesVersions`, so keeping `isSafeVersion` in `updateCheck.ts` created a circular import between the two files.

## Test plan

- [x] Unit tests added/updated (`projectPin.test.ts`, `upgrade.project.test.ts`, `updateCheck.stalepin.test.ts`) — TDD, all passing
- [x] Full `packages/cli` suite green (132 files / 1651 tests), `tsc --noEmit` clean, `bun run build` succeeds
- [x] Manual smoke test: `upgrade --project --check --json` reports the delta without writing; `upgrade --project` rewrites the pinned scripts in place
- [ ] Documentation updated — skill + scaffold templates updated in this PR; `CLAUDE.md`/`AGENTS.md` template parity verified with `diff -q`

Deferred (left for a separate decision, not in this PR): `npm deprecate hyperframes@"<0.7.53"` — reaches frozen projects with no skill loaded, but is a live, hard-to-reverse action against published packages that needs an explicit human call on cutoff version + message.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-14 15:44:50 -07:00
Vance Ingalls e85a50662b docs(templates): note how to bump the project's pinned CLI 2026-07-14 15:29:56 -07:00
Vance Ingalls 499099f1cb docs(skills): instruct agents to bump stale project CLI pins 2026-07-14 15:29:54 -07:00