Skip to content

feat: brand-driven architecture, ESLint Vue + naming, overlay card fixes, Tier 1 headers#3

Merged
Kyonax merged 4 commits intodevfrom
feat-fixes-and-refinement-v3
Apr 21, 2026
Merged

feat: brand-driven architecture, ESLint Vue + naming, overlay card fixes, Tier 1 headers#3
Kyonax merged 4 commits intodevfrom
feat-fixes-and-refinement-v3

Conversation

@Kyonax
Copy link
Copy Markdown
Owner

@Kyonax Kyonax commented Apr 17, 2026

Checklist (check if it applies)

  • Contains testing instructions
  • Requires environment / credential changes
  • Requires special deployment steps
  • Has unit / integration tests
  • Touches licensing, security, or CI
  • License headers on new files (LICENSING.org)
  • Attribution preserved on modified files
  • Lint passes (npm run lint)
  • Security rules followed (SECURITY.org)
  • No credentials committed
  • All GitHub Checks have passed (no Pre-Check Failed label applied by CI)
  • CHANGELOG.org updated (release PRs only)
  • Version bumped (release PRs only)

What does this PR do?

Refactors RECKIT on two passes that share the same goal: make the project trivially extensible. Pass one turns each @brand/ folder at the project root into the single source of truth for that brand and routes everything through import.meta.glob auto-discovery. Pass two splits the component layer by kindsections/ + elements/ + modals/ under views/, ui/ + hud/ under shared/components/ — pairs every kind-folder with its own Vite alias (11 total), and extracts five real sections out of the landing page so home.vue becomes a thin composer. Adds ESLint Vue SFC support with @typescript-eslint/naming-convention, fixes the overlay card grid and truncation, and ships Tier 1 figlet file headers on all root files alongside CONTRIBUTING.org and an expanded SECURITY.org.

