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
58 changes: 58 additions & 0 deletions .agents/skills/component-migration/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
name: component-migration
description: A skill to migrate and colocate component-related files (logic, types, and styles) into a unified directory structure using kebab-case filenames for the main entry file.
---

# Skill: Component File Colocation Migration

**Goal**: Standardize the `src/components` directory by moving logic, types, and styles into individual component folders using explicit kebab-case naming for the entry file.

## Target Structure
For a component named `my-element`:
```
src/components/group/my-element/
├── my-element.ts # Main component logic (Kebab-case, NOT index.ts)
├── my-element.types.ts # Interfaces and types
└── my-element.styles.ts # Lit styles (extracted)
```
This operates on `my-element.*.ts` files that **already exist** — relocate and rename them (creating the per-element directory via `git mv` to preserve history), but do **not** author new code or extract/refactor existing logic. The only edits to file *contents* are `import`/`export` paths and the JSDoc/TSDoc references that point at moved files.

## Implementation Guidelines

1. **Type & style migration — single-consumer rule only**:
- Colocate a definition from `src/types` or `src/styles` into a component's folder **only when exactly one `src/components/**` unit imports it** *and* nothing else does.
- When assessing "nothing else", **ignore `src/index.ts` and barrel `index.ts` files** — a side-effect import there does not count as a second consumer (but still update those paths afterward, see step 4).
- If a definition has **more than one consumer** — e.g. a component *and* a `src/partials/**` view, or two components — **leave it in its shared location**. Do NOT pull a shared definition into one component's folder; that just creates a new cross-unit import into component internals.
- Rename relocated files to the colocation convention: `<element>.types.ts` and `<element>.styles.ts`.
- Update the owning component to `import` the relocated files.

2. **Import resolution**:
- Use **relative** imports for colocated siblings (e.g. `./my-element.types`, `./my-element.styles`).
- Use **`@/` aliases** for every non-sibling import. (Siblings are relative; everything else stays aliased — never a `@/` path to a file in the same folder.)

3. **Documentation references (mandatory)**:
- When a file is moved or renamed, update **every** JSDoc/TSDoc reference that points at it so links still resolve: `{@link …}`, `{@linkcode …}`, `{@inheritDoc …}`, `@see`, `@module`, and any module-path mentions in prose comments — both in the moved file and in any file referencing it.
- A move is not complete until these references resolve to the new location/symbol.

4. **Clean up**:
- As directories are emptied, remove any now-unnecessary files and folders (including barrel `index.ts` files).
- Update `src/index.ts` (and any barrels) to point at the new paths.

## Spaghetti-`import` detection

While migrating, watch for files whose import graph is a smell. Flag a file when **either**:

- **Fan-out** — it imports from **≥3 distinct, unrelated internal (`@/…`) areas**, or
- **Fan-in** — it is imported by **≥4 distinct files** spanning **≥2 unrelated areas**.

Exclude from these counts: external packages (`lit`, `@material/web`, …), a component's own children and `@/data` content, `src/index.ts`, and barrel `index.ts` files.

When a file trips either condition:

