Conversation
…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>
Protected Files ModifiedOne or more files in the protected set were changed in this PR. Each category below explains why the file matters. Legal / Licensing
Modifying these files changes the project's legal posture. Confirm with the maintainer before merging. CI / Security Config
Workflow / lint config. A quiet edit here can disable gates — diff against origin carefully. Build / Config
Build or gitignore config. Verify the build still passes and no ignored paths were accidentally un-ignored. Release Artifact
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>
Protected Files ModifiedOne or more files in the protected set were changed in this PR. Each category below explains why the file matters. Legal / Licensing
Modifying these files changes the project's legal posture. Confirm with the maintainer before merging. Governance
CODEOWNERS / SECURITY / PR template changes affect how every future PR is reviewed. Review carefully. Supply Chain
Dependency or lockfile changes. Verify the diff (no unexpected packages, no version downgrades). CI / Security Config
Workflow / lint config. A quiet edit here can disable gates — diff against origin carefully. Build / Config
Build or gitignore config. Verify the build still passes and no ignored paths were accidentally un-ignored. Release Artifact
Release-tracking files. Expected on release PRs; flag on non-release PRs. |
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>
Protected Files ModifiedOne or more files in the protected set were changed in this PR. Each category below explains why the file matters. Legal / Licensing
Modifying these files changes the project's legal posture. Confirm with the maintainer before merging. Governance
CODEOWNERS / SECURITY / PR template changes affect how every future PR is reviewed. Review carefully. Supply Chain
Dependency or lockfile changes. Verify the diff (no unexpected packages, no version downgrades). CI / Security Config
Workflow / lint config. A quiet edit here can disable gates — diff against origin carefully. Build / Config
Build or gitignore config. Verify the build still passes and no ignored paths were accidentally un-ignored. Release Artifact
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>
Protected Files ModifiedOne or more files in the protected set were changed in this PR. Each category below explains why the file matters. Legal / Licensing
Modifying these files changes the project's legal posture. Confirm with the maintainer before merging. Governance
CODEOWNERS / SECURITY / PR template changes affect how every future PR is reviewed. Review carefully. Supply Chain
Dependency or lockfile changes. Verify the diff (no unexpected packages, no version downgrades). CI / Security Config
Workflow / lint config. A quiet edit here can disable gates — diff against origin carefully. Build / Config
Build or gitignore config. Verify the build still passes and no ignored paths were accidentally un-ignored. Release Artifact
Release-tracking files. Expected on release PRs; flag on non-release PRs. |
Checklist (check if it applies)
npm run lint)Pre-Check Failedlabel applied by CI)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 throughimport.meta.globauto-discovery. Pass two splits the component layer by kind —sections/+elements/+modals/underviews/,ui/+hud/undershared/components/— pairs every kind-folder with its own Vite alias (11 total), and extracts five real sections out of the landing page sohome.vuebecomes 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 alongsideCONTRIBUTING.organd an expandedSECURITY.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, splitsshared/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/*.svgso 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.scssauto-loaded bysrc/main.js,brand.jsno longer carriescolors,App.vueonly applies the.brand-<handle>class — the CSS cascade does the rest).Design / Reference: Scope tracked in
CHANGELOG.org. ASCII logo at.github/assets/logo.txt. Component architecture rules codified inCONTRIBUTING.org.Implementation
Brand-driven architecture (
@kyonax_on_tech/,src/shared/)@kyonax_on_tech/brand.js— brand metadata: handle, name, identity, links. Part B:colorsfield removed — the palette lives exclusively instyles/_theme.scss(see Brand theming below).@kyonax_on_tech/sources.js— web source card data (extracted from overlays.js, addedtypefield)@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 bysrc/main.js.src/shared/brand-loader.js— auto-discovery engine usingimport.meta.glob. Part B: globs updated from/@*/hud/*.vue(etc.) to/@*/sources/hud/*.vue(etc.);resolveComponent()key pattern now/${brand}/sources/${type}/${id}.vue.src/brands/kyonax-on-tech/cam-log.vueto@kyonax_on_tech/hud/cam-log.vue(Part A). Then Part B: [MOV]@kyonax_on_tech/hud/cam-log.vueto@kyonax_on_tech/sources/hud/cam-log.vue— identity still pulled fromgetBrand(); 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>.src/shared/data/overlays.js— replaced by per-brandsources.jsViews — split by kind (
src/views/components/)src/views/components/sections/hero.vue—<HeroSection>, ASCII logo + tagline +SYS.LOG/ version tagssrc/views/components/sections/setup.vue—<SetupSection>, QUICK SETUP six-step flowsrc/views/components/sections/sources.vue—<SourcesSection>, brand tabs + filter bar + grid; owns all filter state (search_query,status_filter,selected_brand) internallysrc/views/components/sections/footer.vue—<FooterSection>, author + license linesrc/views/components/sections/meta.vuetosrc/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.src/views/components/sections/stats.vue— now renders four<UiDataPoint size="lg">tiles instead of inline markup.src/views/components/overlay-card.vuetosrc/views/components/elements/card.vue—<Card>, grid item rendered viav-forinside<SourcesSection>; DETAILS button, 3-line description clamp, max 3 use-case tags + max 3 requires with+Noverflow; imports switched to@modals/preview.vueand@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.src/views/components/base-modal.vuetosrc/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", sobase-modal.vuewould be redundant.src/views/components/preview-modal.vuetosrc/views/components/modals/preview.vue—<PreviewModal>, iframe preview composing<BaseModal>from@modals/base.vuesrc/views/components/detail-modal.vuetosrc/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.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/)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 ownassets/svg/and reference by filename; brand SVGs win shared SVGs on filename collision (brand-override semantics).src/shared/components/ui/badge.vue—<UiBadge>, uppercase HUD status pill withactive/dimvariant. Replaces.status-badge(card.vue) and.detail-status(detail.vue). Two parents.src/shared/components/ui/chip.vue—<UiChip>, lowercase pill withsolid/overflowvariant. 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>.src/shared/components/ui/data-point.vue—<UiDataPoint>, label/value stacked tile withsm/lgsize 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.src/shared/assets/svg/corner-bracket.svg— raw SVG asset consumed by<UiIcon name="corner-bracket">src/shared/components/hud-frame.vuetosrc/shared/components/hud/frame.vue—<HudFrame>, 4 corner brackets + labels + slot; consumes<UiIcon>instead of the former<CornerBracket>SFCsrc/shared/components/recording-timer.vuetosrc/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).src/shared/components/status-indicator.vuetosrc/shared/components/ui/status.vue—<UiStatus>, blinking status dot (Part A). Then Part B: [MOV]src/shared/components/ui/status.vuetosrc/shared/components/ui/status-dot.vue—<UiStatusDot>. Rename: "status" alone failed the "status shown how?" test; "status-dot" answers it directly.src/shared/widgets/audio-meter.vuetosrc/shared/widgets/hud/audio-meter.vue—<AudioMeter>, HUD-domain widget (OBS-coupled viauseAudioAnalyzer). Part B: widgets split by kind to mirrorshared/components/hud|ui/.src/shared/widgets/live-readout.vuetosrc/shared/widgets/ui/live-readout.vue—<LiveReadout>, domain-agnostic widget (ownssetInterval, takes plaintextprop).src/shared/components/corner-bracket.vue— replaced by the raw SVG asset + generic<UiIcon>pipelineUtils — topic-based libraries (
src/views/utils/)src/views/utils/parse-emphasis.jstosrc/views/utils/markup.js—parseEmphasisstays a named export. Rule J (Part B):utils/anddata/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/)src/main.js— bootstrap + Part B: eager glob/@*/styles/_theme.scssto bundle every brand theme into global CSS.src/router.js— dynamic routes frombrand-loader.jsviaresolveComponent()src/App.vue— Part A: brand theme injection viaroute.meta.brand(CSS class + inline style vars frombrand.jscolors). Part B: droppedbrand_theme_varscomputed entirely. Only appliesclass="brand-<handle>"on the root div now — CSS cascade resolves everyvar(--clr-*)call to the brand override or:rootdefault.CI & Tooling
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.eslint.config.mjs— Vue SFC support (eslint-plugin-vue+vue-eslint-parser),@typescript-eslint/naming-conventionfor CCS naming on plain JS +.vue, Vue rule overrides (multi-word-component-namesoff to allow short section filenames),App.vuefilename exemptiontsconfig.eslint.json— ESLint-only TS config (allowJs: true,checkJs: false).gitignore— removed internal comments, added*.gpg,*.sqlite,*.db,*.sql,.npmrc,*.token,*.secret,.ssh/, local clipboard buffers.github/workflows/ci.yml— cam-log reference in comment.github/PULL_REQUEST_TEMPLATE.md— Tier 1 header (the scroll)Dependencies
eslint-plugin-vue,vue-eslint-parser,@typescript-eslint/parser,@typescript-eslint/eslint-pluginDocs
CONTRIBUTING.org— prerequisites, setup, code conventions, branch workflow, CI pipeline, PR process (the dojo)README.org— added Setup section, Contributing summary, personal project disclosure.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
import.meta.glob('/@*/brand.js')with eager loading for metadata, lazy loading for Vue componentsComponent classification by kind, not by region
views/components/intosections/+elements/+modals/, andshared/components/intoui/+hud/; each kind-folder answers "what type of thing is this?"header/,footer/,hero/) or a flatcomponents/directoryFour-layer naming pattern (file / alias / import binding / template tag)
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)hero-section.vue), or scope prefixes at file level (kot-cam-log.vue)<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-documentingEleven Vite aliases, one per kind-folder
@shared,@views,@app,@assets,@sections,@elements,@modals,@ui,@hud,@widgets,@composables@views/components/sections/hero.vuein every import@sections/hero.vue,@modals/base.vue,@hud/frame.vue— the alias alone identifies the kind even before the filenameCorner-bracket Vue component replaced by raw SVG + generic UiIcon loader
src/shared/assets/svg/corner-bracket.svg+<UiIcon name="corner-bracket">viaimport.meta.globwith?rawinlinecurrentColor; no SFC boilerplate per asset; a single generic loader replaces five component filesSections own their own internal state (thin home.vue)
search_query,status_filter,selected_brand) moved out ofhome.vueand intosources.vuewhere it is consumedhome.vueand passing it down to every section via propshome.vuebecomes a pure composer (~80 lines from ~655)Landing page components separated from shared
src/views/components/<kind>/; cross-brand primitives live undersrc/shared/components/<kind>/src/shared/directory for every reusable componentshared/; if only one view uses it, it isviews/views/component toshared/requires a move@typescript-eslint/naming-convention on plain JS
@typescript-eslint/parserwithtsconfig.eslint.json(allowJs: true)Three ui/ primitives extracted via Rule E audit
<UiDataPoint>(label/value tile),<UiChip>(lowercase pill),<UiBadge>(uppercase status pill) tosrc/shared/components/ui/.meta-item×4,.spec/.detail-spec×8,.use-case-tag/.detail-tagN,.status-badge/.detail-status×2) across three consumersshared/components/ui/; consumers have one more import line per primitiveNaming discipline — filename describes purpose, never repeats the alias kind (Rule G final)
base-modal.vue,hero-section.vue) and had used short-but-ambiguous names (metric.vuefor a label/value tile,status.vuefor an indicator)@modals/base.vue— alias says "modal"; adding-modalto the filename is redundant). Conversely,metric.vuefailed because "metric of what?" has no answer from context alone — renamed todata-point.vue.status.vuefailed "status shown how?" — renamed tostatus-dot.vue. The binding layer can repeat the kind (<BaseModal>,<UiDataPoint>) since the binding is the readable identity.Topic libraries for utils (Rule J)
utils/anddata/files are topic-based libraries named after what they parse or style —markup.jsfor inline-markup parsers, futuretimecode.jsfor time formatting, futuredom.jsfor DOM queries. Every function exported by name; consumers import only what they need.parse-emphasis.js,format-timecode.js,query-dom-element.js), which creates a proliferation of tiny single-purpose filesparseLinksadded next month lives in the samemarkup.jsthat already holdsparseEmphasis. Composables stay exempt — oneuseXper file because hooks own state and lifecycle.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/
shared/widgets/hud/audio-meter.vue(HUD-domain, OBS-coupled) +shared/widgets/ui/live-readout.vue(domain-agnostic). Alias@widgetsstays at root; consumers use@widgets/hud/*and@widgets/ui/*.shared/widgets/folder with no domain classificationshared/.@widgets/hud/audio-meter.vueinstead of@widgets/audio-meter.vue— one extra path segment.Brand folder restructured — web sources in sources/ container
<brand>/sources/<type>/<id>.vuefor every web source.brand-loader.jsglobs updated from/@*/hud/*.vue(etc.) to/@*/sources/hud/*.vue(etc.);resolveComponent()key pattern now/${brand}/sources/${type}/${id}.vue. Reserved slots documented for brand-privatecomponents/{hud,ui}/,composables/,widgets/{hud,ui}/— created only when 2+ files of that kind exist (Rule F).@kyonax_on_tech/hud/cam-log.vue), wherehud/meant "HUD web source"@kyonax_on_tech/hud/(web sources) and@hud/(shared HUD primitives) both used "hud". Now the brand folder mirrorssrc/shared/—sources/for the output products,components/for brand-private primitives,widgets/for brand-private data-driven units,composables/for brand-private hooks.multi-pool SVG discovery — shared + brand
<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).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.Brand theming via SCSS single source of truth
@<brand>/styles/_theme.scssdeclares.brand-<handle> { --clr-*: ...; }.src/main.jseagerly globs/@*/styles/_theme.scssso Vite bundles every brand theme into global CSS.src/App.vueappliesclass="brand-<handle>"on route change. CSS cascade resolves everyvar(--clr-*)call to the brand override, or to the:rootdefault.brand.jscarried acolorsobject thatApp.vueinjected as inline style vars per route. Turned out silently broken: the brand SCSS file was orphaned (never imported),brand.jsduplicated the SCSS values, andbrand.jsheld only a subset of palette ranges (no border / full warning / full error). Tested: only worked because the@kyonax_on_techcolors happened to match the defaults byte-for-byte.brand.colors === undefined.Testing Coverage
Test runner: Vitest @ 4.1.x
Command:
npm run testAutomated tests
src/shared/brand-loader.test.jssrc/shared/version.test.jsTotal: 27 tests across 2 files, all passing.
Quality gates (run on every PR)
eslint.config.mjsvianpm run lintvite.config.jsvianpm run testvite.config.jsvianpm run build.github/workflows/ci.yml.github/workflows/ci.ymlPre-Check Failedlabelpre-check-labeljob in.github/workflows/ci.ymlHow to test this PR
Setup
npm ciExpected: Clean install, no warnings
npm run devExpected: Vite boots on
localhost:5173Brand auto-discovery
http://localhost:5173/Expected: Landing page shows CAM-LOG card under the
@kyonax_on_techbrand tab; brand tab auto-generated from the discovered brand folderExpected:
SOURCES: 2,BRANDS: 1,READY: 1,CANVAS: 1920 × 1080 @ 60Landing page sections
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
Expected: Stats bar stays pinned at the top (sticky); its backdrop blurs the content scrolling behind it
Expected: Grid updates live; status chips (ALL / READY / PLANNED) still filter correctly
Component kind-folder architecture
/and inspect the component treeExpected:
<Home>renders<StatsSection>,<HeroSection>,<SetupSection>,<SourcesSection>,<FooterSection>as direct children<SourcesSection>Expected: Children include
<Card>entries rendered viav-for; each<Card>mounts<PreviewModal>and<DetailModal>children<Card>and trigger DETAILSExpected:
<DetailModal>shows an internal<BaseModal>compositionNew UI primitives (Part B)
<StatsSection>in the component treeExpected: Four
<UiDataPoint size="lg">children with labelsSOURCES,BRANDS,READY,CANVAS<Card>in the component treeExpected: Card renders
<UiBadge>for the status pill,<UiChip>elements for each use-case tag (withvariant="overflow"on the+Nindicator), and four<UiDataPoint>tiles for the SIZE / FPS / CACHE / CSS specs<DetailModal>Expected: Same primitives used —
<UiBadge>for status,<UiChip>v-for for use-case tags,<UiDataPoint>for the full spec gridOverlay card grid and truncation
Expected: 1 column on narrow, 2 at 768px+, 3 at 1322px+
Expected: 3 lines max, clamped
Expected: 3
<UiChip>tags + dimmed+N<UiChip variant="overflow">indicator if more existExpected: Gold SVG corners visible at all four card corners, inlined via
<UiIcon>Detail modal
Expected:
<DetailModal>opens with full description, all tags, specs, all requirementsExpected: Modal closes via
<BaseModal>ESC handlercam-log overlay (kind-alias imports)
http://localhost:5173/@kyonax_on_tech/cam-logExpected: HUD overlay renders — identity block shows "Cristian D. Moreno" and
@kyonax_on_tech(frombrand.js, not hardcoded);<HudFrame>renders four corner brackets via<UiIcon>;<HudTimer>(using<UiStatusDot>) +<AudioMeter>+<LiveReadout>all mount and connect to OBS when availablecam-logExpected: 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)
http://localhost:5173/@kyonax_on_tech/cam-logand inspect the root<div>in devtoolsExpected: Root div carries
class="brand-kyonax-on-tech"; no inlinestyle="--clr-..."vars on the root div (theming is pure-SCSS, not JS-inline)--clr-primary-100Expected: Resolves to gold
hsl(47, 95%, 56%)— the brand override from@kyonax_on_tech/styles/_theme.scss, not the:rootdefault@kyonax_on_tech/styles/_theme.scss— change--clr-primary-100tored, saveExpected: Vite HMR pushes the update; the HUD gold accents turn red without a full reload
npm run build && grep brand-kyonax-on-tech dist/assets/*.cssExpected: Grep matches — confirming the brand theme class is inlined into the production CSS bundle
@kyonax_on_tech/brand.jsand attempt to re-add acolors: { ... }field, then runnpm run testExpected: Test fails —
brand @kyonax_on_tech does NOT carry a colors fieldassertsbrand.colors === undefinedBrand SVG multi-pool (Part B)
@kyonax_on_tech/assets/svg/test-icon.svgwithfill="currentColor"on a simple shape<UiIcon name="test-icon" :size="24" />Expected: Icon renders, inheriting the parent element's color via
currentColorsrc/shared/assets/svg/(e.g.corner-bracket.svg) in the brand folder with a visibly different shapeExpected:
<UiIcon name="corner-bracket" />renders the brand's version — brand wins the collisionESLint naming conventions
npm run lintExpected: 0 errors, 7 warnings (all pre-existing false positives in
use-audio-analyzer.jsarray indexing)snake_casevariable tocamelCasein any.jsor.vuefile, runnpm run lintExpected:
@typescript-eslint/naming-conventionerror reportedFile headers (visual)
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
Demo captures pending, will be attached after merge.
DIAGRAM — Component kind-folder architecture
See the project tree and the component classification rules in
CONTRIBUTING.org.DIAGRAM — Brand theming SCSS single source of truth (Part B)
Full flow documented in
CONTRIBUTING.orgunder the brand-theming architecture section.