A second refinement pass on the same branch converges the naming discipline (Rule G — filename describes purpose, never repeats the alias kind), audits every view component for Rule E duplication and extracts three new shared/components/ui/ primitives (<UiDataPoint>, <UiChip>, <UiBadge>) that replace ~12 inline duplicates across the stats / card / detail consumers, splits shared/widgets/ by kind (widgets/hud/ + widgets/ui/) to mirror the components layout, restructures each brand folder so web sources live under <brand>/sources/<type>/<id>.vue (eliminating the vocabulary clash where @brand/hud/ meant web sources and @hud/ meant shared HUD primitives), extends <UiIcon> to auto-scan <brand>/assets/svg/*.svg so any brand can drop its own SVGs, and fixes a silently-broken brand theming chain by moving the palette to a true SCSS single source of truth (@<brand>/styles/_theme.scss auto-loaded by src/main.js, brand.js no longer carries colors, App.vue only applies the .brand-<handle> class — the CSS cascade does the rest).

As a content creator maintaining RECKIT, I want each brand self-contained in its own @brand/ folder and each component filed by kind (section, element, modal, ui, hud, widget), so that adding a new brand requires zero app code changes and every new component has an unambiguous destination. I also want the brand palette to live in one place — an SCSS file — so that customizing a brand is never split between two out-of-sync source files.

Design / Reference: Scope tracked in CHANGELOG.org. ASCII logo at .github/assets/logo.txt. Component architecture rules codified in CONTRIBUTING.org.

Implementation

[NEW] new file · [MOD] modified file · [DEL] removed · [MOV] renamed or relocated

Brand-driven architecture (@kyonax_on_tech/, src/shared/)

  • [NEW] @kyonax_on_tech/brand.js — brand metadata: handle, name, identity, links. Part B: colors field removed — the palette lives exclusively in styles/_theme.scss (see Brand theming below).
  • [NEW] @kyonax_on_tech/sources.js — web source card data (extracted from overlays.js, added type field)
  • [NEW] @kyonax_on_tech/styles/_theme.scss — brand CSS custom property overrides under .brand-kyonax-on-tech. Part B: now the single source of truth — auto-loaded at build by src/main.js.
  • [NEW] src/shared/brand-loader.js — auto-discovery engine using import.meta.glob. Part B: globs updated from /@*/hud/*.vue (etc.) to /@*/sources/hud/*.vue (etc.); resolveComponent() key pattern now /${brand}/sources/${type}/${id}.vue.
  • [MOV] src/brands/kyonax-on-tech/cam-log.vue to @kyonax_on_tech/hud/cam-log.vue (Part A). Then Part B: [MOV] @kyonax_on_tech/hud/cam-log.vue to @kyonax_on_tech/sources/hud/cam-log.vue — identity still pulled from getBrand(); imports now @hud/frame.vue, @hud/timer.vue, @widgets/hud/audio-meter.vue, @widgets/ui/live-readout.vue, @composables/*; template tags <HudFrame>, <HudTimer>, <AudioMeter>, <LiveReadout>, <UiStatusDot>.
  • [DEL] src/shared/data/overlays.js — replaced by per-brand sources.js

Views — split by kind (src/views/components/)

  • [NEW] src/views/components/sections/hero.vue<HeroSection>, ASCII logo + tagline + SYS.LOG / version tags
  • [NEW] src/views/components/sections/setup.vue<SetupSection>, QUICK SETUP six-step flow
  • [NEW] src/views/components/sections/sources.vue<SourcesSection>, brand tabs + filter bar + grid; owns all filter state (search_query, status_filter, selected_brand) internally
  • [NEW] src/views/components/sections/footer.vue<FooterSection>, author + license line
  • [MOV] src/views/components/sections/meta.vue to src/views/components/sections/stats.vue<StatsSection>, sticky stats strip (SOURCES / BRANDS / READY / CANVAS). Rename: "meta" was ambiguous (metadata / HTML-meta / meta-programming); "stats" describes the purpose directly.
  • [MOD] src/views/components/sections/stats.vue — now renders four <UiDataPoint size="lg"> tiles instead of inline markup.
  • [MOV] src/views/components/overlay-card.vue to src/views/components/elements/card.vue<Card>, grid item rendered via v-for inside <SourcesSection>; DETAILS button, 3-line description clamp, max 3 use-case tags + max 3 requires with +N overflow; imports switched to @modals/preview.vue and @modals/detail.vue. Part B: now consumes <UiDataPoint> for specs, <UiChip> for use-case tags, <UiBadge> for status; ~20 lines of inline SCSS removed in favor of the shared primitives.
  • [MOV] src/views/components/base-modal.vue to src/views/components/modals/base.vue<BaseModal>, generic shell (backdrop, ESC, fade, named slots). Filename stays single-word per Rule G final form — @modals/ alias already supplies "modal", so base-modal.vue would be redundant.
  • [MOV] src/views/components/preview-modal.vue to src/views/components/modals/preview.vue<PreviewModal>, iframe preview composing <BaseModal> from @modals/base.vue
  • [MOV] src/views/components/detail-modal.vue to src/views/components/modals/detail.vue<DetailModal>, full source info composing <BaseModal> from @modals/base.vue. Part B: consumes <UiDataPoint> for specs, <UiChip> for use-case tags, <UiBadge> for status.
  • [MOD] src/views/home.vue — slimmed from ~655 lines to ~80; composes <StatsSection> / <HeroSection> / <SetupSection> / <SourcesSection> / <FooterSection> via @sections/* imports; keeps only brand-count derivation. Part B: <MetaSection> renamed to <StatsSection>.

Shared — split by kind (src/shared/)

  • [NEW] src/shared/components/ui/icon.vue<UiIcon>, generic SVG loader. Part A: globs @shared/assets/svg/*.svg. Part B: now also globs /@*/assets/svg/*.svg — any brand can drop SVGs in its own assets/svg/ and reference by filename; brand SVGs win shared SVGs on filename collision (brand-override semantics).
  • [NEW] src/shared/components/ui/badge.vue<UiBadge>, uppercase HUD status pill with active / dim variant. Replaces .status-badge (card.vue) and .detail-status (detail.vue). Two parents.
  • [NEW] src/shared/components/ui/chip.vue<UiChip>, lowercase pill with solid / overflow variant. Replaces .use-case-tag (card.vue, v-for + overflow) and .detail-tag (detail.vue, v-for). Two parents. Name from Material Design / PrimeVue / Quasar — distinct from HTML tags and from <UiBadge>.
  • [NEW] src/shared/components/ui/data-point.vue<UiDataPoint>, label/value stacked tile with sm / lg size prop. Replaces .meta-item ×4 (stats.vue), .spec ×4 (card.vue), .detail-spec ×4 (detail.vue). Three parents. Name answers "metric of what?" — a data point, i.e. a label paired with a value.
  • [NEW] src/shared/assets/svg/corner-bracket.svg — raw SVG asset consumed by <UiIcon name="corner-bracket">
  • [MOV] src/shared/components/hud-frame.vue to src/shared/components/hud/frame.vue<HudFrame>, 4 corner brackets + labels + slot; consumes <UiIcon> instead of the former <CornerBracket> SFC
  • [MOV] src/shared/components/recording-timer.vue to src/shared/components/hud/timer.vue<HudTimer>, REC + MODE + elapsed time. Part B: now imports <UiStatusDot> from @ui/status-dot.vue (was <UiStatus> from @ui/status.vue).
  • [MOV] src/shared/components/status-indicator.vue to src/shared/components/ui/status.vue<UiStatus>, blinking status dot (Part A). Then Part B: [MOV] src/shared/components/ui/status.vue to src/shared/components/ui/status-dot.vue<UiStatusDot>. Rename: "status" alone failed the "status shown how?" test; "status-dot" answers it directly.
  • [MOV] src/shared/widgets/audio-meter.vue to src/shared/widgets/hud/audio-meter.vue<AudioMeter>, HUD-domain widget (OBS-coupled via useAudioAnalyzer). Part B: widgets split by kind to mirror shared/components/hud|ui/.
  • [MOV] src/shared/widgets/live-readout.vue to src/shared/widgets/ui/live-readout.vue<LiveReadout>, domain-agnostic widget (owns setInterval, takes plain text prop).
  • [DEL] src/shared/components/corner-bracket.vue — replaced by the raw SVG asset + generic <UiIcon> pipeline

Utils — topic-based libraries (src/views/utils/)

  • [MOV] src/views/utils/parse-emphasis.js to src/views/utils/markup.jsparseEmphasis stays a named export. Rule J (Part B): utils/ and data/ are topic-based libraries, never one-function-per-file. Future inline-markup parsers (parseLinks, parseCodeSpans, stripMarkup) absorb into the same file without a rename.

App wiring (src/)

  • [MOD] src/main.js — bootstrap + Part B: eager glob /@*/styles/_theme.scss to bundle every brand theme into global CSS.
  • [MOD] src/router.js — dynamic routes from brand-loader.js via resolveComponent()
  • [MOD] src/App.vue — Part A: brand theme injection via route.meta.brand (CSS class + inline style vars from brand.js colors). Part B: dropped brand_theme_vars computed entirely. Only applies class="brand-<handle>" on the root div now — CSS cascade resolves every var(--clr-*) call to the brand override or :root default.

CI & Tooling

  • [MOD] vite.config.js — 4 top-level scope aliases (@shared, @views, @app, @assets) + 7 kind-folder aliases (@sections, @elements, @modals, @ui, @hud, @widgets, @composables). Alias count bumped from 4 to 11; every kind-folder has one.
  • [MOD] eslint.config.mjs — Vue SFC support (eslint-plugin-vue + vue-eslint-parser), @typescript-eslint/naming-convention for CCS naming on plain JS + .vue, Vue rule overrides (multi-word-component-names off to allow short section filenames), App.vue filename exemption
  • [NEW] tsconfig.eslint.json — ESLint-only TS config (allowJs: true, checkJs: false)
  • [MOD] .gitignore — removed internal comments, added *.gpg, *.sqlite, *.db, *.sql, .npmrc, *.token, *.secret, .ssh/, local clipboard buffers
  • [MOD] .github/workflows/ci.yml — cam-log reference in comment
  • [MOD] .github/PULL_REQUEST_TEMPLATE.md — Tier 1 header (the scroll)

