Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .codepress/dev-server/Dockerfile.editor
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by CodePress bootstrap-dev-server.
# Dev-mode image for the "editor" frontend (Live Dev Server).
# Edits are preserved, but running /codepress-bootstrap-dev-server again may overwrite them.
FROM public.ecr.aws/docker/library/node:22-bookworm

# System packages the dev runtime needs. `procps` only: every native-ish dependency
# here (onnxruntime-web, @huggingface/transformers, mediabunny) ships WebAssembly or
# prebuilt binaries, so nothing compiles against a system library at install time.
RUN apt-get update \
&& apt-get install -y --no-install-recommends procps \
&& rm -rf /var/lib/apt/lists/*

# npm 11.8.0 is the repo's declared packageManager; activate that exact version so a
# cold start never pauses to fetch a package manager.
RUN corepack enable && corepack prepare npm@11.8.0 --activate
WORKDIR /app

# THIN image: no `node_modules`, no source COPY. The dev-server runtime provides
# dependencies at run time (squashfs hydration, or `npm install` in this image
# against the bind-mounted checkout). Only the toolchain lives here.

# HMR + bind config — values come from the runtime, never hardcoded. vite.config.ts
# reads CODEPRESS_HMR_PROTOCOL / CODEPRESS_HMR_CLIENT_PORT into `server.hmr`.
ENV HOSTNAME=0.0.0.0 \
CODEPRESS_BIND_HOST=0.0.0.0 \
PORT=5173 \
CODEPRESS_HMR_CLIENT_PORT=443 \
CODEPRESS_HMR_PROTOCOL=wss

EXPOSE 5173

# Single root app, so only the root bin dir. Populated at run time on the
# bind-mounted checkout; prepended so the corepack shims survive.
ENV PATH="/app/node_modules/.bin:${PATH}"

# Dev command (HMR on). `vp` is vite-plus, the repo's Vite toolchain wrapper; `vp dev`
# is its alias for the dev server and takes Vite's own --host/--port.
CMD ["sh", "-c", "npm exec --no -- vp dev --host \"${CODEPRESS_BIND_HOST:-0.0.0.0}\" --port \"$PORT\""]
30 changes: 30 additions & 0 deletions .codepress/dev-server/recipe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"schema_version": 1,
"bootstrapped_at": "2026-09-04T16:32:20Z",
"discovery_branch": "codepress/stevekriz/setup-storybook-component-catalog-91f1ea3a",
"frontends": [
{
"label": "editor",
"working_dir": "",
"description": "FreeCut — the browser-based multi-track video editor (the whole app; there is no separate site or admin).",
"dockerfile_path": ".codepress/dev-server/Dockerfile.editor",
"dev_command": "npm exec --no -- vp dev --host \"${CODEPRESS_BIND_HOST:-0.0.0.0}\" --port \"$PORT\"",
"prebuild_command": "",
"prebuild_inputs": [],
"framework": "vite",
"package_manager": "npm",
"install_command": "npm install",
"dependency_manifests": ["package-lock.json"],
"dev_port": 5173,
"system_packages": ["procps"],
"size": "large",
"companions": [
{
"label": "storybook",
"dev_command": "npm exec --no -- storybook dev -p \"$PORT\" -h \"${CODEPRESS_BIND_HOST:-0.0.0.0}\" --ci --no-open",
"dev_port": 6006
}
]
}
]
}
7 changes: 6 additions & 1 deletion .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
"ignorePatterns": [
// Static reachability cannot see URL-loaded assets such as /sw.js
// and /moss-tts/moss_tts.worker.js.
"public/**"
"public/**",
// Storybook loads *.stories.tsx through its own glob, so every story export
// is unreachable from the app entries by construction. Ignoring them keeps
// the dead-code ratchet meaningful instead of demanding an allowlist entry
// per story — and keeps stories from counting as "usage" of a component.
"**/*.stories.tsx"
],
"duplicates": {
// Hide pair-only clones; focus on widespread copy-paste
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,6 @@ ds-bundle/

artifacts/
.playwright-cli/

# Storybook production build output
storybook-static
51 changes: 51 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { StorybookConfig } from '@storybook/react-vite'

/**
* Hosts allowed to reach the Storybook dev server and its manager websocket.
*
* CodePress serves the preview from a public proxied origin, and Storybook validates
* the websocket Origin independently of Vite. The two static suffixes cover the
* production and staging preview domains; `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` is
* injected at runtime and carries the MicroVM endpoint host, which differs per
* environment. All three must be present — a non-empty allowlist disables the
* allow-all fallback, so listing one environment alone would 403 the other.
*/
const previewAllowedHosts = ['.preview.codepress.dev', '.preview-staging.codepress.dev']
.concat((process.env.__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS ?? '').split(','))
.map((host) => host.trim())
.filter(Boolean)

const config: StorybookConfig = {
stories: ['../src/**/*.stories.tsx'],
framework: {
name: '@storybook/react-vite',
options: {
builder: {
// Not the app config — see the header comment in vite.storybook.config.ts.
viteConfigPath: 'vite.storybook.config.ts',
},
},
},
core: {
disableTelemetry: true,
disableWhatsNewNotifications: true,
allowedHosts: previewAllowedHosts,
},
viteFinal: (viteConfig) => ({
...viteConfig,
server: {
...viteConfig.server,
// The preview origin terminates HTTPS on 443, so the HMR client must dial the
// proxy rather than the container's own dev port. Both values are injected by
// the CodePress runtime; locally the defaults keep plain same-port websockets.
hmr: {
protocol: (process.env.CODEPRESS_HMR_PROTOCOL ?? 'ws') as 'ws' | 'wss',
...(process.env.CODEPRESS_HMR_CLIENT_PORT
? { clientPort: Number.parseInt(process.env.CODEPRESS_HMR_CLIENT_PORT, 10) }
: {}),
},
},
}),
}

export default config
6 changes: 6 additions & 0 deletions .storybook/manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// The catalog is a dark-room tool like the editor it documents; the light manager
// chrome against near-black story canvases is a jarring pair.
import { addons } from 'storybook/manager-api'
import { themes } from 'storybook/theming'

addons.setConfig({ theme: themes.dark })
9 changes: 9 additions & 0 deletions .storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- Mirrors index.html: the editor's type stack is IBM Plex Sans / IBM Plex Mono,
so the catalog has to load the same faces or every story renders in fallback. -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap"
rel="stylesheet"
crossorigin="anonymous"
/>
41 changes: 41 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Decorator, Preview } from '@storybook/react-vite'
import { TooltipProvider } from '@/components/ui/tooltip'
// Side-effect imports: the app's global stylesheet (Tailwind theme, OKLCH tokens,
// timeline theme extension, animation utilities) and the i18next instance that
// translated components — Dialog, FloatingPanel, LanguageSwitcher — read from.
import '@/index.css'
import '@/i18n'

/**
* FreeCut is dark-only by design, so every story renders on the app background with
* the shared tooltip provider already mounted — the same chrome a component sees
* inside the editor.
*/
const withEditorSurface: Decorator = (Story) => (
<TooltipProvider delayDuration={200} skipDelayDuration={300}>
<div className="min-h-screen bg-background p-6 text-foreground">
<Story />
</div>
</TooltipProvider>
)

const preview: Preview = {
decorators: [withEditorSurface],
parameters: {
// The decorator owns padding, and panels/overlays need the full frame.
layout: 'fullscreen',
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
options: {
storySort: {
order: ['Foundations', 'UI', 'Property Controls', 'Editor Surfaces', 'Brand'],
},
},
},
}

export default preview
Loading
Loading