Skip to content
Merged
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
3 changes: 2 additions & 1 deletion commitlint.config.js → .commitlintrc.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
export default {
extends: ['@commitlint/config-conventional'],
formatter: './commitlint.formatter.mjs',
rules: {
'subject-case': [2, 'always', 'sentence-case'],
},
Expand Down
5 changes: 3 additions & 2 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/
dist/
.*
dist/
coverage/
.nx/
35 changes: 35 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nx"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{
"sourceTag": "*",
"onlyDependOnLibsWithTags": ["*"]
}
]
}
]
}
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nx/typescript"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nx/javascript"],
"rules": {}
}
]
}
29 changes: 29 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Default owners for all repository paths.

# Replace @cutterbl with your GitHub username or team if needed.

- @cutterbl

# CI, automation, and repository policy

/.github/ @cutterbl
/scripts/ @cutterbl

# Applications

/apps/ @cutterbl

# Publishable/runtime packages

/packages/ @cutterbl

# Workspace and toolchain config

/nx.json @cutterbl
/pnpm-workspace.yaml @cutterbl
/pnpm-lock.yaml @cutterbl
/package.json @cutterbl
/tsconfig.json @cutterbl
/tsconfig.base.json @cutterbl
/eslint.config.mjs @cutterbl
/vitest.workspace.ts @cutterbl
55 changes: 55 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Project Guidelines

## Code Style

- Prefer TypeScript for shipped application and package code.
- Never use `any` in TypeScript. Use precise types, generics, discriminated unions, or `unknown` with narrowing.
- Use modern ECMAScript patterns already established in this repo: ESM modules, async/await, named-argument objects, and standard web platform APIs.
- Keep public APIs, state, and event payloads explicitly typed.
- Avoid reintroducing legacy patterns when a modern TypeScript or browser-native approach already exists in the codebase.

## CSS Guidelines

- Prefer CSS features that are baseline for 2024, including nested CSS and CSS layers.
- Use nested selectors to keep component styling scoped and maintainable.
- Prefer CSS Modules (`.module.css`) for component and interface styling.
- Avoid new global styles and inline styles except for one-off prototyping, CSS variables, or classes intentionally shared across components.
- When touching existing global styles, migrate incrementally toward CSS Modules instead of broad rewrites.
- Shared/global exceptions should be limited to:
- Design tokens and theme variables (for example, `:root` custom properties).
- Reset/normalize/base element styles.
- App-shell layout utilities used across many pages (for example, page container and spacing utility classes).
- Third-party integration hooks where global selectors are required by the library.

## Architecture

- This repository is an Nx monorepo managed with `pnpm` workspaces.
- Reusable libraries live under `packages/cdg-*`.
- Runnable apps live under `apps/`, including `demo`, `framework-demo`, and the Storybook apps.
- Keep package boundaries intact between `packages/cdg-*`, demo apps, and Storybook apps.
- Make focused changes; avoid broad refactors unless the task requires them.
- Keep implementation guides and example stories aligned with the code they document.
- Framework-agnostic implementation guidance must align with `apps/demo`.
- React implementation guidance must align with `apps/framework-demo`.

## Monorepo Workflow

- Use `pnpm` for installs and script execution; do not switch to `npm` or `yarn` for workspace tasks.
- Prefer `pnpm nx run <project>:<target>` for project-scoped work.
- Prefer `pnpm nx run-many -t <target>` when validating the same target across multiple projects.
- Use root `pnpm run ...` scripts when the repository already defines the canonical workflow, especially for CI-oriented commands and Storybook orchestration.
- Prefer the smallest relevant Nx target for validation before running broad workspace commands.
- When changing a package or app, check whether there is a matching `build`, `test`, `lint`, or `typecheck` target before using broader commands.

## Documentation

- Update Storybook documentation when public behavior, integration flow, or architecture guidance changes.
- Keep runtime contracts in `apps/storybook-hub/docs`.
- Keep framework-specific implementation guides next to their example stories in `apps/storybook-web/stories` and `apps/storybook-react/stories`.
- Storybook contribution guidance is pull-request oriented and should not describe release workflow.

## Build and Test

- Run the smallest relevant validation for the change when possible.
- For documentation changes, prefer `pnpm run ci:docs` and the relevant Storybook build.
- For broader changes, use the repo validation commands already documented in Storybook contribution guidance.
113 changes: 113 additions & 0 deletions .github/instructions/react-framework.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
description: 'Use when writing or refactoring React code, React example snippets, or framework demo code in apps/framework-demo and apps/storybook-react. Covers file naming, component folder structure, nesting rules, export patterns, and hook composition style.'
applyTo: 'apps/framework-demo/src/**/*.{ts,tsx}, apps/storybook-react/**/*.{ts,tsx,mdx}'
---

# React Framework Guidelines

- Match React example code and framework demo code to the user's React coding style and standards.
- When examples in `apps/storybook-react` describe implementation, keep them aligned with the actual React source in `apps/framework-demo`.
- Never use `any` in TypeScript/TSX. Prefer explicit interfaces, generics, discriminated unions, or `unknown` with narrowing.

## File Naming

- Component files: `Name.component.tsx`
- Hook files follow: `use<Purpose>.<hookType>.ts` or `use<Purpose>.<hookType>.tsx`
- Hook type suffixes should be explicit and consistent (for example: `.memo`, `.callback`, `.effect`, `.context`).
- Prefer these examples as canonical naming:
- `useCdgPlayer.context.ts`
- `useLoadTrack.callback.ts`
- `useOnFieldChange.effect.ts`
- `useFilePickerRowProps.memo.tsx`
- Function files: `name.function.ts`
- Constant files: `name.constant.ts`
- Type files: `name.type.ts`