Dependencies

  • Runtime added:
  • Dev added: eslint-plugin-vue, vue-eslint-parser, @typescript-eslint/parser, @typescript-eslint/eslint-plugin
  • Upgraded:
  • Removed:
  • Lockfile: updated

Docs

  • [NEW] CONTRIBUTING.org — prerequisites, setup, code conventions, branch workflow, CI pipeline, PR process (the dojo)
  • [MOD] README.org — added Setup section, Contributing summary, personal project disclosure
  • [MOD] .github/SECURITY.org — expanded: banned code + file patterns tables, enforcement layers, env vars, contributor checklist (the shield)

Technical Details

  • Brand auto-discovery via import.meta.glob

    • Chose: import.meta.glob('/@*/brand.js') with eager loading for metadata, lazy loading for Vue components
    • Over: Manual route registration per brand
    • Why: Adding a new brand folder requires zero app code changes
    • Trade-off: Glob patterns evaluated at build time, not runtime
  • Component classification by kind, not by region

    • Chose: Split views/components/ into sections/ + elements/ + modals/, and shared/components/ into ui/ + hud/; each kind-folder answers "what type of thing is this?"
    • Over: Region-based folders (header/, footer/, hero/) or a flat components/ directory
    • Why: Kind absorbs new siblings without renaming; a future Dashboard page does not collide with Home; the split mirrors how shared is already organized
    • Trade-off: One more path segment per file; authors must pick a kind at creation time
  • Four-layer naming pattern (file / alias / import binding / template tag)

    • Chose: Short kebab filenames (hero.vue, card.vue, base.vue) paired with kind-folder aliases and PascalCase import bindings that re-add the kind as suffix or prefix (HeroSection, Card, BaseModal, UiIcon, HudFrame)
    • Over: Long filenames with the kind baked in (hero-section.vue), or scope prefixes at file level (kot-cam-log.vue)
    • Why: Vue's <script setup> resolves tags from the import binding, not the filename; short filenames stay ergonomic in navigation while import lines and template tags remain self-documenting
    • Trade-off: Import binding and filename look different; new contributors need the kind suffix/prefix registry to stay consistent
  • Eleven Vite aliases, one per kind-folder

    • Chose: 4 top-level scopes + 7 kind-folder aliases — @shared, @views, @app, @assets, @sections, @elements, @modals, @ui, @hud, @widgets, @composables
    • Over: Four top-level aliases only, forcing @views/components/sections/hero.vue in every import
    • Why: Every kind-folder that holds multiple files earns its own alias; imports read as @sections/hero.vue, @modals/base.vue, @hud/frame.vue — the alias alone identifies the kind even before the filename
    • Trade-off: Aliases must be mirrored in any ESLint import resolver; the registry is the choke point for future kind-folders
  • Corner-bracket Vue component replaced by raw SVG + generic UiIcon loader

    • Chose: src/shared/assets/svg/corner-bracket.svg + <UiIcon name="corner-bracket"> via import.meta.glob with ?raw inline
    • Over: One Vue SFC per SVG, or a third-party icon library
    • Why: Adding a new icon is "drop the SVG into the folder"; color still inherits via currentColor; no SFC boilerplate per asset; a single generic loader replaces five component files
    • Trade-off: SVGs are inlined eagerly, which means all icons ship in the initial bundle
  • Sections own their own internal state (thin home.vue)

    • Chose: Filter state (search_query, status_filter, selected_brand) moved out of home.vue and into sources.vue where it is consumed
    • Over: Keeping all state in home.vue and passing it down to every section via props
    • Why: State that only one section reads should live in that section; home.vue becomes a pure composer (~80 lines from ~655)
    • Trade-off: Sections that later need to share state will require either a composable or lifting state back up
  • Landing page components separated from shared

    • Chose: Landing-page-only components live under src/views/components/<kind>/; cross-brand primitives live under src/shared/components/<kind>/
    • Over: One flat src/shared/ directory for every reusable component
    • Why: Rule D of the naming convention — if a brand overlay could consume it, it is shared/; if only one view uses it, it is views/
    • Trade-off: Two component directories instead of one; promoting a views/ component to shared/ requires a move
  • @typescript-eslint/naming-convention on plain JS

    • Chose: @typescript-eslint/parser with tsconfig.eslint.json (allowJs: true)
    • Over: Code-review-only naming enforcement
    • Why: Machine-enforced CCS naming conventions (snake_case vars, camelCase functions, PascalCase classes)
    • Trade-off: Requires TS parser for JS files, adds ~1s to lint time
  • Three ui/ primitives extracted via Rule E audit

    • Chose: Promote <UiDataPoint> (label/value tile), <UiChip> (lowercase pill), <UiBadge> (uppercase status pill) to src/shared/components/ui/
    • Over: Leaving the ~12 inline duplicates (.meta-item ×4, .spec / .detail-spec ×8, .use-case-tag / .detail-tag N, .status-badge / .detail-status ×2) across three consumers
    • Why: Rule E thresholds satisfied — 3 parents for UiDataPoint, 2 parents + v-for for UiChip, 2 parents for UiBadge; all render as pure props-in-DOM-out with no side-effects (Rule B classifies them as ui/, not widgets/); ~40 lines of duplicated SCSS removed from consumers
    • Trade-off: Three additional files in shared/components/ui/; consumers have one more import line per primitive
  • Naming discipline — filename describes purpose, never repeats the alias kind (Rule G final)

    • Chose: Filenames are kebab-case and convey what the component is, not what kind of thing it is. Multi-word only when a single word leaves an "X of what?" question unanswered.
    • Over: A first pass that had added the kind as a suffix (base-modal.vue, hero-section.vue) and had used short-but-ambiguous names (metric.vue for a label/value tile, status.vue for an indicator)
    • Why: The alias already supplies the kind (@modals/base.vue — alias says "modal"; adding -modal to the filename is redundant). Conversely, metric.vue failed because "metric of what?" has no answer from context alone — renamed to data-point.vue. status.vue failed "status shown how?" — renamed to status-dot.vue. The binding layer can repeat the kind (<BaseModal>, <UiDataPoint>) since the binding is the readable identity.
    • Trade-off: Filename vs. binding divergence requires the kind suffix/prefix registry to stay in the architecture docs; casual readers may need to trace imports to confirm the binding form.
  • Topic libraries for utils (Rule J)

    • Chose: utils/ and data/ files are topic-based libraries named after what they parse or stylemarkup.js for inline-markup parsers, future timecode.js for time formatting, future dom.js for DOM queries. Every function exported by name; consumers import only what they need.
    • Over: One-function-per-file (parse-emphasis.js, format-timecode.js, query-dom-element.js), which creates a proliferation of tiny single-purpose files
    • Why: A file can absorb related helpers without being renamed; a parseLinks added next month lives in the same markup.js that already holds parseEmphasis. Composables stay exempt — one useX per file because hooks own state and lifecycle.
    • Trade-off: Import statement is slightly longer (import { parseEmphasis } from '@views/utils/markup.js' vs. a default import from a function-specific file); trivial in practice.
  • Widgets split by kind — mirrors components/hud|ui/

    • Chose: shared/widgets/hud/audio-meter.vue (HUD-domain, OBS-coupled) + shared/widgets/ui/live-readout.vue (domain-agnostic). Alias @widgets stays at root; consumers use @widgets/hud/* and @widgets/ui/*.
    • Over: A flat shared/widgets/ folder with no domain classification
    • Why: Rule B distinguishes widgets from components by side-effect ownership; Rule C distinguishes ui/ from hud/ by domain vocabulary. Both axes apply to widgets too — a widget can be OBS-coupled (hud/) or project-neutral (ui/). Mirroring the components layout means the same kind vocabulary is used consistently across both halves of shared/.
    • Trade-off: Widget consumers now type @widgets/hud/audio-meter.vue instead of @widgets/audio-meter.vue — one extra path segment.
  • Brand folder restructured — web sources in sources/ container

    • Chose: <brand>/sources/<type>/<id>.vue for every web source. brand-loader.js globs updated from /@*/hud/*.vue (etc.) to /@*/sources/hud/*.vue (etc.); resolveComponent() key pattern now /${brand}/sources/${type}/${id}.vue. Reserved slots documented for brand-private components/{hud,ui}/, composables/, widgets/{hud,ui}/ — created only when 2+ files of that kind exist (Rule F).
    • Over: Keeping web sources at the brand root (@kyonax_on_tech/hud/cam-log.vue), where hud/ meant "HUD web source"
    • Why: Eliminates the vocabulary clash where @kyonax_on_tech/hud/ (web sources) and @hud/ (shared HUD primitives) both used "hud". Now the brand folder mirrors src/shared/sources/ for the output products, components/ for brand-private primitives, widgets/ for brand-private data-driven units, composables/ for brand-private hooks.
    • Trade-off: One more path segment per web source file; brand-loader glob patterns needed updating.
  • multi-pool SVG discovery — shared + brand

    • Chose: Two-pool glob — <UiIcon> scans both @shared/assets/svg/*.svg (project-wide primitives) and /@*/assets/svg/*.svg (any brand's private SVGs). Brand SVGs win filename collisions with shared SVGs (brand-override semantics).
    • Over: A single shared pool with brand-prefixed filenames, or separate components per pool
    • Why: Any brand can drop an SVG in its own assets/svg/ folder and reference by filename. Brand-flavored overrides of shared primitives come for free. Cross-brand collisions = last-loaded wins; brands avoid them by choosing distinct filenames.
    • Trade-off: All SVGs (shared + brand) are eagerly inlined into the bundle — no lazy loading.
  • Brand theming via SCSS single source of truth

    • Chose: @<brand>/styles/_theme.scss declares .brand-<handle> { --clr-*: ...; }. src/main.js eagerly globs /@*/styles/_theme.scss so Vite bundles every brand theme into global CSS. src/App.vue applies class="brand-<handle>" on route change. CSS cascade resolves every var(--clr-*) call to the brand override, or to the :root default.
    • Over: The Part A approach — brand.js carried a colors object that App.vue injected as inline style vars per route. Turned out silently broken: the brand SCSS file was orphaned (never imported), brand.js duplicated the SCSS values, and brand.js held only a subset of palette ranges (no border / full warning / full error). Tested: only worked because the @kyonax_on_tech colors happened to match the defaults byte-for-byte.
    • Why: Full-palette override (brands can customize any CSS custom property, not a hard-coded subset), single source of truth (zero drift risk), zero JS runtime cost (CSS cascade does the work), HMR-friendly (edit the brand SCSS and Vite hot-reloads), and enforcement via a brand-loader test that asserts brand.colors === undefined.
    • Trade-off: Brand themes are compiled into the bundle at build time, not fetched at runtime — switching brands requires the SCSS file to have been present at build.