- Record the finding in a repo-root **`TODOS.md`** (create it if absent) under an `h2` (`##`) section titled exactly:
`YYYY-MM-DD @ HH:mm:ss | Component Migration | Spaghetti `import`s`
(use a real timestamp; one section per audit run — append findings to the existing run's section).
- For each finding, name the file, give the fan-out/fan-in counts and areas, and add a **small decoupling suggestion** — **unless this migration already resolves it** (e.g. the coupling disappears once a single-consumer type is colocated), in which case just say so.
- Distinguish *acceptable* high coupling (e.g. a dependency-free shared leaf like a design-token module) from genuine spaghetti, and mark it as such rather than inventing work.

Record findings in `TODOS.md`, not as inline comments scattered across the source files.
34 changes: 34 additions & 0 deletions .agents/skills/ensure-code-documentation/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
name: ensure-code-documentation
description: A skill to ensure that code is properly documented, including docstrings, comments, TypeDoc compliant annotations, proper JSDoc/TSDoc comments, and, where possible, properly formatted and annotated code samples.
---

# Documentation Enforcement Skill

**Goal**: Ensure that all code is properly documented with clear and comprehensive docstrings, comments, and annotations. This includes adhering to TypeDoc standards, using proper JSDoc/TSDoc comments, and providing well-formatted code samples where applicable.

## Implementation Guidelines

1. **Check What's Already There**:
- Review existing code for documentation. Identify areas that lack docstrings, comments, or proper annotations.
- Correct any existing documentation that is incomplete, inaccurate, incorrectly formatted, or with invalid linking (external or cross code-base).
- Spelling and grammar should be correct, and the writing in the third-person inclusive voice (use `we` instead of `I` or `you`).

2. **Complete Incomplete Documentation**:
- For code that is documented *incompletely*, prioritize fleshing out existing documentation and targeting 100% file coverage before adding new documentation to undocumented files.
- Review all documentation as an entire file (for single-definition files) as a cohesive document; it should read as cohesively as the code is organized.
- Files that contain multiple definitions should read as if each `export`-ed definition is a separate file. For non `export`-ed definitions, documentation can be more brief and should link to the relevant `export`-ed definition(s) for more information.

3. **Add New Documentation**:
- For code that is completely undocumented, analyze the intent and use cases of the code and generate comprehensive documentation including:
- Proper `JSDoc`/`TSDoc` `@` tags
- Accurate type declarations
- Code samples and referential links where applicable

4. **Maintain Consistency**:
- Ensure that all documentation follows a consistent style and format across the codebase. Tone must be from a single voice (third-person inclusive) and the writing style should be clear, concise, and informative.
- Use consistent formatting for code samples, including proper indentation and syntax highlighting, adhering to the `2 space` indentation convention and patterns of the codebase.

5. **README.md Files**:
- Add an `index`-like `README.md` file to each `src/components` entry.
- Treat each `src/components/` as something that will be published as an **independent** package (with its own `package.json` and the like).
7 changes: 7 additions & 0 deletions .codex/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[mcp_servers."io.github.wonderwhy-er/desktop-commander"]
command = "npx"
args = [
"--registry",
"https://registry.npmjs.org",
"@wonderwhy-er/desktop-commander@0.2.43",
]
4 changes: 2 additions & 2 deletions .config/mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ monorepo_root = true
npm = "12.0.0"
"npm:corepack" = "0.35.0"
"npm:vite" = "8.1.5"
pnpm = "11.15.1"
pnpm = "11.16.0"
tombi = "1.1.5"
usage = "3.5.5"
usage = "3.5.6"

[deps]
pnpm = {
Expand Down
22 changes: 16 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@
"[lit]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[postcss]": {
"editor.defaultFormatter": "stylelint.vscode-stylelint"
},
"[python]": {
"diffEditor.ignoreTrimWhitespace": false,
"editor.defaultFormatter": "ms-python.python",
// "editor.formatOnType": true,
"editor.wordBasedSuggestions": "off"
},
"[toml]": {
"editor.defaultFormatter": "tamasfe.even-better-toml",
"editor.defaultFormatter": "tombi-toml.tombi",
"editor.detectIndentation": true
},
"[typescript]": {
Expand All @@ -48,20 +51,27 @@
]
}
},
"css.completion.completePropertyWithSemicolon": true,
"css.customData": [
"${workspaceFolder}/.vscode/custom-elements-manifest/vscode.css-custom-data.json"
],
"css.format.enable": true,
"css.format.spaceAroundSelectorSeparator": true,
"css.hover.documentation": true,
"css.hover.references": true,
"css.lint.importStatement": "ignore",
"css.lint.vendorPrefix": "ignore",
"css.styleSheets": [
"node_modules/material-symbols/material-symbols-outlined.css",
"node_modules/@fnc314/packages.design-tokens/dist/@fnc314.packages.design-tokens.css",
"**/${fileBasenameNoExtension}.styles.ts",
"${workspaceFolder}/packages/design-tokens/assets/css/*.css",
],
"css.format.spaceAroundSelectorSeparator": true,
"css.lint.importStatement": "ignore",
"css.lint.vendorPrefix": "ignore",
"css.trace.server": "verbose",
"css.validate": true,
"cssCustomProperties.files": [
"${workspaceFolder}/index.css",
"${workspaceFolder}/packages/design-tokens/**/*.css",
"${workspaceFolder}/sites/portfolio/index.css",
"${workspaceFolder}/packages/design-tokens/assets/css/*.css",
"${workspaceFolder}/node_modules/@fnc314/packages.design-tokens/@fnc314.packages.design-tokens.css",
],
"debug.javascript.defaultRuntimeExecutable": {
Expand Down
93 changes: 93 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.

## What this is

The personal portfolio website for Franco N. Colaizzi (fnc314.com), built as a single-page app of **Lit web components** styled with **Material Web (Material Design 3)**, written in TypeScript. It deploys as a static site to GitHub Pages, with a separate Firebase Functions backend in `functions/`.

## Toolchain: pnpm + mise

- Package manager is **pnpm 11.5** (not npm). Node 26, Firebase CLI, and other tools are pinned via **mise** (`mise.toml`). Run `mise install` before first use.
- Dependency *versions* are not in `package.json` — every dep reads `catalog:<group>` and the actual version is resolved from the catalogs in `pnpm-workspace.yaml`. To change a version, edit the catalog there, not `package.json`.
- `bin/*` are thin shell wrappers that `exec mise run <task>`. Task definitions live in `.mise/tasks/`.

## Build system: migrating Rollup → Vite

This branch (`feature/vite`) is mid-migration from Rollup to Vite. **Both build systems coexist** and write to separate output dirs:

- **Vite** (`vite.config.ts`) → `dist/vite/<development|production>/`. This is the source of truth for deploys — GitHub Pages publishes `dist/vite/production` (see `.github/workflows/gh-pages.yaml` → `mise run github:deploy` → `vite build -m production`).
- **Rollup** (`rollup.config.ts`) → `dist/rollup/<env>/`. Legacy path, still wired into the `build`/`deploy`/`serve` npm scripts.

When adding build behavior, prefer the Vite config unless explicitly working on the Rollup path.

## Common commands

```bash
mise run vite-prod-dev # vite build for BOTH dev + prod (local), into dist/vite/*
mise run github:deploy # production vite build (what CI deploys)
pnpm serve # @web/dev-server dev server (rollup-era)
pnpm build:website:watch # rollup watch build for local dev
pnpm lint # lit-analyzer + eslint over src/**/*.ts
pnpm lint:eslint:fix # eslint --fix
pnpm format # prettier --write over src
pnpm test # web-test-runner, dev + prod modes
pnpm test:watch # wtr --watch
pnpm build:tsc # type-check / emit .d.ts only (tsc)
pnpm cem-analyze # regenerate the Custom Elements Manifest
```

Run a single test by filtering web-test-runner, e.g. `pnpm wtr --group default --files 'src/**/foo.test.ts'`. (No test files exist yet; `pnpm test` runs the default wtr config.)

## Generated files — do not hand-edit

- **`README.md`** is auto-generated web-component-analyzer markdown (`pnpm dx:wca`). Don't edit it by hand.
- **`docs/`** holds generated output: TypeDoc HTML (`pnpm typedoc:gen`), CEM JSON, and WCA markdown/JSON. Regenerate via the `dx:*` scripts rather than editing.
- The **Custom Elements Manifest** drives IDE autocomplete and the docs. After changing a component's public API (props, slots, CSS custom props, events), regenerate with `pnpm cem-analyze`. Manifest tooling config lives in `.cem.yaml` and `.config/`.

## Architecture

### Services — singleton `EventTarget`s wired by constructor DI

`src/services/` holds the app's shared state, each an exported singleton instance of a class extending `EventTarget`. They form a dependency chain via constructor injection (`storage → configs → theme`) and communicate with components by dispatching **`CustomEvent`s that bubble + compose**, with event types augmented onto `GlobalEventHandlersEventMap`:

- `storage-service` — typed `localStorage` wrapper.
- `configs-service` — persists `AppConfigs` (color scheme, FAB layout); dispatches `app-configs.change`. Also does forward-migration of older stored config shapes on load.
- `theme-service` — resolves the active `ThemeConfig` + Material scheme name from configs + `prefers-color-scheme`.
- `router-service` — hash-based routing; dispatches `router.change` / `router.back`.

Components listen for these events rather than holding references to the services where possible.

### Components, partials, and routing

- `src/components/` — reusable Lit elements (`app-shell` is the root; `nav-component`, `fab-menu`, dialogs, `word-cloud`, etc.).
- `src/partials/` — route-level view containers (`info`, `work`, `code`, `blog`).
- Routing is **hash-based**, no server routes. The route set is `ROUTES` in `src/types/components/nav/routes.ts` (`info`/`work`/`code`/`blog`). `app-shell` swaps the active partial; `nav-component` syncs tabs to `window.location.hash`.
- `src/index.ts` is the single entry: it side-effect-imports every component and Material element, instantiates the service singletons, and on `DOMContentLoaded` adopts the Material + typescale stylesheets into `document.adoptedStyleSheets` and applies the resolved theme.

### Theming

Multiple named themes (`chicago`, `inter`, `red`, `sunset`) live in `src/theme/<name>/`, each exporting a `ThemeConfig` built from JSON color-scheme data, aggregated into `THEME_CONFIGS`. Colors map to **M3 design tokens** (`--md-sys-color-*`) applied through a shared `MaterialCSSStyleSheet`. The scheme is the cross-product of mode (dark/light/system) × contrast (normal/medium/high). Component-local styling uses scoped CSS custom properties (e.g. `--word-tag-*`) that default to Material tokens.

**Design Token Enforcement:**
- Use `@fnc314/design-tokens` for all design-related values (colors, spacing, typography).
- `stylelint` strictly enforces the use of tokens. If you encounter lint errors, replace hardcoded values with appropriate tokens.
- Run `mise run lint-tokens` to check compliance.

### Data

Site content is data-driven from `src/data/*.json` (`bio`, `work`, `code`, `blog`, `skills`, `education`, `connections`, `photo`). Edit JSON to change content; components render from it.

### Path aliases

Imports use `@/*` → `src/*` (also `@/components/*`, `@/services/*`, `@/theme/*`, `@/types/*`), defined in `tsconfig.json` and mirrored in the build configs. Prefer aliases over deep relative paths.

## Firebase (`functions/`)

`functions/` is an independent package (its own `package.json`, `tsconfig`, eslint config) for Firebase Cloud Functions. `firebase.json` also configures Firestore rules (`firebase/firestore/`) and App Hosting. Build/lint/deploy from within that dir (`pnpm --prefix functions run build|lint|deploy`); its lint+build run as Firebase predeploy hooks.

## Conventions

- Strict TypeScript throughout; Lit decorators (`useDefineForClassFields: false`, `experimentalDecorators`).
- Linting is **lit-analyzer + eslint** (lit, lit-a11y, wc, tsdoc plugins) for TS, **stylelint** for CSS. The `ts-lit-plugin` and `custom-elements-lsp` give in-editor template type-checking against the CEM.
- Document component public API with TSDoc/JSDoc tags (`@slot`, `@cssprop`, `@fires`, etc.) — these feed the manifest and generated docs.
4 changes: 2 additions & 2 deletions configs/@pwrs-cem/.config/mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
node = "26.5.0"
"npm:corepack" = "0.35.0"
"npm:vite" = "8.1.5"
pnpm = "11.15.1"
pnpm = "11.16.0"
tombi = "1.1.5"
usage = "3.5.5"
usage = "3.5.6"
npm = "12.0.0"
4 changes: 2 additions & 2 deletions configs/eslint/.config/mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
node = "26.5.0"
"npm:corepack" = "0.35.0"
"npm:vite" = "8.1.5"
pnpm = "11.15.1"
pnpm = "11.16.0"
tombi = "1.1.5"
usage = "3.5.5"
usage = "3.5.6"
npm = "12.0.0"
4 changes: 2 additions & 2 deletions configs/prettier/.config/mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
node = "26.5.0"
"npm:corepack" = "0.35.0"
"npm:vite" = "8.1.5"
pnpm = "11.15.1"
pnpm = "11.16.0"
tombi = "1.1.5"
usage = "3.5.5"
usage = "3.5.6"
npm = "12.0.0"
4 changes: 2 additions & 2 deletions configs/typedoc/.config/mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
node = "26.5.0"
"npm:corepack" = "0.35.0"
"npm:vite" = "8.1.5"
pnpm = "11.15.1"
pnpm = "11.16.0"
tombi = "1.1.5"
usage = "3.5.5"
usage = "3.5.6"
npm = "12.0.0"
4 changes: 2 additions & 2 deletions configs/typescript/.config/mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
node = "26.5.0"
"npm:corepack" = "0.35.0"
"npm:vite" = "8.1.5"
pnpm = "11.15.1"
pnpm = "11.16.0"
tombi = "1.1.5"
usage = "3.5.5"
usage = "3.5.6"
npm = "12.0.0"
4 changes: 2 additions & 2 deletions firebase/functions/node/.config/mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
firebase = "15.24.0"
node = "26.5.0"
tombi = "1.1.5"
usage = "3.5.5"
pnpm = "11.15.1"
usage = "3.5.6"
pnpm = "11.16.0"
"npm:vite" = "8.1.5"
"npm:corepack" = "0.35.0"
npm = "12.0.0"
6 changes: 3 additions & 3 deletions firebase/functions/node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fnc314/functions.node",
"version": "2.1.0",
"version": "3.0.0",
"private": true,
"description": "Firebase Cloud Functions for www.fnc314.com",
"author": "Franco N. Colaizzi",
Expand Down Expand Up @@ -32,14 +32,14 @@
"overrides": {
"uuid": "catalog:overrides"
},
"packageManager": "pnpm@11.15.1+sha512.81350b07e53c9538a02f1f2303b4290fa2d7be04e56e2a970c4cc4b417dc761de196edabd49d55c7dc9580db81007c44143e4e3d7e462b3000d23c255122d065",
"packageManager": "pnpm@11.16.0+sha512.b767e9a98fc87aec0f42daefcd0e84941c2cdb45d73c4e1e68860fb2bdfdbe032ab592105479e5e05c237f740c8a5ffdbbb52385797ddc2296745a1bbb883e8d",
"engines": {
"node": "26.5.0"
},
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": "11.15.1+sha512.81350b07e53c9538a02f1f2303b4290fa2d7be04e56e2a970c4cc4b417dc761de196edabd49d55c7dc9580db81007c44143e4e3d7e462b3000d23c255122d065",
"version": "11.16.0+sha512.b767e9a98fc87aec0f42daefcd0e84941c2cdb45d73c4e1e68860fb2bdfdbe032ab592105479e5e05c237f740c8a5ffdbbb52385797ddc2296745a1bbb883e8d",
"onFail": "warn"
}
},
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"short_name": "fnc314.com",
"author": "Franco N. Colaizzi",
"description": "Personal Website of Franco N. Colaizzi - fnc314",
"version": "2.1.0",
"version": "3.0.0",
"name": "fnc314.com",
"manifest_version": "3",
"display": "standalone",
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fnc314/fnc314.github.io",
"version": "2.1.0",
"version": "3.0.0",
"description": "Website for Franco N. Colaizzi - fnc314.com",
"homepage": "https://fnc314.com",
"license": "ISC",
Expand All @@ -14,7 +14,7 @@
"types": "./dist/out",
"main": "./dist/vite/production/assets/index.js",
"web-types": "./.idea/web-types.json",
"packageManager": "pnpm@11.15.1+sha512.81350b07e53c9538a02f1f2303b4290fa2d7be04e56e2a970c4cc4b417dc761de196edabd49d55c7dc9580db81007c44143e4e3d7e462b3000d23c255122d065",
"packageManager": "pnpm@11.16.0+sha512.b767e9a98fc87aec0f42daefcd0e84941c2cdb45d73c4e1e68860fb2bdfdbe032ab592105479e5e05c237f740c8a5ffdbbb52385797ddc2296745a1bbb883e8d",
"keywords": [
"website",
"homepage",
Expand Down Expand Up @@ -206,7 +206,7 @@
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": "11.15.1+sha512.81350b07e53c9538a02f1f2303b4290fa2d7be04e56e2a970c4cc4b417dc761de196edabd49d55c7dc9580db81007c44143e4e3d7e462b3000d23c255122d065",
"version": "11.16.0+sha512.b767e9a98fc87aec0f42daefcd0e84941c2cdb45d73c4e1e68860fb2bdfdbe032ab592105479e5e05c237f740c8a5ffdbbb52385797ddc2296745a1bbb883e8d",
"onFail": "warn"
}
}
Expand Down
Loading
Loading