## Folder Structure

- Prefer component folders over monolithic React files.
- Top-level reusable components belong under `src/app/components/ComponentName/`.
- Parent-only implementation details belong under `src/app/components/ParentName/components/SubComponentName/`.
- Each component folder should prefer:
- `ComponentName.component.tsx`
- `ComponentName.module.css`
- `index.ts`
- `hooks/` when needed
- `components/` for parent-owned subcomponents

## CSS Expectations

- Prefer baseline-2024 CSS features, including native nested CSS and CSS layers.
- For component and interface styling, use CSS Modules (`.module.css`) by default.
- Keep selectors locally scoped to the component module; avoid broad global selectors in React component work.
- Avoid inline styles except for one-off prototyping, CSS custom properties, or intentionally shared utility cases.
- If an area still uses existing shared/global styles, avoid broad rewrites and migrate incrementally when touching related components.
- In React work, only treat these as acceptable shared/global CSS exceptions:
- Theme/design tokens via root-level CSS custom properties.
- Reset/base element styles applied once at app entry.
- App-shell utility classes intentionally reused across routes/features.
- Required global selectors for third-party library integration points.

## Component Nesting Rules

- If a component is only used by one parent, nest it under that parent's `components/` folder.
- If a component is reused across screens, routes, or parents, keep it as a top-level component.
- Sections, cards, rows, headers, and item renderers are usually parent-owned subcomponents.

## Component Pattern

- Export prop types near the top of the component file.
- Define components as named functions, not arrow-function component constants, unless an existing local pattern requires otherwise.
- Default export the component at the bottom.

```tsx
export type ExampleProps = {
label: string;
};

function Example({ label }: ExampleProps) {
return <div>{label}</div>;
}

export default Example;
```

## Barrel Exports

- Use folder-level `index.ts` barrel exports.

```ts
export { default } from './Example.component';
export type { ExampleProps } from './Example.component';
```

## Hook Composition

- Keep render files focused on rendering.
- For non-trivial behavior, compose one top-level hook that assembles smaller hooks and returns resolved props.
- Prefer patterns like `useComponentNameProps.memo.ts(x)` that compose helper hooks following `use<Purpose>.<hookType>.ts(x)` naming.

## Component Hook Pattern

- For each non-trivial component, prefer a colocated hook under `hooks/` named `use<ComponentName>Props.memo.tsx`.
- Treat `*.component.tsx` files as render-first files:
- No embedded async workflows.
- No embedded event business logic.
- No inline parsing/validation branches beyond trivial JSX glue.
- Put callback/event behavior in hook files and expose stable handlers/derived values through resolved props.
- Keep parsing and command-style calls in the owning component hook, not in parent orchestration hooks.
- If behavior belongs to one UI control (file picker, transport, settings), colocate it in that control's hook.
- If behavior is shared by multiple components, move only the shared primitive/state to context or a shared hook; keep control-specific handlers local.

## Context Boundary

- Context should provide shared state, shared refs, and cross-cutting helpers only.
- Do not put control-owned handlers in context when those handlers are only consumed by one component.
- Promote logic into context only after a second concrete consumer appears, or when synchronization across multiple components is required.

## Practical Guidance For This Repo

- Avoid growing `apps/framework-demo/src/App.tsx` with more mixed rendering and orchestration logic.
- When the framework demo grows, prefer extracting React UI into component folders that follow the naming and nesting rules above.
- Keep React example snippets consistent with these conventions rather than using simplified ad hoc file shapes.
- In `apps/framework-demo/src/app/components/FrameworkDemo/components/*`, pair each subcomponent with a colocated `hooks/use<ComponentName>Props.memo.tsx` when it has non-trivial handlers.
112 changes: 112 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: CI

on:
pull_request:
branches: [main, master]
types: [opened, reopened, synchronize, ready_for_review]
workflow_dispatch:

permissions:
contents: read
pull-requests: write

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
lint-typecheck:
name: ci/lint-typecheck
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.33.0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run lint
run: pnpm nx run-many -t lint --projects=cdg-core,cdg-loader,cdg-player,cdg-controls,demo,framework-demo --outputStyle=stream

- name: Run typecheck
run: pnpm nx run-many -t typecheck --projects=cdg-core,cdg-loader,cdg-player,cdg-controls,demo,framework-demo --outputStyle=stream

unit-test:
name: ci/unit-test
runs-on: ubuntu-latest
needs: lint-typecheck
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.33.0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run unit tests
run: pnpm run ci:test:coverage

- name: Generate coverage summary
run: node scripts/ci/summarize-coverage.mjs --out coverage/summary.md

- name: Append coverage summary to job summary
run: cat coverage/summary.md >> "$GITHUB_STEP_SUMMARY"

- name: Upload coverage artifacts
uses: actions/upload-artifact@v4
with:
name: coverage-reports
path: coverage/**

- name: Post coverage summary comment
if: ${{ github.event_name == 'pull_request' }}
uses: marocchino/sticky-pull-request-comment@v2
with:
header: coverage-summary
path: coverage/summary.md

build:
name: ci/build
runs-on: ubuntu-latest
needs: unit-test
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.33.0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run builds
run: pnpm run ci:build
Loading
Loading