Testing Coverage

Test runner: Vitest @ 4.1.x
Command: npm run test

Automated tests

Test file Covers Tests Status
src/shared/brand-loader.test.js Brand discovery, source schema, type validation, uniqueness, brand-source join, links object, no-colors-field enforcement 24
src/shared/version.test.js VERSION semver shape, VERSION_TAG derivation 3

Total: 27 tests across 2 files, all passing.

Quality gates (run on every PR)

Gate Source Status
Lint eslint.config.mjs via npm run lint ✅ 0 errors (7 known warnings)
Unit tests vite.config.js via npm run test ✅ 27 passing
Build vite.config.js via npm run build ✅ clean, 1.25s
Security scan .github/workflows/ci.yml
License headers .github/workflows/ci.yml
Pre-Check Failed label pre-check-label job in .github/workflows/ci.yml ✅ label absent

How to test this PR

feat: brand-driven + kind-folder architecture, UI primitives, SCSS theming fix
├─ Setup
├─ Brand auto-discovery
├─ Landing page sections
├─ Component kind-folder architecture
├─ New UI primitives (Part B)
├─ Overlay card grid + truncation
├─ Detail modal
├─ cam-log overlay (kind-alias imports)
├─ Brand theming SCSS single source of truth (Part B)
├─ Brand SVG multi-pool (Part B)
├─ ESLint naming conventions
└─ File headers (visual)

Setup

Prereqs: Node.js >=20, npm

  1. npm ci
    Expected: Clean install, no warnings
  2. npm run dev
    Expected: Vite boots on localhost:5173

Brand auto-discovery

Prereqs: Dev server running

  1. Open http://localhost:5173/
    Expected: Landing page shows CAM-LOG card under the @kyonax_on_tech brand tab; brand tab auto-generated from the discovered brand folder
  2. Check the stats bar at the top
    Expected: SOURCES: 2, BRANDS: 1, READY: 1, CANVAS: 1920 × 1080 @ 60

Landing page sections

Prereqs: Dev server running

  1. Scroll the landing page top to bottom
    Expected: Five distinct regions in order — sticky stats bar at the top, hero block with ASCII logo and tagline, QUICK SETUP numbered list, WEB SOURCES browser with filters and grid, footer line with author and license
  2. Scroll the page past the hero
    Expected: Stats bar stays pinned at the top (sticky); its backdrop blurs the content scrolling behind it
  3. Type in the WEB SOURCES filter input
    Expected: Grid updates live; status chips (ALL / READY / PLANNED) still filter correctly

Component kind-folder architecture

Prereqs: Dev server running, Vue DevTools installed

  1. Open Vue DevTools on / and inspect the component tree
    Expected: <Home> renders <StatsSection>, <HeroSection>, <SetupSection>, <SourcesSection>, <FooterSection> as direct children
  2. Expand <SourcesSection>
    Expected: Children include <Card> entries rendered via v-for; each <Card> mounts <PreviewModal> and <DetailModal> children
  3. Expand a <Card> and trigger DETAILS
    Expected: <DetailModal> shows an internal <BaseModal> composition

New UI primitives (Part B)

Prereqs: Dev server running, Vue DevTools installed

  1. Inspect <StatsSection> in the component tree
    Expected: Four <UiDataPoint size="lg"> children with labels SOURCES, BRANDS, READY, CANVAS
  2. Inspect a <Card> in the component tree
    Expected: Card renders <UiBadge> for the status pill, <UiChip> elements for each use-case tag (with variant="overflow" on the +N indicator), and four <UiDataPoint> tiles for the SIZE / FPS / CACHE / CSS specs
  3. Open the DETAILS modal and inspect <DetailModal>
    Expected: Same primitives used — <UiBadge> for status, <UiChip> v-for for use-case tags, <UiDataPoint> for the full spec grid

Overlay card grid and truncation

Prereqs: Landing page loaded

  1. Resize browser from mobile to desktop
    Expected: 1 column on narrow, 2 at 768px+, 3 at 1322px+
  2. Check CAM-LOG card description
    Expected: 3 lines max, clamped
  3. Check WHEN TO USE tags
    Expected: 3 <UiChip> tags + dimmed +N <UiChip variant="overflow"> indicator if more exist
  4. Check corner brackets
    Expected: Gold SVG corners visible at all four card corners, inlined via <UiIcon>

Detail modal

Prereqs: Landing page loaded

  1. Click DETAILS on the CAM-LOG card
    Expected: <DetailModal> opens with full description, all tags, specs, all requirements
  2. Press ESC
    Expected: Modal closes via <BaseModal> ESC handler

cam-log overlay (kind-alias imports)

Prereqs: Dev server running

  1. Navigate to http://localhost:5173/@kyonax_on_tech/cam-log
    Expected: HUD overlay renders — identity block shows "Cristian D. Moreno" and @kyonax_on_tech (from brand.js, not hardcoded); <HudFrame> renders four corner brackets via <UiIcon>; <HudTimer> (using <UiStatusDot>) + <AudioMeter> + <LiveReadout> all mount and connect to OBS when available
  2. Open the browser devtools Network tab and inspect the bundled module graph for cam-log
    Expected: Resolved imports come from @hud/frame.vue, @hud/timer.vue, @widgets/hud/audio-meter.vue, @widgets/ui/live-readout.vue, @composables/use-obs-websocket.js (the Part-B kind-subfolder aliases)

Brand theming SCSS single source of truth (Part B)

Prereqs: Dev server running

  1. Navigate to http://localhost:5173/@kyonax_on_tech/cam-log and inspect the root <div> in devtools
    Expected: Root div carries class="brand-kyonax-on-tech"; no inline style="--clr-..." vars on the root div (theming is pure-SCSS, not JS-inline)
  2. Open Computed Styles on any element and look up --clr-primary-100
    Expected: Resolves to gold hsl(47, 95%, 56%) — the brand override from @kyonax_on_tech/styles/_theme.scss, not the :root default
  3. Edit @kyonax_on_tech/styles/_theme.scss — change --clr-primary-100 to red, save
    Expected: Vite HMR pushes the update; the HUD gold accents turn red without a full reload
  4. Revert the edit, then run npm run build && grep brand-kyonax-on-tech dist/assets/*.css
    Expected: Grep matches — confirming the brand theme class is inlined into the production CSS bundle
  5. Open @kyonax_on_tech/brand.js and attempt to re-add a colors: { ... } field, then run npm run test
    Expected: Test fails — brand @kyonax_on_tech does NOT carry a colors field asserts brand.colors === undefined

Brand SVG multi-pool (Part B)

Prereqs: Dev server running

  1. Create a test SVG at @kyonax_on_tech/assets/svg/test-icon.svg with fill="currentColor" on a simple shape
  2. Reference it anywhere in a Vue template: <UiIcon name="test-icon" :size="24" />
    Expected: Icon renders, inheriting the parent element's color via currentColor
  3. Create an SVG with a filename that also exists in src/shared/assets/svg/ (e.g. corner-bracket.svg) in the brand folder with a visibly different shape
    Expected: <UiIcon name="corner-bracket" /> renders the brand's version — brand wins the collision

ESLint naming conventions

Prereqs: none

  1. npm run lint
    Expected: 0 errors, 7 warnings (all pre-existing false positives in use-audio-analyzer.js array indexing)
  2. Temporarily rename a snake_case variable to camelCase in any .js or .vue file, run npm run lint
    Expected: @typescript-eslint/naming-convention error reported

File headers (visual)

Prereqs: none

  1. Open root config files in an editor
    Expected: Each has: license block, figlet ASCII art with cyberpunk place name, filename + date, description, TOC, author/contact

Documentation

DESKTOP — Landing page with overlay cards

Shows 3-column grid, truncated cards, corner brackets, brand tabs, sticky stats bar. Card specs now render via <UiDataPoint>; status as <UiBadge>; use-case tags as <UiChip>.

Demo captures pending, will be attached after merge.

DIAGRAM — Component kind-folder architecture

Views split into sections/ + elements/ + modals/; shared into components/{hud,ui}/ + widgets/{hud,ui}/; eleven Vite aliases one per kind-folder. Brand folders mirror the same shape with sources/{hud,animation,scene}/ for web-source output plus reserved components/{hud,ui}/, composables/, widgets/{hud,ui}/ for brand-private primitives.

See the project tree and the component classification rules in CONTRIBUTING.org.

DIAGRAM — Brand theming SCSS single source of truth (Part B)

Three-layer flow: src/app/scss/abstracts/_theme.scss declares :root defaults; @<brand>/styles/_theme.scss redeclares any subset inside .brand-<handle>, auto-loaded by src/main.js's eager glob; src/App.vue applies the brand class on route change and the CSS cascade resolves every var(--clr-*) to the brand override or the :root default.

Full flow documented in CONTRIBUTING.org under the brand-theming architecture section.

…le headers

Refinement pass on the v0.3 overlay card system and project
documentation. Four areas of work.

1. Overlay card fixes. Removed overflow: hidden that was
   clipping corner-bracket SVGs. Replaced the px-based
   auto-fill grid with mobile-first 1fr columns (1 col
   default, 2 at sm/48em, 3 at lg/82.667em). Added
   truncation for consistent card height: 3-line CSS clamp
   on descriptions, max 3 use_case tags and requires items
   with +N overflow indicators. Always-visible DETAILS
   button opens a full-content detail modal.

2. Modal abstraction. Extracted base-modal.vue (reusable
   shell with backdrop, ESC, teleport, fade, named slots).
   Refactored preview-modal.vue to use it. Created
   detail-modal.vue as a second consumer for overlay info.
   Extracted parseEmphasis into shared utils.

3. cam-person renamed to cam-log across 7 files (Vue
   component, router, overlays.js id + path, CHANGELOG,
   ci.yml comment, local buffers) for consistency with the
   display name CAM-LOG.

4. Tier 1 file headers. Established a standardized header
   pattern for all root config/setup/org files using figlet
   smslant ASCII art with cyberpunk place names. Applied to
   11 files: .gitignore (the void), vite.config.js (the
   forge), eslint.config.mjs (the precinct), index.html
   (the gate), .gitattributes (the lab), .env.example (the
   vault), ci.yml (the watchtower), release.yml (the
   checkpoint), CHANGELOG.org (the logs), README.org (the
   bridge), LICENSING.org (the pact). Each header follows:
   license, figlet art, filename + date, description, TOC,
   optional guidelines/requirements, author/contact.

Modified-by: Cristian D. Moreno (Kyonax) <kyonax25@gmail.com>
@Kyonax Kyonax self-assigned this Apr 17, 2026
@github-actions
Copy link
Copy Markdown

Protected Files Modified

One or more files in the protected set were changed in this PR. Each category below explains why the file matters.

Legal / Licensing

  • LICENSING.org was modified

Modifying these files changes the project's legal posture. Confirm with the maintainer before merging.

CI / Security Config

  • .github/workflows/ci.yml was modified
  • .github/workflows/release.yml was modified
  • eslint.config.mjs was modified

Workflow / lint config. A quiet edit here can disable gates — diff against origin carefully.

Build / Config

  • vite.config.js was modified
  • .gitignore was modified
  • .env.example was modified

Build or gitignore config. Verify the build still passes and no ignored paths were accidentally un-ignored.

Release Artifact

  • CHANGELOG.org was modified
  • README.org was modified

Release-tracking files. Expected on release PRs; flag on non-release PRs.

…xes, Tier 1 headers

Comprehensive refinement and architectural refactor of the
v0.3 codebase. Six areas of work.

1. Brand-driven architecture. Each @brand/ folder at the
   project root is now the single source of truth for that
   brand. brand-loader.js auto-discovers brands + sources
   via import.meta.glob. Routes generate dynamically from
   discovered sources. Centralized overlays.js deleted,
   replaced by per-brand sources.js. src/brands/ removed.
   cam-log.vue moved to @kyonax_on_tech/hud/cam-log.vue.
   Brand metadata (identity, colors, links) in brand.js.
   Theme injection in App.vue via route meta.

2. Vite aliases + import cleanup. Added @shared, @views,
   @app, @assets aliases. Converted all imports from
   relative paths to alias-based. Zero ../  paths remain.
   Landing page components (overlay-card, modals,
   parse-emphasis) separated from src/shared/ to
   src/views/ since they are landing-page-only.

3. ESLint Vue SFC + naming conventions. Added
   eslint-plugin-vue with flat/essential processor for
   script setup support. Added @typescript-eslint/
   naming-convention for CCS standards enforcement on
   plain JS (snake_case vars, camelCase functions,
   UPPER_CASE constants, PascalCase classes). Created
   tsconfig.eslint.json for type-aware linting.

4. Overlay card fixes. Removed overflow: hidden, mobile-
   first 1fr grid, description 3-line clamp, max 3
   use_case tags and requires with +N indicators,
   always-visible DETAILS button with detail-modal.

5. Tier 1 file headers. Figlet smslant ASCII art with
   cyberpunk place names on 15 root config/setup/org
   files. Structured layout: license, art, filename,
   date, description, TOC, guidelines, author/contact.

6. Documentation + security. CONTRIBUTING.org created
   with setup, conventions, workflow, CI, PR process.
   SECURITY.org expanded with banned patterns table,
   enforcement layers, contributor checklist. .gitignore
   hardened with additional secret-file patterns. README
   updated with Setup section and Contributing summary.

31 files changed, 1611 insertions, 336 deletions.
26 tests passing, 0 lint errors, clean build.

Modified-by: Cristian D. Moreno (Kyonax) <kyonax25@gmail.com>
@github-actions
Copy link
Copy Markdown

Protected Files Modified

One or more files in the protected set were changed in this PR. Each category below explains why the file matters.

Legal / Licensing

  • LICENSING.org was modified

Modifying these files changes the project's legal posture. Confirm with the maintainer before merging.

Governance

  • .github/SECURITY.org was modified
  • .github/PULL_REQUEST_TEMPLATE.md was modified

CODEOWNERS / SECURITY / PR template changes affect how every future PR is reviewed. Review carefully.

Supply Chain

  • package.json was modified
  • package-lock.json was modified

Dependency or lockfile changes. Verify the diff (no unexpected packages, no version downgrades).

CI / Security Config

  • .github/workflows/ci.yml was modified
  • .github/workflows/release.yml was modified
  • eslint.config.mjs was modified

Workflow / lint config. A quiet edit here can disable gates — diff against origin carefully.

Build / Config

  • vite.config.js was modified
  • .gitignore was modified
  • .env.example was modified

Build or gitignore config. Verify the build still passes and no ignored paths were accidentally un-ignored.

Release Artifact

  • CHANGELOG.org was modified
  • README.org was modified

Release-tracking files. Expected on release PRs; flag on non-release PRs.

@Kyonax Kyonax changed the title fix: overlay card fixes, modal abstraction, cam-log rename, Tier 1 fi… feat: brand-driven architecture, ESLint Vue + naming, overlay card fixes, Tier 1 headers Apr 17, 2026
Splits the component layer by kind (views into sections /
elements / modals; shared components into ui / hud), extracts
five real sections out of home.vue, adds one Vite alias per
kind-folder, and rewrites all template tags to PascalCase so
they match the import binding. 18 files touched.

1. views/components/ split by kind

   Three new subfolders with a single purpose each:

     sections/   top-level page regions (one per page)
     elements/   reusable list items (v-for children)
     modals/     overlay surfaces (shell + instances)

   Existing view files moved into their kind folder:

     overlay-card.vue    to  elements/card.vue
     base-modal.vue      to  modals/base.vue
     preview-modal.vue   to  modals/preview.vue
     detail-modal.vue    to  modals/detail.vue

2. Five sections extracted from home.vue

     meta.vue     sticky stats strip — SOURCES/BRANDS/READY/CANVAS
     hero.vue     ASCII logo + tagline + SYS.LOG/version tags
     setup.vue    QUICK SETUP six-step flow
     sources.vue  brand tabs + filter bar + grid
     footer.vue   author + license line

   home.vue slimmed from ~655 lines to ~80. Filter state —
   search_query, status_filter, selected_brand — moved out of
   home.vue and into sources.vue where it is actually consumed.

3. shared/components/ renamed to Option-C short filenames

     components/hud-frame.vue         to  components/hud/frame.vue
     components/recording-timer.vue   to  components/hud/timer.vue
     components/status-indicator.vue  to  components/ui/status.vue

   Filenames drop the kind; the kind comes back at the
   import-binding layer as <HudFrame>, <HudTimer>, <UiStatus>.

4. Corner-bracket Vue component to raw SVG asset

   components/corner-bracket.vue deleted. Replaced by a raw SVG
   at assets/svg/corner-bracket.svg plus a new generic loader at
   components/ui/icon.vue using import.meta.glob with the ?raw
   query. New icons are now "drop the file into the folder" — no
   more one-SFC-per-icon boilerplate.

5. Seven new Vite kind-aliases in vite.config.js

     @Sections     src/views/components/sections/
     @elements     src/views/components/elements/
     @modals       src/views/components/modals/
     @ui           src/shared/components/ui/
     @hud          src/shared/components/hud/
     @widgets      src/shared/widgets/
     @composables  src/shared/composables/

   Alias count grows from 4 to 11. Every kind-folder has one.

6. Template tags and imports rewritten

   All tags in touched files switched to PascalCase to match the
   import binding. cam-log.vue, frame.vue, timer.vue, card.vue,
   preview.vue, detail.vue, sources.vue, home.vue. Bindings
   follow the kind suffix/prefix registry:

     @Sections   XxxSection   HeroSection, MetaSection, ...
     @modals     XxxModal     BaseModal, PreviewModal, ...
     @elements   Xxx          Card
     @ui         UiXxx        UiIcon, UiStatus
     @hud        HudXxx       HudFrame, HudTimer
     @widgets    as-is        AudioMeter, LiveReadout

Validation: 26/26 tests pass, 0 lint errors (7 pre-existing
warnings), production build clean in 1.15s.

Modified-by: Cristian D. Moreno (Kyonax) <kyonax25@gmail.com>
@github-actions
Copy link
Copy Markdown

Protected Files Modified

One or more files in the protected set were changed in this PR. Each category below explains why the file matters.

Legal / Licensing

  • LICENSING.org was modified

Modifying these files changes the project's legal posture. Confirm with the maintainer before merging.

Governance

  • .github/SECURITY.org was modified
  • .github/PULL_REQUEST_TEMPLATE.md was modified

CODEOWNERS / SECURITY / PR template changes affect how every future PR is reviewed. Review carefully.

Supply Chain

  • package.json was modified
  • package-lock.json was modified

Dependency or lockfile changes. Verify the diff (no unexpected packages, no version downgrades).

CI / Security Config

  • .github/workflows/ci.yml was modified
  • .github/workflows/release.yml was modified
  • eslint.config.mjs was modified

Workflow / lint config. A quiet edit here can disable gates — diff against origin carefully.

Build / Config

  • vite.config.js was modified
  • .gitignore was modified
  • .env.example was modified

Build or gitignore config. Verify the build still passes and no ignored paths were accidentally un-ignored.

Release Artifact

  • CHANGELOG.org was modified
  • README.org was modified

Release-tracking files. Expected on release PRs; flag on non-release PRs.

Post-Part-A refinement pass. Seven coherent changes that tighten
naming discipline, deduplicate view SCSS, isolate web sources from
shared HUD vocabulary, and fix the silently-broken brand theming
chain. 23 files staged.

1. Three new shared/components/ui/ primitives

     data-point.vue   <UiDataPoint>   label/value tile (sm | lg)
     chip.vue         <UiChip>        lowercase pill (solid | overflow)
     badge.vue        <UiBadge>       uppercase status pill (active | dim)

   Replaced ~12 inline duplicates across stats.vue, card.vue, and
   detail.vue. ~40 lines of duplicated SCSS removed from consumers.
   All props-in-DOM-out — correctly placed in ui/ (no side-effects).

2. shared/widgets/ split by kind

     widgets/audio-meter.vue   to  widgets/hud/audio-meter.vue
     widgets/live-readout.vue  to  widgets/ui/live-readout.vue

   Mirrors the shared/components/hud|ui split: audio-meter is
   OBS-coupled (HUD-domain), live-readout is domain-agnostic.
   Consumers import via @widgets/hud/* and @widgets/ui/*.

3. Filenames describe purpose, never repeat the alias kind (Rule G)

     sections/meta.vue              to  sections/stats.vue
     ui/metric.vue                  to  ui/data-point.vue
     ui/status.vue                  to  ui/status-dot.vue
     views/utils/parse-emphasis.js  to  views/utils/markup.js

   "meta" was ambiguous (metadata/HTML-meta/meta-programming).
   "metric of what?" / "status shown how?" tests failed — multi-
   word names answer the question. Modals stay single-word
   (base/preview/detail.vue) because @modals/ already supplies
   "modal" — base-modal.vue would be redundant. Bindings keep
   the kind: <BaseModal>, <UiDataPoint>, <StatsSection>.

4. Utils as topic-based libraries (Rule J)

   markup.js holds parseEmphasis as a named export. Future markup
   parsers (parseLinks, parseCodeSpans) live in the same file.
   Never one-function-per-file. Composables exempt — one useX
   per file because hooks own state and lifecycle.

5. Brand folder restructured to mirror src/shared/

     @brand/hud/cam-log.vue   to  @brand/sources/hud/cam-log.vue
     @brand/animation/        to  @brand/sources/animation/
     @brand/scene/            to  @brand/sources/scene/

   Eliminates the clash where @brand/hud/ (web sources) and @hud/
   (shared HUD primitives) both used "hud". brand-loader.js globs
   updated; resolveComponent() key pattern now
   /<brand>/sources/<type>/<id>.vue. Reserved slots documented
   for brand-private components/{hud,ui}/, composables/,
   widgets/{hud,ui}/ (created only when 2+ files exist, Rule F).

6. <UiIcon> multi-pool SVG discovery

   Previously globbed only @shared/assets/svg/*.svg. Now also
   globs /@*/assets/svg/*.svg — any brand can drop SVGs in its
   own assets/svg/ folder and reference them by filename. Brand
   SVGs override shared SVGs on filename collision (useful for
   brand-flavored overrides of shared primitives).

7. Brand theming — SCSS single source of truth

   Fixed three silently-broken things:
     - @<brand>/styles/_theme.scss was orphaned (never imported).
     - brand.js colors duplicated the SCSS values.
     - brand.js held only a subset of palette ranges.

   Fix: src/main.js eagerly globs /@*/styles/_theme.scss so Vite
   bundles every brand theme into global CSS. brand.js lost its
   colors field entirely. App.vue dropped brand_theme_vars and
   only applies .brand-<handle> on route change — the CSS
   cascade does the rest. brand-loader test flipped from "has
   colors object" to "does NOT carry colors field" (enforcement).
   Verified: grep brand-kyonax-on-tech dist/assets/*.css
   matches in the production bundle.

Validation: 27/27 tests pass, 0 lint errors (7 pre-existing
warnings in use-audio-analyzer.js), production build clean in
1.25s.

Modified-by: Cristian D. Moreno (Kyonax) <kyonax25@gmail.com>
@github-actions
Copy link
Copy Markdown

Protected Files Modified

One or more files in the protected set were changed in this PR. Each category below explains why the file matters.

Legal / Licensing

  • LICENSING.org was modified

Modifying these files changes the project's legal posture. Confirm with the maintainer before merging.

Governance

  • .github/SECURITY.org was modified
  • .github/PULL_REQUEST_TEMPLATE.md was modified

CODEOWNERS / SECURITY / PR template changes affect how every future PR is reviewed. Review carefully.

Supply Chain

  • package.json was modified
  • package-lock.json was modified

Dependency or lockfile changes. Verify the diff (no unexpected packages, no version downgrades).

CI / Security Config

  • .github/workflows/ci.yml was modified
  • .github/workflows/release.yml was modified
  • eslint.config.mjs was modified

Workflow / lint config. A quiet edit here can disable gates — diff against origin carefully.

Build / Config

  • vite.config.js was modified
  • .gitignore was modified
  • .env.example was modified

Build or gitignore config. Verify the build still passes and no ignored paths were accidentally un-ignored.

Release Artifact

  • CHANGELOG.org was modified
  • README.org was modified

Release-tracking files. Expected on release PRs; flag on non-release PRs.

@Kyonax Kyonax merged commit e6e112f into dev Apr 21, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant