Initial commit
This commit is contained in:
13
.claude-plugin/plugin.json
Normal file
13
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "fluxwing-skills",
|
||||||
|
"description": "AI-native UX design system using uxscii standard - create, validate, and compose UI designs through natural language",
|
||||||
|
"version": "0.0.2",
|
||||||
|
"author": {
|
||||||
|
"name": "Trabian",
|
||||||
|
"email": "hello@trabian.com",
|
||||||
|
"url": "https://github.com/trabian"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./skills"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# fluxwing-skills
|
||||||
|
|
||||||
|
AI-native UX design system using uxscii standard - create, validate, and compose UI designs through natural language
|
||||||
357
plugin.lock.json
Normal file
357
plugin.lock.json
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:trabian/fluxwing-skills:",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "cf36e4e0e3ac18ba2e58290268ee2bceb62e9297",
|
||||||
|
"treeHash": "a81f643ecfaf31721d8bff33d08ce8a81e7e24849aad691a3ad172e3c156532e",
|
||||||
|
"generatedAt": "2025-11-28T10:28:43.924313Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "fluxwing-skills",
|
||||||
|
"description": "AI-native UX design system using uxscii standard - create, validate, and compose UI designs through natural language",
|
||||||
|
"version": "0.0.2"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "4c3bd8bd2a4b5e724aa8b9ff12f2c30d4eefad12efb5b2a0fd3991afed57432c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "33361abe7b6ad675664b5cef19c9ab416d21b2bad722678916e9493ac1a742d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-library-browser/SKILL.md",
|
||||||
|
"sha256": "3189b4f78b8121b82db86e9160d2616b3b2186b3a736a37097a56ac4f3b6319b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-library-browser/docs/07-examples-guide.md",
|
||||||
|
"sha256": "9df337492d98bde702798e241e4186a9108c9e29fdc72bfd47d466db04ee3c55"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-library-browser/scripts/build_index.py",
|
||||||
|
"sha256": "68cc72de86fb37fe93448260459719db7fab8e4e9697933168d9b81975909d99"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-library-browser/data/template-index.json",
|
||||||
|
"sha256": "20ac073423742a6ea7f4c9d80412c2d8b3b08b418d3a999806a192eef49ea3dc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/SKILL.md",
|
||||||
|
"sha256": "c534d1fc10b1b8030093bfcfaba1e1866e298666198afa36e1035d93829a6369"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/docs/07-schema-reference.md",
|
||||||
|
"sha256": "41682e38965bc3777f52052fe62472ab763aadc9f5bccaee61fd484cd73e2624"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/docs/06-ascii-patterns.md",
|
||||||
|
"sha256": "91db0c29a54be2039f5dcb128f71646fd2201a5efa318376dafe12da8618dcb4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/docs/03-component-creation.md",
|
||||||
|
"sha256": "c5584bb413897c894c7f99eab95b7d26f73ba9db4fcccb5b1dd611da25549a9e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/schemas/uxm-component.schema.json",
|
||||||
|
"sha256": "9814452ac5286b5d94f6ea37e0e859284ecfebe4ab563f0c22c59b4dc02df1ea"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/scripts/quick_validate.py",
|
||||||
|
"sha256": "af0d8d10db41ce74d06f7c192c0a760628f33a330af0c24e004ee8a7490a8cf6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/scripts/validate_component.py",
|
||||||
|
"sha256": "2bf1f0981fea90f4cf5b7338d8ac8afa64161362d628a51189e75bc565cde9b7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/modal.md",
|
||||||
|
"sha256": "20b048deda91f1b65d650c54d2a9c217fb0ca951b956a268fe53c5637a664468"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/card.uxm",
|
||||||
|
"sha256": "691750bb0c3f744461c7d1f61f53139df9310565247e4de0359f134f128ca334"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/email-input.uxm",
|
||||||
|
"sha256": "64afcdaf457512cb33dd3d1fc3440230205a0bf90199427bbb414dd73bae2a9c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/custom-widget.uxm",
|
||||||
|
"sha256": "c1ea1ff4a61ecdbb56321e7edc82010b0b403f30fee318754c3f8d34c79ba6c9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/card.md",
|
||||||
|
"sha256": "9edc9678ac819ad4021c59decef3e1095bfbb9287c70f8fa25bcb19029802984"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/email-input.md",
|
||||||
|
"sha256": "e959ef72db20c6054933d05c39cacf67f16ce1c05054649c8f746f6d5161c4ec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/form.md",
|
||||||
|
"sha256": "b6ed936df7c89a251b14f4e1d7da8c9a91d85d9d12fdeed3104f38051452113f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/primary-button.md",
|
||||||
|
"sha256": "b03a60ecad2ecab0f22c5fc0b903b8a9b81be59798a711e7845c8074bf13cfdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/alert.uxm",
|
||||||
|
"sha256": "fa3dd537cd31d47c311169a59d3d667b318d02729f61cc5339d1602e87c1f5cf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/alert.md",
|
||||||
|
"sha256": "8378fc5968eac1b4a9062011c7acc455ab36bceb0853f3fb2d031b199c011494"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/custom-widget.md",
|
||||||
|
"sha256": "92df02c704bf585e798d7d92ab8fd2664fbe74f43801c20aecb0bef5fca3d458"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/list.uxm",
|
||||||
|
"sha256": "2f65e0e6fca608bd8591c6e257f9bf6e163673307601fd52e584a9eb755b59ec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/navigation.md",
|
||||||
|
"sha256": "563a1ca5dddd70528e2c80dc89a8a117bf3464a3fef572f494294864fd79b027"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/list.md",
|
||||||
|
"sha256": "160e27bbd1719e8bb903c7ebd0ee3ca478b26a73c7b8dfc14ac63f9764f932f9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/secondary-button.uxm",
|
||||||
|
"sha256": "eb2fd56dfc99f257d7a3ed8d541311ed24705b76559185897a0e30c8d5779618"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/secondary-button.md",
|
||||||
|
"sha256": "48c2dce074b7d4fe49820c8ffc34a255f0d66ad0f6c1c6b3f5de36a84a109f6a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/form.uxm",
|
||||||
|
"sha256": "5d8f8a1bc70c7131b10312ec1398c2cffbc87940a34731ce81f6eb14b0323f02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/badge.md",
|
||||||
|
"sha256": "b8d6d04b2aec0b0c7ba1c98b98d940c34dfb6172936946f9d2cfe98280a05ad0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/navigation.uxm",
|
||||||
|
"sha256": "4f8d5e63272a6f68ebbc2f56fec208a8ee75b90df8510e737690f74c0bd00193"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/modal.uxm",
|
||||||
|
"sha256": "73ca00a76535f9f056d4e4da6bf662ef46801e51b4f2b21e0dd8460c307817c1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/primary-button.uxm",
|
||||||
|
"sha256": "755544d4ff5ddd65f76472426fa3dead7ea199b3da6db2f5ed081c19d073f280"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/badge.uxm",
|
||||||
|
"sha256": "f4a31820f20ff8de5b9620b20aa4f5aee443ad5e2f83fa9c2e1e4238a1bcf247"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/icon.uxm.template",
|
||||||
|
"sha256": "436ca861edbad1a7d9d84fa30183c894ce575490a52c4c84772c999fae7fb21a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/list.uxm.template",
|
||||||
|
"sha256": "26257e8da98fd8768b806aea79546e9a834ed43c6eeb6157c5fda981f4ec65c3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/form.uxm.template",
|
||||||
|
"sha256": "daba14d3bb4ec4c2cfd41bd2b684f4e67da6c5eb68328ab7021d583d0025030e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/card.uxm.template",
|
||||||
|
"sha256": "eb0b557f8d197eb87d5cd38f8f312098df016e51c43d82e3ace8ab0c8e1e8978"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/navigation.uxm.template",
|
||||||
|
"sha256": "4102f41330ce73251eda05e345201197fb3a1659386772cc36bd7d91275850e9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/container.uxm.template",
|
||||||
|
"sha256": "045d2529819e4223ea69e9e17dfd30b4632adbde6b30251d19dec93f0475ff6f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/modal.uxm.template",
|
||||||
|
"sha256": "830995220ad281526f11510cfeeaeefe389defa975c03a9fc5c5b1f9a097746f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/checkbox.uxm.template",
|
||||||
|
"sha256": "bdbecdd9b3f7b26fa547ea9f8c810445060e44b1f041997d708ef5a7e6522253"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/button.uxm.template",
|
||||||
|
"sha256": "8b5054e5feaf786d85589484b8ac6e661d659456a4f2d8cca11f8681b94a210e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/badge.uxm.template",
|
||||||
|
"sha256": "bae810b8a7f0801baa3bff243dac4a54210beeb4784113dfc3f29feb93023e0c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/minimal/input.uxm.template",
|
||||||
|
"sha256": "87ac80eff8011e1d90548f2226ae194d866264a6123cd5aa7cd58695a744d42f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/state-additions/focus.json",
|
||||||
|
"sha256": "03b6ecfffee2573fb852bc2421d63355a884de7632106bc81055297e0130da0d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/state-additions/error.json",
|
||||||
|
"sha256": "ef2571645ce1ccd3302789e4ccc19397e2d2cf5bcce4d0b634d0e701804f6e9f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/state-additions/disabled.json",
|
||||||
|
"sha256": "3163eb823cb404eeeb236977f6f2a8870dfd850d2b20e2c48abaa556047be488"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-creator/templates/state-additions/hover.json",
|
||||||
|
"sha256": "5428ab15cb39e664c735cf3f4c63422d5260e57584c27725717cf1aa135f1a21"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-expander/SKILL.md",
|
||||||
|
"sha256": "788d6f3c61eb2e4cdcb223ed99f8c4bcf94c8981a517c35e92ddcfb4caa7d3bb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-expander/docs/06-ascii-patterns.md",
|
||||||
|
"sha256": "de4aa557fe8bd3e2ddde0d4f5f1aa66dc9f80df88784b993de7066a09977b8af"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-expander/docs/03-component-creation.md",
|
||||||
|
"sha256": "c5584bb413897c894c7f99eab95b7d26f73ba9db4fcccb5b1dd611da25549a9e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-viewer/SKILL.md",
|
||||||
|
"sha256": "337418fbdae32b4819b98e397fc0dfed0692f97b4c37de437e8da644ca4f9373"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-component-viewer/docs/02-core-concepts.md",
|
||||||
|
"sha256": "1cba6f41d1124070380d6f064d8546fb24171ff55bb62e9208ebed7006faf089"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-enhancer/SKILL.md",
|
||||||
|
"sha256": "149834573c4c2e7db3b9f8a21be64bd11975d97bd73a644a38d902d7af2149de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-enhancer/docs/enhancement-patterns.md",
|
||||||
|
"sha256": "6233a61565b2123799401e8a6a16bb0f82d9f343b7ec28119f1fb1dd525cad87"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/shared/docs/copy-versioning.md",
|
||||||
|
"sha256": "bed54e01aa0dfa97328fef5515a8dd335c136a5063ea926021bed8abd57f78ef"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-validator/validate-component.js",
|
||||||
|
"sha256": "bf0e4634148ba0c6171c74342fb76964915339c9191d84344b6f6a58459e7a52"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-validator/test-validator.js",
|
||||||
|
"sha256": "e566f0e4d59a5d8173bcf53391273112cbae56903939f8073898a288e4e6a4b1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-validator/README.md",
|
||||||
|
"sha256": "9773f62811518dd396d73edd2b52294d2458ddf32b2602bbb3ff6de0b8ac857a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-validator/validate-screen.js",
|
||||||
|
"sha256": "98bc32e91f74b4a4056a6e9cf31e4fcbbe0c55c6e757a281775803e3f277bc66"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-validator/test-screen-validator.js",
|
||||||
|
"sha256": "cb4811de8fce2df2905a31c8ed92ffce20e670ac3fa74882bfea8531292d9f10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-validator/package.json",
|
||||||
|
"sha256": "820fceb5fc7800f375e2b4c990cd237dce7c07913b16a0372db1361d64bf6210"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-validator/validate-batch.js",
|
||||||
|
"sha256": "0325a88fbe470f7aa3788ebe65c2ee29af553886a6e4819874f4b3ff1cf2ab9d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-validator/SKILL.md",
|
||||||
|
"sha256": "ad7fd21768e8f3eca7e3d68d57abd89f643bd2cd7eca53431529bf6ef1a94302"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screen-scaffolder/SKILL.md",
|
||||||
|
"sha256": "33e3b70202f191516185b329642267e47a2cfbf2d66bc198acfb08372c781f27"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screen-scaffolder/docs/04-screen-composition.md",
|
||||||
|
"sha256": "1967703beacec97f178d424a4d0601c1d8e99270ae3650a44c0fe639ee95355c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screen-scaffolder/templates/dashboard.uxm",
|
||||||
|
"sha256": "a50a188cd966e5eb7977ab9100cc05ac3ddeb59abe32c9167292b74b20dca0a4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screen-scaffolder/templates/dashboard.md",
|
||||||
|
"sha256": "d77dbec1d2b8500474b23eabf279810200ed01645d57c1c8f5d2a4cd8acb7b44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screen-scaffolder/templates/login-screen.md",
|
||||||
|
"sha256": "2d959597f4f7a579387093b46742f9104ab02cfc68381160a3566fb4def96121"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screen-scaffolder/templates/dashboard.rendered.md",
|
||||||
|
"sha256": "febf80a4720b9282307677507c47db18f8e58211155ed79d0fbac953c5197bc5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screen-scaffolder/templates/login-screen.rendered.md",
|
||||||
|
"sha256": "1a848cf6cc248f88ea01e583947f5558199a376a34c422afa788be0e4aaa198e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screen-scaffolder/templates/login-screen.uxm",
|
||||||
|
"sha256": "ce2021f61854c7bfb33e4611008c5dc35608c3dfd637debe8d2905799fc5e744"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screenshot-importer/SKILL.md",
|
||||||
|
"sha256": "04eb7ae936c5d37ab70e021b32af02a333e5749f089d1be6f748170b4934c4be"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screenshot-importer/docs/screenshot-import-ascii.md",
|
||||||
|
"sha256": "64dcab9d45cdc5825aa8d5b3db8863c441e1fc68c303e32b0dcb7221c79a6790"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screenshot-importer/docs/screenshot-validation-functions.md",
|
||||||
|
"sha256": "50c9b2911b63913db1797d6dae2b5209384c4d3f6e9f5ba79ff79af0ad307798"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screenshot-importer/docs/screenshot-import-examples.md",
|
||||||
|
"sha256": "bb8d0415fecb971da739b84c0e41b8d93c6cb65cd779faf10ea30ec3ffa50f44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screenshot-importer/docs/screenshot-data-merging.md",
|
||||||
|
"sha256": "fe36da33cfd13ee0dace575bc8b306f22aa99eb7fd336bee63b3c95f81aa6dec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screenshot-importer/docs/screenshot-screen-generation.md",
|
||||||
|
"sha256": "aa8497cfc0a8ea96ec591402ed55af9b48fa3c57f95844d9c07e94dbb21f9dd0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/fluxwing-screenshot-importer/docs/screenshot-import-helpers.md",
|
||||||
|
"sha256": "db10b2c973553afda01e4f01d8576721b06ef7a048cb8df397966201daf68ee5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "a81f643ecfaf31721d8bff33d08ce8a81e7e24849aad691a3ad172e3c156532e"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
742
skills/fluxwing-component-creator/SKILL.md
Normal file
742
skills/fluxwing-component-creator/SKILL.md
Normal file
@@ -0,0 +1,742 @@
|
|||||||
|
---
|
||||||
|
name: Fluxwing Component Creator
|
||||||
|
description: Create uxscii components with ASCII art and structured metadata when user wants to create, build, or design UI components. Use when working with .uxm files, when user mentions .uxm components, or when creating buttons, inputs, cards, forms, modals, or navigation.
|
||||||
|
version: 0.0.1
|
||||||
|
author: Trabian
|
||||||
|
allowed-tools: Read, Write, Edit, Glob, Grep, Task, TodoWrite, Bash
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fluxwing Component Creator
|
||||||
|
|
||||||
|
You are helping the user create uxscii component(s) using the **uxscii standard** by orchestrating the designer agent.
|
||||||
|
|
||||||
|
## Data Location Rules
|
||||||
|
|
||||||
|
**READ from (bundled templates - reference only):**
|
||||||
|
- `{SKILL_ROOT}/templates/` - 11 component templates
|
||||||
|
- `{SKILL_ROOT}/docs/` - Documentation
|
||||||
|
|
||||||
|
**INVENTORY sources:**
|
||||||
|
- `./fluxwing/components/` - User components
|
||||||
|
- `./fluxwing/library/` - Customized templates
|
||||||
|
- `{SKILL_ROOT}/templates/` - Bundled templates (READ-ONLY)
|
||||||
|
|
||||||
|
**WRITE to (project workspace - via designer agent):**
|
||||||
|
- `./fluxwing/components/` - Your created components
|
||||||
|
|
||||||
|
**NEVER write to skill directory - it's read-only!**
|
||||||
|
|
||||||
|
**LOAD for copy-on-update logic:**
|
||||||
|
- `{SKILL_ROOT}/../shared/docs/copy-versioning.md` - Versioning pattern for existing components
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
Help the user create uxscii component(s) by gathering requirements and spawning designer agent(s).
|
||||||
|
|
||||||
|
**Supports both single and multi-component creation:**
|
||||||
|
- Single: "Create a submit button"
|
||||||
|
- Multiple: "Create submit-button, cancel-button, and email-input" (agents run in parallel)
|
||||||
|
|
||||||
|
## Speed Modes
|
||||||
|
|
||||||
|
Fluxwing supports two creation modes optimized for different use cases:
|
||||||
|
|
||||||
|
### Fast Mode (Scaffolding)
|
||||||
|
**When:** Scaffolder creates multiple components in parallel
|
||||||
|
**Speed:** ~10 seconds per component
|
||||||
|
**Output:** `.uxm` only (fidelity: sketch)
|
||||||
|
**Method:** Template-based variable substitution
|
||||||
|
|
||||||
|
Fast mode skips:
|
||||||
|
- Documentation loading
|
||||||
|
- ASCII art generation
|
||||||
|
- Detailed metadata
|
||||||
|
|
||||||
|
### Detailed Mode (Standalone)
|
||||||
|
**When:** User explicitly creates single component
|
||||||
|
**Speed:** ~60-90 seconds per component
|
||||||
|
**Output:** `.uxm` + `.md` (fidelity: detailed)
|
||||||
|
**Method:** Full docs, careful ASCII generation
|
||||||
|
|
||||||
|
Detailed mode includes:
|
||||||
|
- Complete documentation reference
|
||||||
|
- Hand-crafted ASCII art
|
||||||
|
- Rich metadata with examples
|
||||||
|
|
||||||
|
**Default:** Fast mode when called by scaffolder, detailed mode otherwise
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### Step 1: Determine Creation Mode & Parse Request
|
||||||
|
|
||||||
|
**First, determine creation mode:**
|
||||||
|
|
||||||
|
Check context to decide fast vs detailed mode:
|
||||||
|
|
||||||
|
**Use Fast Mode if:**
|
||||||
|
- Called by scaffolder skill (check for "screen context" in request)
|
||||||
|
- User explicitly requests "fast" or "quick" component
|
||||||
|
- Creating multiple components (6+ components)
|
||||||
|
|
||||||
|
**Use Detailed Mode if:**
|
||||||
|
- User creating single component interactively
|
||||||
|
- User requests "detailed" or "production" quality
|
||||||
|
- No screen context provided
|
||||||
|
|
||||||
|
**Default:** Detailed mode (safer, better quality)
|
||||||
|
|
||||||
|
**Then, detect if user wants single or multiple components:**
|
||||||
|
|
||||||
|
1. **Single component request**:
|
||||||
|
- "Create a submit button"
|
||||||
|
- "I need a card component"
|
||||||
|
- Proceed with single-component workflow (Steps 2-4)
|
||||||
|
|
||||||
|
2. **Multiple component request**:
|
||||||
|
- "Create submit-button, cancel-button, and email-input"
|
||||||
|
- "I need a button, input, and card"
|
||||||
|
- "Create these components: [list]"
|
||||||
|
- Proceed with multi-component workflow (see Step 3b)
|
||||||
|
|
||||||
|
**For each component, gather:**
|
||||||
|
- **Component name** (will be converted to kebab-case, e.g., "Submit Button" → "submit-button")
|
||||||
|
- **Component type**: button, input, card, navigation, form, list, modal, table, badge, alert, container, text, image, divider, or custom
|
||||||
|
- **Key properties**: What should be configurable? (text, colors, sizes, etc.)
|
||||||
|
- **Visual style preferences**: rounded, sharp, minimal, detailed, etc.
|
||||||
|
|
||||||
|
**If details are missing**: Make reasonable assumptions based on component type and common patterns. Don't over-ask.
|
||||||
|
|
||||||
|
**Note**: Components are created with default state only for fast MVP prototyping. Users can expand components later to add interactive states (hover, focus, disabled, etc.).
|
||||||
|
|
||||||
|
### Step 2: Check for Similar Templates
|
||||||
|
|
||||||
|
Browse available templates to offer starting points:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Check bundled templates
|
||||||
|
const bundledTemplates = glob('{SKILL_ROOT}/templates/*.uxm');
|
||||||
|
// Check user library
|
||||||
|
const libraryTemplates = glob('./fluxwing/library/*.uxm');
|
||||||
|
|
||||||
|
// Suggest similar templates if found
|
||||||
|
if (similarTemplates.length > 0) {
|
||||||
|
console.log(`Similar templates found: ${similarTemplates.join(', ')}`);
|
||||||
|
console.log('Would you like to base this on an existing template or create from scratch?');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Pre-Creation Validation (Check for Existing Component)
|
||||||
|
|
||||||
|
**IMPORTANT**: Before creating any component, check if it already exists to prevent data loss.
|
||||||
|
|
||||||
|
**For each component you plan to create:**
|
||||||
|
|
||||||
|
1. **Convert to kebab-case ID**:
|
||||||
|
```
|
||||||
|
"Submit Button" → "submit-button"
|
||||||
|
"User Profile Card" → "user-profile-card"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check if component exists**:
|
||||||
|
```bash
|
||||||
|
# Check for existing component
|
||||||
|
test -f ./fluxwing/components/{component-id}.uxm
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **If component EXISTS**:
|
||||||
|
|
||||||
|
**Inform user and offer choices**:
|
||||||
|
```
|
||||||
|
Component '{component-id}' already exists (version {current-version}).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
(a) Create new version (copy-on-update: {component-id}-v{N+1})
|
||||||
|
(b) Create with different name
|
||||||
|
(c) Cancel operation
|
||||||
|
|
||||||
|
What would you like to do?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Handle user response**:
|
||||||
|
|
||||||
|
- **Choice (a) - Create new version**:
|
||||||
|
1. Load copy-versioning logic from `{SKILL_ROOT}/../shared/docs/copy-versioning.md`
|
||||||
|
2. Read existing `{component-id}.uxm`
|
||||||
|
3. Find highest version (check for `{component-id}-v2`, `-v3`, etc.)
|
||||||
|
4. Calculate next version: `v{N+1}`
|
||||||
|
5. Pass to designer agent with versioning parameters:
|
||||||
|
- `baseComponentId`: Original ID (e.g., "submit-button")
|
||||||
|
- `newComponentId`: Versioned ID (e.g., "submit-button-v2")
|
||||||
|
- `baseOnExisting`: true
|
||||||
|
- `sourceVersion`: Highest existing version
|
||||||
|
6. Designer creates `{component-id}-v{N+1}.uxm` and `.md`
|
||||||
|
7. Metadata: Increment minor version (1.0.0 → 1.1.0), update modified, preserve created
|
||||||
|
|
||||||
|
- **Choice (b) - Different name**:
|
||||||
|
1. Ask: "What would you like to name this component?"
|
||||||
|
2. Wait for user response
|
||||||
|
3. Use new name for component ID
|
||||||
|
4. Proceed with normal creation
|
||||||
|
|
||||||
|
- **Choice (c) - Cancel**:
|
||||||
|
1. Do not create any files
|
||||||
|
2. Inform user: "Operation cancelled. No files were created."
|
||||||
|
3. Exit workflow
|
||||||
|
|
||||||
|
4. **If component DOES NOT exist**:
|
||||||
|
- Proceed with normal creation workflow (no versioning needed)
|
||||||
|
- Component will be created as `{component-id}.uxm` (no version suffix)
|
||||||
|
- Initial version: 1.0.0
|
||||||
|
|
||||||
|
**For multiple components**: Check existence for EACH component individually. Some may need versioning, others may not.
|
||||||
|
|
||||||
|
## Agent Prompts
|
||||||
|
|
||||||
|
### Fast Mode Agent (For Scaffolder)
|
||||||
|
|
||||||
|
Use this when creating multiple components quickly:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
// Note: model parameter not yet supported by Task tool
|
||||||
|
description: "Create ${componentName} (fast)",
|
||||||
|
prompt: `Create sketch-fidelity uxscii component from template.
|
||||||
|
|
||||||
|
Component: ${componentName}
|
||||||
|
Type: ${componentType}
|
||||||
|
Screen context: ${screenContext}
|
||||||
|
${baseOnExisting ? `
|
||||||
|
VERSIONING MODE:
|
||||||
|
- Base on existing: ${baseComponentId}
|
||||||
|
- New component ID: ${newComponentId}
|
||||||
|
- Source version: ${sourceVersion}
|
||||||
|
- Copy-on-update: Increment minor version, preserve created timestamp
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
FAST MODE - Speed is critical! <10 seconds target.
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. ${baseOnExisting ? `Load existing: ./fluxwing/components/${sourceVersion}.uxm` : `Load minimal template: {SKILL_ROOT}/templates/minimal/${componentType}.uxm.template`}
|
||||||
|
2. ${baseOnExisting ? `Read current version number and metadata.created timestamp` : `If template not found, FAIL with error: "No template found for type: ${componentType}"`}
|
||||||
|
3. Replace template variables (component type specific):
|
||||||
|
|
||||||
|
**Common variables (all types):**
|
||||||
|
- {{id}} = "${componentId}"
|
||||||
|
- {{name}} = "${componentName}"
|
||||||
|
- {{description}} = "${description || 'Component for ' + screenContext}"
|
||||||
|
- {{timestamp}} = "${new Date().toISOString()}"
|
||||||
|
|
||||||
|
**Component-specific variables:**
|
||||||
|
| Type | Variables |
|
||||||
|
|------------|-------------------------------------------|
|
||||||
|
| button | {{label}}, {{variant}} |
|
||||||
|
| input | {{placeholder}}, {{type}}, {{value}} |
|
||||||
|
| text | {{content}}, {{align}} |
|
||||||
|
| heading | {{text}}, {{level}} |
|
||||||
|
| card | {{title}}, {{content}} |
|
||||||
|
| modal | {{title}}, {{content}} |
|
||||||
|
| container | {{content}}, {{direction}} |
|
||||||
|
| navigation | {{items}}, {{orientation}} |
|
||||||
|
| form | {{fields}}, {{action}} |
|
||||||
|
| table | {{headers}}, {{rows}} |
|
||||||
|
| list | {{items}}, {{type}} |
|
||||||
|
|
||||||
|
Use component name as default value if variable not provided.
|
||||||
|
|
||||||
|
4. CRITICAL: Set metadata.fidelity = "sketch"
|
||||||
|
|
||||||
|
**REQUIRED FIELD**: The fidelity field is MANDATORY in the schema and tracks progressive enhancement.
|
||||||
|
Fast mode MUST set fidelity to "sketch" to indicate initial scaffolding quality.
|
||||||
|
|
||||||
|
This field enables progressive fidelity workflow:
|
||||||
|
- sketch (fast mode) → basic → detailed → production
|
||||||
|
|
||||||
|
5. ${baseOnExisting ? `Update version metadata:
|
||||||
|
- id: "${newComponentId}" (versioned ID)
|
||||||
|
- version: Increment minor from source (e.g., 1.0.0 → 1.1.0)
|
||||||
|
- metadata.created: PRESERVE from ${sourceVersion}
|
||||||
|
- metadata.modified: SET to current timestamp
|
||||||
|
` : `Verify JSON is well-formed (quick syntax check)`}
|
||||||
|
6. Save to ./fluxwing/components/${componentId}.uxm
|
||||||
|
7. DO NOT create .md file
|
||||||
|
8. DO NOT load documentation
|
||||||
|
9. DO NOT generate ASCII art
|
||||||
|
|
||||||
|
VERIFICATION CHECKLIST:
|
||||||
|
- [ ] metadata.fidelity field is set to "sketch"
|
||||||
|
- [ ] All required fields are present (name, description, created, modified, tags, category, fidelity)
|
||||||
|
- [ ] JSON is valid and well-formed
|
||||||
|
|
||||||
|
Return message: "Created ${componentId}.uxm (sketch fidelity)"
|
||||||
|
|
||||||
|
Target: <10 seconds
|
||||||
|
`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detailed Mode Agent (For User)
|
||||||
|
|
||||||
|
Use this when creating single component with full quality:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
// Note: model parameter not yet supported by Task tool
|
||||||
|
description: "Create ${componentName} (detailed)",
|
||||||
|
prompt: `Create production-ready uxscii component with full documentation.
|
||||||
|
|
||||||
|
Component: ${componentName}
|
||||||
|
Type: ${componentType}
|
||||||
|
${baseOnExisting ? `
|
||||||
|
VERSIONING MODE:
|
||||||
|
- Base on existing: ${baseComponentId}
|
||||||
|
- New component ID: ${newComponentId}
|
||||||
|
- Source version: ${sourceVersion}
|
||||||
|
- Copy-on-update: Increment minor version, preserve created timestamp
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
DETAILED MODE - Quality is priority.
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. ${baseOnExisting ? `Load existing: ./fluxwing/components/${sourceVersion}.uxm` : `Load schema: {SKILL_ROOT}/schemas/uxm-component.schema.json`}
|
||||||
|
2. ${baseOnExisting ? `Read copy-versioning docs: {SKILL_ROOT}/../shared/docs/copy-versioning.md` : `Load docs: {SKILL_ROOT}/docs/03-component-creation.md`}
|
||||||
|
3. Load ASCII patterns: {SKILL_ROOT}/docs/06-ascii-patterns.md
|
||||||
|
4. Create rich .uxm with:
|
||||||
|
- Detailed metadata.description
|
||||||
|
- Relevant tags
|
||||||
|
- Complete props with examples
|
||||||
|
- Default + hover states
|
||||||
|
- Full accessibility metadata
|
||||||
|
|
||||||
|
5. CRITICAL: Set metadata.fidelity = "detailed"
|
||||||
|
|
||||||
|
**REQUIRED FIELD**: The fidelity field is MANDATORY in the schema and tracks progressive enhancement.
|
||||||
|
Detailed mode MUST set fidelity to "detailed" to indicate high-quality production-ready components.
|
||||||
|
|
||||||
|
This field enables progressive fidelity workflow:
|
||||||
|
- sketch → basic → detailed (detailed mode) → production
|
||||||
|
|
||||||
|
6. ${baseOnExisting ? `Update version metadata:
|
||||||
|
- id: "${newComponentId}" (versioned ID with -v{N} suffix)
|
||||||
|
- version: Increment minor from source (e.g., 1.0.0 → 1.1.0)
|
||||||
|
- metadata.created: PRESERVE from ${sourceVersion}
|
||||||
|
- metadata.modified: SET to current timestamp
|
||||||
|
- metadata.fidelity: Update if enhancing (preserve or upgrade)
|
||||||
|
` : `Verify JSON is well-formed`}
|
||||||
|
|
||||||
|
7. Create polished .md with:
|
||||||
|
- Clean ASCII art using box-drawing characters
|
||||||
|
- All variables documented
|
||||||
|
- State examples
|
||||||
|
- ${baseOnExisting ? `Filename: ${newComponentId}.md (versioned)` : `Filename: ${componentId}.md`}
|
||||||
|
|
||||||
|
8. Validate against schema
|
||||||
|
9. Save both files to ./fluxwing/components/
|
||||||
|
- ${baseOnExisting ? `${newComponentId}.uxm and ${newComponentId}.md` : `${componentId}.uxm and ${componentId}.md`}
|
||||||
|
|
||||||
|
VERIFICATION CHECKLIST:
|
||||||
|
- [ ] metadata.fidelity field is set to "detailed"
|
||||||
|
- [ ] All required fields are present (name, description, created, modified, tags, category, fidelity)
|
||||||
|
- [ ] Both .uxm and .md files are created
|
||||||
|
- [ ] ${baseOnExisting ? `Version incremented and ID has -v{N} suffix` : `Component ID is kebab-case`}
|
||||||
|
- [ ] ${baseOnExisting ? `metadata.created preserved from source` : `metadata.created set to current timestamp`}
|
||||||
|
- [ ] JSON is valid and well-formed
|
||||||
|
|
||||||
|
Return: Component summary with preview ${baseOnExisting ? `- Mention version created` : ``}
|
||||||
|
|
||||||
|
Target: 60-90 seconds
|
||||||
|
`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3a: Spawn Designer Agent (Single Component)
|
||||||
|
|
||||||
|
**For SINGLE component requests**, spawn one designer agent:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Create single uxscii component",
|
||||||
|
prompt: `You are a uxscii component designer creating production-ready components.
|
||||||
|
|
||||||
|
Component requirements:
|
||||||
|
- Name: ${componentName}
|
||||||
|
- Type: ${componentType}
|
||||||
|
- Key properties: ${keyProperties}
|
||||||
|
- Visual style: ${visualStyle}
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load schema from {SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
2. Load documentation from {SKILL_ROOT}/docs/03-component-creation.md and 06-ascii-patterns.md
|
||||||
|
3. Check {SKILL_ROOT}/templates/ for similar examples
|
||||||
|
4. Create .uxm file (valid JSON with default state only)
|
||||||
|
5. Create .md file (ASCII template with default state only)
|
||||||
|
6. Save both files to ./fluxwing/components/
|
||||||
|
7. Validate using: node {SKILL_ROOT}/../fluxwing-validator/validate-component.js ./fluxwing/components/${componentId}.uxm {SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
8. Use TodoWrite to track progress
|
||||||
|
9. Return component summary with ASCII preview
|
||||||
|
|
||||||
|
Component creation guidelines:
|
||||||
|
- Create default state only for fast MVP prototyping
|
||||||
|
- Use consistent box-drawing characters (see docs/06-ascii-patterns.md)
|
||||||
|
- Include complete accessibility attributes (ARIA roles, keyboard support)
|
||||||
|
- Follow naming conventions: kebab-case IDs, camelCase variables
|
||||||
|
- Ensure all template variables in .md are defined in .uxm props
|
||||||
|
- Keep ASCII dimensions reasonable (width: 1-120, height: 1-50)
|
||||||
|
|
||||||
|
Data locations:
|
||||||
|
- READ templates from: {SKILL_ROOT}/templates/ (reference only)
|
||||||
|
- WRITE components to: ./fluxwing/components/ (your output)
|
||||||
|
- NEVER write to skill directory
|
||||||
|
|
||||||
|
Follow the uxscii standard strictly for production quality.`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wait for designer agent to complete.**
|
||||||
|
|
||||||
|
### Step 3b: Spawn Designer Agents (Multiple Components - IN PARALLEL)
|
||||||
|
|
||||||
|
**For MULTIPLE component requests**, spawn ALL designer agents in a SINGLE message for maximum parallelism:
|
||||||
|
|
||||||
|
**CRITICAL**: You MUST send ONE message with multiple Task calls to achieve parallel execution.
|
||||||
|
|
||||||
|
**DO THIS**: One message with N Task calls (one per component)
|
||||||
|
**DON'T DO THIS**: Separate messages for each component (runs sequentially)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example: User wants submit-button, cancel-button, email-input
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Create submit-button component",
|
||||||
|
prompt: `You are a uxscii component designer creating production-ready components.
|
||||||
|
|
||||||
|
Component requirements:
|
||||||
|
- Name: submit-button
|
||||||
|
- Type: button
|
||||||
|
- Key properties: text, variant
|
||||||
|
- Visual style: rounded, filled
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load schema from {SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
2. Load docs from {SKILL_ROOT}/docs/03-component-creation.md and 06-ascii-patterns.md
|
||||||
|
3. Create .uxm file (valid JSON with default state only)
|
||||||
|
4. Create .md file (ASCII template with default state only)
|
||||||
|
5. Save to ./fluxwing/components/
|
||||||
|
6. Validate using: node {SKILL_ROOT}/../fluxwing-validator/validate-component.js
|
||||||
|
7. Return component summary
|
||||||
|
|
||||||
|
Follow uxscii standard strictly. Create default state only for fast MVP.`
|
||||||
|
})
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Create cancel-button component",
|
||||||
|
prompt: `You are a uxscii component designer creating production-ready components.
|
||||||
|
|
||||||
|
Component requirements:
|
||||||
|
- Name: cancel-button
|
||||||
|
- Type: button
|
||||||
|
- Key properties: text, variant
|
||||||
|
- Visual style: rounded, outlined
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load schema from {SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
2. Load docs from {SKILL_ROOT}/docs/03-component-creation.md and 06-ascii-patterns.md
|
||||||
|
3. Create .uxm file (valid JSON with default state only)
|
||||||
|
4. Create .md file (ASCII template with default state only)
|
||||||
|
5. Save to ./fluxwing/components/
|
||||||
|
6. Validate using: node {SKILL_ROOT}/../fluxwing-validator/validate-component.js
|
||||||
|
7. Return component summary
|
||||||
|
|
||||||
|
Follow uxscii standard strictly. Create default state only for fast MVP.`
|
||||||
|
})
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Create email-input component",
|
||||||
|
prompt: `You are a uxscii component designer creating production-ready components.
|
||||||
|
|
||||||
|
Component requirements:
|
||||||
|
- Name: email-input
|
||||||
|
- Type: input
|
||||||
|
- Key properties: placeholder, value
|
||||||
|
- Visual style: light border, minimal
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load schema from {SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
2. Load docs from {SKILL_ROOT}/docs/03-component-creation.md and 06-ascii-patterns.md
|
||||||
|
3. Create .uxm file (valid JSON with default state only)
|
||||||
|
4. Create .md file (ASCII template with default state only)
|
||||||
|
5. Save to ./fluxwing/components/
|
||||||
|
6. Validate using: node {SKILL_ROOT}/../fluxwing-validator/validate-component.js
|
||||||
|
7. Return component summary
|
||||||
|
|
||||||
|
Follow uxscii standard strictly. Create default state only for fast MVP.`
|
||||||
|
})
|
||||||
|
|
||||||
|
... all Task calls in the SAME message for parallel execution ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wait for ALL designer agents to complete.**
|
||||||
|
|
||||||
|
**Performance Benefit**: Creating 3 components in parallel is ~3x faster than sequential creation!
|
||||||
|
|
||||||
|
### Step 3c: Validate Created Components (Optional but Recommended)
|
||||||
|
|
||||||
|
After the designer agent(s) complete, validate the created components using the fast validation script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For single component
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-component.js \\
|
||||||
|
./fluxwing/components/${componentId}.uxm \\
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
|
||||||
|
# For multiple components, validate each one
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-component.js \\
|
||||||
|
./fluxwing/components/submit-button.uxm \\
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-component.js \\
|
||||||
|
./fluxwing/components/cancel-button.uxm \\
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation output:**
|
||||||
|
- ✓ If valid: Shows component summary (type, states, props)
|
||||||
|
- ✗ If invalid: Shows specific errors that need fixing
|
||||||
|
|
||||||
|
**Performance**: ~80ms per component (very fast!)
|
||||||
|
|
||||||
|
**If validation fails:**
|
||||||
|
1. Read the error messages carefully
|
||||||
|
2. Fix the issues in the .uxm or .md files
|
||||||
|
3. Re-validate before reporting to user
|
||||||
|
|
||||||
|
**Note**: The validation script checks:
|
||||||
|
- JSON schema compliance
|
||||||
|
- .md file exists
|
||||||
|
- Variables match between .uxm and .md
|
||||||
|
- Accessibility requirements
|
||||||
|
- Dimension constraints
|
||||||
|
|
||||||
|
### Step 4: Report Success
|
||||||
|
|
||||||
|
**For SINGLE component**, present the results:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Component Created ✓
|
||||||
|
|
||||||
|
## ${componentName}
|
||||||
|
|
||||||
|
**Type**: ${componentType}
|
||||||
|
**Files**:
|
||||||
|
- ./fluxwing/components/${componentId}.uxm
|
||||||
|
- ./fluxwing/components/${componentId}.md
|
||||||
|
|
||||||
|
**States**: default (created)
|
||||||
|
**Detected states**: ${detectedStates.join(', ')}
|
||||||
|
|
||||||
|
## Preview
|
||||||
|
|
||||||
|
${asciiPreview}
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Add interaction states using the component expander skill
|
||||||
|
2. Use in a screen with the screen scaffolder skill
|
||||||
|
3. View all components with the library browser skill
|
||||||
|
4. Customize: Edit files in ./fluxwing/components/
|
||||||
|
```
|
||||||
|
|
||||||
|
**For MULTIPLE components**, present a summary:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Components Created ✓
|
||||||
|
|
||||||
|
Created ${componentCount} components in parallel:
|
||||||
|
|
||||||
|
## submit-button (button)
|
||||||
|
**Files**:
|
||||||
|
- ./fluxwing/components/submit-button.uxm
|
||||||
|
- ./fluxwing/components/submit-button.md
|
||||||
|
|
||||||
|
**Preview**:
|
||||||
|
╭──────────────────╮
|
||||||
|
│ Submit Form │
|
||||||
|
╰──────────────────╯
|
||||||
|
|
||||||
|
## cancel-button (button)
|
||||||
|
**Files**:
|
||||||
|
- ./fluxwing/components/cancel-button.uxm
|
||||||
|
- ./fluxwing/components/cancel-button.md
|
||||||
|
|
||||||
|
**Preview**:
|
||||||
|
┌──────────────────┐
|
||||||
|
│ Cancel │
|
||||||
|
└──────────────────┘
|
||||||
|
|
||||||
|
## email-input (input)
|
||||||
|
**Files**:
|
||||||
|
- ./fluxwing/components/email-input.uxm
|
||||||
|
- ./fluxwing/components/email-input.md
|
||||||
|
|
||||||
|
**Preview**:
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Email: [________________] │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Total**: 6 files created (3 .uxm + 3 .md)
|
||||||
|
**Performance**: Created in parallel (~3x faster than sequential)
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Add interaction states to components
|
||||||
|
2. Use components in screens
|
||||||
|
3. View all components
|
||||||
|
4. Customize: Edit files in ./fluxwing/components/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Interactions
|
||||||
|
|
||||||
|
### Example 1: Single Component
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Create a submit button
|
||||||
|
|
||||||
|
Skill: I'll create a submit button component! Let me check for similar templates...
|
||||||
|
|
||||||
|
[Checks bundled templates]
|
||||||
|
|
||||||
|
I found a primary-button template. Would you like me to:
|
||||||
|
1. Base it on the primary-button template (faster)
|
||||||
|
2. Create a custom design from scratch
|
||||||
|
|
||||||
|
[User responds: custom, rounded corners, filled]
|
||||||
|
|
||||||
|
Perfect! Creating submit-button component with the designer agent...
|
||||||
|
|
||||||
|
[Spawns designer agent]
|
||||||
|
|
||||||
|
✓ Component created successfully!
|
||||||
|
|
||||||
|
Files:
|
||||||
|
- ./fluxwing/components/submit-button.uxm
|
||||||
|
- ./fluxwing/components/submit-button.md
|
||||||
|
|
||||||
|
Preview (default state):
|
||||||
|
╭──────────────────╮
|
||||||
|
│ Submit Form │
|
||||||
|
╰──────────────────╯
|
||||||
|
|
||||||
|
Next steps:
|
||||||
|
- Add states: Ask me to "add hover state to submit-button"
|
||||||
|
- Use in screen: Ask me to "create a login screen"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Multiple Components (Parallel)
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Create submit-button, cancel-button, and email-input
|
||||||
|
|
||||||
|
Skill: I'll create all 3 components in parallel! This will be much faster.
|
||||||
|
|
||||||
|
Based on the component types, I'll use these defaults:
|
||||||
|
- submit-button: button type, rounded, filled style
|
||||||
|
- cancel-button: button type, rounded, outlined style
|
||||||
|
- email-input: input type, light border, minimal style
|
||||||
|
|
||||||
|
[Spawns 3 designer agents in a SINGLE message]
|
||||||
|
|
||||||
|
✓ All 3 components created successfully in parallel!
|
||||||
|
|
||||||
|
## submit-button (button)
|
||||||
|
Files: submit-button.uxm + submit-button.md
|
||||||
|
|
||||||
|
## cancel-button (button)
|
||||||
|
Files: cancel-button.uxm + cancel-button.md
|
||||||
|
|
||||||
|
## email-input (input)
|
||||||
|
Files: email-input.uxm + email-input.md
|
||||||
|
|
||||||
|
Total: 6 files created
|
||||||
|
Performance: ~3x faster than sequential creation
|
||||||
|
|
||||||
|
Next steps:
|
||||||
|
- Add states to components
|
||||||
|
- Use in a screen
|
||||||
|
- View all components
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benefits of Using Designer Agent
|
||||||
|
|
||||||
|
- **Consistent quality**: Agent follows quality standards
|
||||||
|
- **Complete metadata**: Proper tags, descriptions, accessibility
|
||||||
|
- **Best practices**: Schema compliance, naming conventions
|
||||||
|
- **Reusable logic**: Same creation workflow across all skills
|
||||||
|
- **Parallel execution**: Multiple components created simultaneously (not sequentially)
|
||||||
|
|
||||||
|
## Performance Benefits
|
||||||
|
|
||||||
|
**Single vs Multi-Component Creation:**
|
||||||
|
|
||||||
|
- **Sequential (old)**: Component 1 → wait → Component 2 → wait → Component 3
|
||||||
|
- Time: 3 × agent_time
|
||||||
|
|
||||||
|
- **Parallel (new)**: Component 1 + Component 2 + Component 3 → all at once
|
||||||
|
- Time: 1 × agent_time (3x faster!)
|
||||||
|
|
||||||
|
**When to use multi-component creation:**
|
||||||
|
- Creating multiple components for a single screen (e.g., login form components)
|
||||||
|
- Building a component library in bulk
|
||||||
|
- Prototyping quickly with several variations
|
||||||
|
|
||||||
|
**Example speedup:**
|
||||||
|
- 1 component: ~30 seconds
|
||||||
|
- 3 components sequential: ~90 seconds
|
||||||
|
- 3 components parallel: ~30 seconds (3x faster!) ⚡
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**If single designer agent fails:**
|
||||||
|
- Report specific error from agent
|
||||||
|
- Suggest fixes or alternatives
|
||||||
|
- User can retry with adjusted requirements
|
||||||
|
|
||||||
|
**If multiple designer agents fail:**
|
||||||
|
- Report which components succeeded and which failed
|
||||||
|
- Keep successfully created components (partial success is OK)
|
||||||
|
- User can retry failed components individually or as a group
|
||||||
|
|
||||||
|
**Example partial failure:**
|
||||||
|
```
|
||||||
|
✓ submit-button created successfully
|
||||||
|
✓ cancel-button created successfully
|
||||||
|
✗ email-input failed: Invalid component type specified
|
||||||
|
|
||||||
|
2 of 3 components created. You can:
|
||||||
|
1. Retry email-input with corrected parameters
|
||||||
|
2. Use the 2 successful components as-is
|
||||||
|
3. Manually create email-input
|
||||||
|
```
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
**For single component:**
|
||||||
|
- ✓ Designer agent created component successfully
|
||||||
|
- ✓ Both .uxm and .md files exist in ./fluxwing/components/
|
||||||
|
- ✓ Component follows uxscii standard
|
||||||
|
- ✓ User can immediately use or expand the component
|
||||||
|
|
||||||
|
**For multiple components:**
|
||||||
|
- ✓ All designer agents launched in parallel (single message)
|
||||||
|
- ✓ Each component has both .uxm and .md files in ./fluxwing/components/
|
||||||
|
- ✓ All components follow uxscii standard
|
||||||
|
- ✓ Clear report showing which succeeded/failed (if any failures)
|
||||||
|
- ✓ User can immediately use all successful components
|
||||||
|
|
||||||
|
You are helping build AI-native designs with production-quality components at maximum speed!
|
||||||
294
skills/fluxwing-component-creator/docs/03-component-creation.md
Normal file
294
skills/fluxwing-component-creator/docs/03-component-creation.md
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
# Component Creation - Step-by-Step Workflow
|
||||||
|
|
||||||
|
Complete guide for creating uxscii components from scratch.
|
||||||
|
|
||||||
|
## Before You Start
|
||||||
|
|
||||||
|
1. **Understand the concept** - Read `02-core-concepts.md` if you haven't
|
||||||
|
2. **Check examples** - Browse `../examples/` for similar components
|
||||||
|
3. **Plan your component** - Know what you're building
|
||||||
|
|
||||||
|
## Step-by-Step Process
|
||||||
|
|
||||||
|
### Step 1: Plan Your Component
|
||||||
|
|
||||||
|
Answer these questions:
|
||||||
|
- **What is it?** Button, input, card, modal, etc.
|
||||||
|
- **What does it do?** Primary purpose and use case
|
||||||
|
- **What's configurable?** Props users can change
|
||||||
|
- **What states does it have?** Default, hover, focus, disabled, error, success
|
||||||
|
- **How will it look?** Sketch ASCII layout
|
||||||
|
|
||||||
|
### Step 2: Create the .uxm File
|
||||||
|
|
||||||
|
Start with required fields:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "my-component",
|
||||||
|
"type": "button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "My Component",
|
||||||
|
"created": "2024-10-11T12:00:00Z",
|
||||||
|
"modified": "2024-10-11T12:00:00Z"
|
||||||
|
},
|
||||||
|
"props": {},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "my-component.md",
|
||||||
|
"width": 20,
|
||||||
|
"height": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tips:**
|
||||||
|
- ID must be kebab-case, 2-64 characters
|
||||||
|
- Version must be semantic (major.minor.patch)
|
||||||
|
- Timestamps should be ISO 8601 format
|
||||||
|
- Template file must end with `.md`
|
||||||
|
|
||||||
|
### Step 3: Add Props and Variables
|
||||||
|
|
||||||
|
Define what users can configure:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"props": {
|
||||||
|
"text": "Click me", // Default values
|
||||||
|
"variant": "primary",
|
||||||
|
"disabled": false
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "my-component.md",
|
||||||
|
"width": 20,
|
||||||
|
"height": 3,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "text",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"description": "Button label text",
|
||||||
|
"validation": {
|
||||||
|
"min": 1,
|
||||||
|
"max": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "variant",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "primary",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["primary", "secondary", "outline"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Define Default State and Behaviors
|
||||||
|
|
||||||
|
Components are created with **default state only** for fast MVP prototyping:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"border": "solid",
|
||||||
|
"background": "primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "emit-click-event"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"ariaLabel": "{{text}}",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Enter", "Space"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Adding more states**: After MVP validation, use `/fluxwing-expand-component` to add hover, focus, disabled states. The command will automatically add appropriate states based on component type.
|
||||||
|
|
||||||
|
### Step 5: Add Metadata (Recommended)
|
||||||
|
|
||||||
|
Help others discover and understand your component:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "My Component",
|
||||||
|
"description": "A customizable button component with multiple variants and states",
|
||||||
|
"author": "Your Name",
|
||||||
|
"created": "2024-10-11T12:00:00Z",
|
||||||
|
"modified": "2024-10-11T12:00:00Z",
|
||||||
|
"tags": ["button", "interactive", "form"],
|
||||||
|
"category": "input"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Create the .md Template
|
||||||
|
|
||||||
|
Basic structure:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
# My Component
|
||||||
|
|
||||||
|
Brief description of what this component does.
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ {{text}} │
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): Button label text. Max 20 characters.
|
||||||
|
- `variant` (string): Visual style - "primary", "secondary", or "outline"
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: button
|
||||||
|
- **Focusable**: Yes
|
||||||
|
- **Keyboard**: Enter or Space to activate
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Primary Button
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ Submit Form │
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secondary Button
|
||||||
|
```
|
||||||
|
┌──────────────────┐
|
||||||
|
│ Cancel │
|
||||||
|
└──────────────────┘
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Tips:**
|
||||||
|
- Start with default state only (fast MVP creation)
|
||||||
|
- Document ALL variables used in template
|
||||||
|
- Include usage examples with real data
|
||||||
|
- Add accessibility notes for interactive components
|
||||||
|
|
||||||
|
**Adding more states**: After creating the component, run `/fluxwing-expand-component my-component` to add hover, focus, disabled states automatically.
|
||||||
|
|
||||||
|
### Step 7: Save Files
|
||||||
|
|
||||||
|
Save both files together:
|
||||||
|
```
|
||||||
|
./fluxwing/components/my-component.uxm
|
||||||
|
./fluxwing/components/my-component.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the directory if it doesn't exist:
|
||||||
|
```bash
|
||||||
|
mkdir -p ./fluxwing/components
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Form Input
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "text-input",
|
||||||
|
"type": "input",
|
||||||
|
"props": {
|
||||||
|
"placeholder": "Enter text",
|
||||||
|
"value": "",
|
||||||
|
"error": "",
|
||||||
|
"disabled": false
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": ["default", "focus", "error", "disabled"],
|
||||||
|
"interactions": [
|
||||||
|
{"trigger": "focus", "action": "highlight-field"},
|
||||||
|
{"trigger": "blur", "action": "validate-field"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Card
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "metric-card",
|
||||||
|
"type": "card",
|
||||||
|
"props": {
|
||||||
|
"title": "Metric",
|
||||||
|
"value": "1,234",
|
||||||
|
"change": "+12%",
|
||||||
|
"trend": "up"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modal Dialog
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "confirm-dialog",
|
||||||
|
"type": "modal",
|
||||||
|
"props": {
|
||||||
|
"title": "Confirm",
|
||||||
|
"message": "Are you sure?",
|
||||||
|
"confirmText": "Yes",
|
||||||
|
"cancelText": "No"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": ["open", "closed"],
|
||||||
|
"interactions": [
|
||||||
|
{"trigger": "click", "action": "close-modal", "target": "backdrop"},
|
||||||
|
{"trigger": "keydown", "action": "close-modal", "condition": "key === 'Escape'"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Template file not found"
|
||||||
|
**Fix**: Ensure `.md` file exists and `templateFile` path is correct
|
||||||
|
|
||||||
|
### "Variable not defined"
|
||||||
|
**Fix**: Add variable to `ascii.variables` array in `.uxm`
|
||||||
|
|
||||||
|
### "Invalid JSON"
|
||||||
|
**Fix**: Validate JSON syntax (no trailing commas, proper quotes)
|
||||||
|
|
||||||
|
### "ASCII art looks broken"
|
||||||
|
**Fix**: Use monospace font, check for consistent character width
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
**Required .uxm fields**: id, type, version, metadata (name, created, modified), props, ascii (templateFile, width, height)
|
||||||
|
|
||||||
|
**Required .md sections**: Title, default state, variables documentation
|
||||||
|
|
||||||
|
**Recommended additions**: Multiple states, accessibility attributes, usage examples, detailed metadata
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- **Compose screens**: Use `/fluxwing-scaffold` to build complete screens
|
||||||
|
- **Browse library**: Use `/fluxwing-library` to see all components
|
||||||
|
- **Learn patterns**: Check `06-ascii-patterns.md` for ASCII art reference
|
||||||
|
|
||||||
|
You can now create production-ready uxscii components!
|
||||||
792
skills/fluxwing-component-creator/docs/06-ascii-patterns.md
Normal file
792
skills/fluxwing-component-creator/docs/06-ascii-patterns.md
Normal file
@@ -0,0 +1,792 @@
|
|||||||
|
# ASCII Patterns - Visual Toolkit
|
||||||
|
|
||||||
|
Complete library of ASCII characters and patterns for uxscii components.
|
||||||
|
|
||||||
|
## Box-Drawing Characters
|
||||||
|
|
||||||
|
### Basic Borders
|
||||||
|
|
||||||
|
**Light (Default)**
|
||||||
|
```
|
||||||
|
┌─────┐
|
||||||
|
│ Box │
|
||||||
|
└─────┘
|
||||||
|
```
|
||||||
|
Characters: `┌ ─ ┐ │ └ ┘`
|
||||||
|
|
||||||
|
**Rounded (Friendly)**
|
||||||
|
```
|
||||||
|
╭─────╮
|
||||||
|
│ Box │
|
||||||
|
╰─────╯
|
||||||
|
```
|
||||||
|
Characters: `╭ ─ ╮ │ ╰ ╯`
|
||||||
|
|
||||||
|
**Double (Emphasis)**
|
||||||
|
```
|
||||||
|
╔═════╗
|
||||||
|
║ Box ║
|
||||||
|
╚═════╝
|
||||||
|
```
|
||||||
|
Characters: `╔ ═ ╗ ║ ╚ ╝`
|
||||||
|
|
||||||
|
**Heavy (Strong)**
|
||||||
|
```
|
||||||
|
┏━━━━━┓
|
||||||
|
┃ Box ┃
|
||||||
|
┗━━━━━┛
|
||||||
|
```
|
||||||
|
Characters: `┏ ━ ┓ ┃ ┗ ┛`
|
||||||
|
|
||||||
|
### Complex Borders
|
||||||
|
|
||||||
|
**With Dividers**
|
||||||
|
```
|
||||||
|
┌─────────┬─────────┐
|
||||||
|
│ Col 1 │ Col 2 │
|
||||||
|
├─────────┼─────────┤
|
||||||
|
│ Data │ Data │
|
||||||
|
└─────────┴─────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nested**
|
||||||
|
```
|
||||||
|
╭─────────────────╮
|
||||||
|
│ Outer │
|
||||||
|
│ ┌─────────────┐ │
|
||||||
|
│ │ Inner │ │
|
||||||
|
│ └─────────────┘ │
|
||||||
|
╰─────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component State Patterns
|
||||||
|
|
||||||
|
### Buttons
|
||||||
|
|
||||||
|
**Default**
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Click ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hover (Highlighted)**
|
||||||
|
```
|
||||||
|
░▓▓▓▓▓▓▓▓▓▓░
|
||||||
|
░▓ Click ▓░
|
||||||
|
░▓▓▓▓▓▓▓▓▓▓░
|
||||||
|
```
|
||||||
|
|
||||||
|
**Focus (Ring)**
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━┓✨
|
||||||
|
┃ Click ┃
|
||||||
|
┗━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
**Disabled (Grayed)**
|
||||||
|
```
|
||||||
|
┌ ─ ─ ─ ─ ┐
|
||||||
|
│ Click │
|
||||||
|
└ ─ ─ ─ ─ ┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pressed/Active**
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓▓Click ▓▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form Inputs
|
||||||
|
|
||||||
|
**Text Input (Empty)**
|
||||||
|
```
|
||||||
|
[____________________]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Filled)**
|
||||||
|
```
|
||||||
|
[john@example.com ]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Focus)**
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃john@example.com│ ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Error)**
|
||||||
|
```
|
||||||
|
[invalid-email ]⚠️
|
||||||
|
❌ Please enter valid email
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Success)**
|
||||||
|
```
|
||||||
|
[john@example.com ]✅
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Disabled)**
|
||||||
|
```
|
||||||
|
[────────────────────]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Password (Masked)**
|
||||||
|
```
|
||||||
|
[•••••••• ]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Password (With Toggle)**
|
||||||
|
```
|
||||||
|
[•••••••• ]👁️
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checkboxes & Radios
|
||||||
|
|
||||||
|
**Checkbox (Unchecked)**
|
||||||
|
```
|
||||||
|
[□] Option 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Checkbox (Checked)**
|
||||||
|
```
|
||||||
|
[✓] Option 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Checkbox (Indeterminate)**
|
||||||
|
```
|
||||||
|
[▬] Option 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Radio (Unselected)**
|
||||||
|
```
|
||||||
|
○ Option A
|
||||||
|
```
|
||||||
|
|
||||||
|
**Radio (Selected)**
|
||||||
|
```
|
||||||
|
◉ Option A
|
||||||
|
```
|
||||||
|
|
||||||
|
### Selects & Dropdowns
|
||||||
|
|
||||||
|
**Select (Closed)**
|
||||||
|
```
|
||||||
|
[Choose option ▼]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Select (Open)**
|
||||||
|
```
|
||||||
|
[Current option ▼]
|
||||||
|
╭────────────────╮
|
||||||
|
│ Option 1 │
|
||||||
|
│ ● Option 2 │
|
||||||
|
│ Option 3 │
|
||||||
|
╰────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sliders
|
||||||
|
|
||||||
|
**Slider (Basic)**
|
||||||
|
```
|
||||||
|
├────────●───────┤
|
||||||
|
0% 100%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Slider (With Value)**
|
||||||
|
```
|
||||||
|
├────────●───────┤ 45%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Range Slider**
|
||||||
|
```
|
||||||
|
├────●────●──────┤
|
||||||
|
20% 80%
|
||||||
|
```
|
||||||
|
|
||||||
|
## Status Indicators
|
||||||
|
|
||||||
|
### Icons
|
||||||
|
|
||||||
|
**Success**: ✅ ✓ ●
|
||||||
|
**Error**: ❌ ✗ ⚠️ ⛔
|
||||||
|
**Warning**: ⚠️ ⚡ △
|
||||||
|
**Info**: ℹ️ ⓘ ◉
|
||||||
|
**Loading**: ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏
|
||||||
|
|
||||||
|
### Progress Bars
|
||||||
|
|
||||||
|
**Loading (40%)**
|
||||||
|
```
|
||||||
|
████▓▓▓▓▓▓ 40%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Loading (Indeterminate)**
|
||||||
|
```
|
||||||
|
▓▓▓░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
**Steps**
|
||||||
|
```
|
||||||
|
● ━━━ ● ━━━ ○ ━━━ ○
|
||||||
|
```
|
||||||
|
|
||||||
|
### Badges
|
||||||
|
|
||||||
|
**Count Badge**
|
||||||
|
```
|
||||||
|
[Inbox] ●3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Status Badge**
|
||||||
|
```
|
||||||
|
● Online
|
||||||
|
○ Offline
|
||||||
|
```
|
||||||
|
|
||||||
|
**Label Badge**
|
||||||
|
```
|
||||||
|
▓ New ▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Display
|
||||||
|
|
||||||
|
### Lists
|
||||||
|
|
||||||
|
**Unordered**
|
||||||
|
```
|
||||||
|
• Item 1
|
||||||
|
• Item 2
|
||||||
|
• Item 3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ordered**
|
||||||
|
```
|
||||||
|
1. First
|
||||||
|
2. Second
|
||||||
|
3. Third
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nested**
|
||||||
|
```
|
||||||
|
• Parent
|
||||||
|
├─ Child 1
|
||||||
|
├─ Child 2
|
||||||
|
└─ Child 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tables
|
||||||
|
|
||||||
|
**Simple**
|
||||||
|
```
|
||||||
|
┌─────┬─────┬─────┐
|
||||||
|
│ A │ B │ C │
|
||||||
|
├─────┼─────┼─────┤
|
||||||
|
│ 1 │ 2 │ 3 │
|
||||||
|
│ 4 │ 5 │ 6 │
|
||||||
|
└─────┴─────┴─────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Header**
|
||||||
|
```
|
||||||
|
╔═══════╦═══════╦═══════╗
|
||||||
|
║ Name ║ Email ║ Status║
|
||||||
|
╠═══════╬═══════╬═══════╣
|
||||||
|
║ Alice ║ a@... ║ ✓ ║
|
||||||
|
║ Bob ║ b@... ║ ○ ║
|
||||||
|
╚═══════╩═══════╩═══════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cards
|
||||||
|
|
||||||
|
**Basic**
|
||||||
|
```
|
||||||
|
╭─────────────╮
|
||||||
|
│ Title │
|
||||||
|
├─────────────┤
|
||||||
|
│ Content... │
|
||||||
|
╰─────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Actions**
|
||||||
|
```
|
||||||
|
╭─────────────────────╮
|
||||||
|
│ Card Title │
|
||||||
|
├─────────────────────┤
|
||||||
|
│ Content goes here │
|
||||||
|
│ │
|
||||||
|
│ [Action] ─┐ │
|
||||||
|
╰─────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Metric Card**
|
||||||
|
```
|
||||||
|
╭───────────╮
|
||||||
|
│ Revenue │
|
||||||
|
├───────────┤
|
||||||
|
│ $24,567 │
|
||||||
|
│ +12.5% ↗ │
|
||||||
|
╰───────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
|
||||||
|
### Tabs
|
||||||
|
|
||||||
|
**Horizontal**
|
||||||
|
```
|
||||||
|
[Active] [Tab 2] [Tab 3]
|
||||||
|
─────────
|
||||||
|
Content for active tab
|
||||||
|
```
|
||||||
|
|
||||||
|
**Vertical**
|
||||||
|
```
|
||||||
|
┌─ ● Tab 1
|
||||||
|
├─ ○ Tab 2
|
||||||
|
└─ ○ Tab 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Breadcrumbs
|
||||||
|
|
||||||
|
```
|
||||||
|
Home > Products > Details
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pagination
|
||||||
|
|
||||||
|
```
|
||||||
|
‹ Prev [1] 2 3 4 5 Next ›
|
||||||
|
```
|
||||||
|
|
||||||
|
### Menu
|
||||||
|
|
||||||
|
**Dropdown**
|
||||||
|
```
|
||||||
|
File ▼
|
||||||
|
╭──────────╮
|
||||||
|
│ New │
|
||||||
|
│ Open │
|
||||||
|
│ Save │
|
||||||
|
├──────────┤
|
||||||
|
│ Exit │
|
||||||
|
╰──────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sidebar**
|
||||||
|
```
|
||||||
|
╭─────────╮
|
||||||
|
│ • Home │
|
||||||
|
│ • Users │
|
||||||
|
│ • Data │
|
||||||
|
│ • Info │
|
||||||
|
╰─────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modals & Overlays
|
||||||
|
|
||||||
|
### Modal
|
||||||
|
|
||||||
|
**Simple**
|
||||||
|
```
|
||||||
|
╔═══════════════════╗
|
||||||
|
║ Modal Title ║
|
||||||
|
╠═══════════════════╣
|
||||||
|
║ Content here... ║
|
||||||
|
║ ║
|
||||||
|
║ [OK] [Cancel] ║
|
||||||
|
╚═══════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Close**
|
||||||
|
```
|
||||||
|
╔═══════════════════╗✕
|
||||||
|
║ Title ║
|
||||||
|
╠═══════════════════╣
|
||||||
|
║ Content... ║
|
||||||
|
╚═══════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
### Toast/Alert
|
||||||
|
|
||||||
|
**Success**
|
||||||
|
```
|
||||||
|
╭─────────────────╮
|
||||||
|
│ ✅ Saved! │
|
||||||
|
╰─────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error**
|
||||||
|
```
|
||||||
|
╭─────────────────────╮
|
||||||
|
│ ❌ Error: Try again │
|
||||||
|
╰─────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warning**
|
||||||
|
```
|
||||||
|
╭────────────────────╮
|
||||||
|
│ ⚠️ Warning message │
|
||||||
|
╰────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arrows & Indicators
|
||||||
|
|
||||||
|
### Directional
|
||||||
|
```
|
||||||
|
↑ ↓ ← →
|
||||||
|
↗ ↘ ↙ ↖
|
||||||
|
⬆ ⬇ ⬅ ➡
|
||||||
|
▲ ▼ ◀ ▶
|
||||||
|
```
|
||||||
|
|
||||||
|
### Trend
|
||||||
|
```
|
||||||
|
↗️ Up trend
|
||||||
|
→ Flat
|
||||||
|
↘️ Down trend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Expand/Collapse
|
||||||
|
```
|
||||||
|
▼ Expanded
|
||||||
|
▶ Collapsed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Spacing & Alignment
|
||||||
|
|
||||||
|
**CRITICAL FOR SCREEN RENDERING**: These rules prevent alignment issues in `.rendered.md` files.
|
||||||
|
|
||||||
|
### 1. Boundary Alignment - Content MUST Stay Inside Borders
|
||||||
|
|
||||||
|
**Golden Rule**: All text and content MUST be contained within component borders. Nothing floats outside.
|
||||||
|
|
||||||
|
**❌ INCORRECT - Text outside borders:**
|
||||||
|
```
|
||||||
|
╔════════════════════════════════════════╗
|
||||||
|
║ ║
|
||||||
|
║ Video Title Here ║
|
||||||
|
║ ┌─────────┐ ║
|
||||||
|
║ │ 12:34 │ ║
|
||||||
|
╚════════════════════════════└─────────┘═╝
|
||||||
|
|
||||||
|
Video Title Here ← WRONG: Outside the box!
|
||||||
|
MrBeast ✓ ← WRONG: Floating metadata!
|
||||||
|
24M views · 2 days ago
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ CORRECT - All content inside borders:**
|
||||||
|
```
|
||||||
|
╔════════════════════════════════════════╗
|
||||||
|
║ ║
|
||||||
|
║ Video Title Here ║
|
||||||
|
║ ┌─────────┐ ║
|
||||||
|
║ │ 12:34 │ ║
|
||||||
|
║ ║
|
||||||
|
║ Video Title Here ║
|
||||||
|
║ MrBeast ✓ ║
|
||||||
|
║ 24M views · 2 days ago ║
|
||||||
|
╚════════════════════════════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rule**: If a box has borders, ALL related content must appear between those borders.
|
||||||
|
|
||||||
|
### 2. Vertical Spacing - Consistent Gaps Between Components
|
||||||
|
|
||||||
|
**Standard vertical gaps:**
|
||||||
|
- **1 line** - Tight spacing (related items within same section)
|
||||||
|
- **2 lines** - Standard spacing (between different components)
|
||||||
|
- **3 lines** - Section dividers (major layout breaks)
|
||||||
|
|
||||||
|
**❌ INCORRECT - Inconsistent spacing:**
|
||||||
|
```
|
||||||
|
╭────────────╮
|
||||||
|
│ Button 1 │
|
||||||
|
╰────────────╯
|
||||||
|
╭────────────╮
|
||||||
|
│ Button 2 │
|
||||||
|
╰────────────╯
|
||||||
|
|
||||||
|
|
||||||
|
╭────────────╮
|
||||||
|
│ Button 3 │
|
||||||
|
╰────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ CORRECT - Consistent 2-line gaps:**
|
||||||
|
```
|
||||||
|
╭────────────╮
|
||||||
|
│ Button 1 │
|
||||||
|
╰────────────╯
|
||||||
|
|
||||||
|
╭────────────╮
|
||||||
|
│ Button 2 │
|
||||||
|
╰────────────╯
|
||||||
|
|
||||||
|
╭────────────╮
|
||||||
|
│ Button 3 │
|
||||||
|
╰────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nested component spacing:**
|
||||||
|
```
|
||||||
|
╭─────────────────────────────────╮
|
||||||
|
│ Outer Container │
|
||||||
|
│ │ ← 1 line padding
|
||||||
|
│ ╭─────────────────────────╮ │
|
||||||
|
│ │ Inner Component │ │
|
||||||
|
│ ╰─────────────────────────╯ │
|
||||||
|
│ │ ← 1 line padding
|
||||||
|
╰─────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Horizontal Alignment - Consistent Padding and Text Columns
|
||||||
|
|
||||||
|
**Standard left padding:**
|
||||||
|
- **2 characters** - Minimum readable padding (compact layouts)
|
||||||
|
- **4 characters** - Standard padding (default for most screens)
|
||||||
|
- **6 characters** - Generous padding (spacious layouts)
|
||||||
|
|
||||||
|
**❌ INCORRECT - No padding, text against border:**
|
||||||
|
```
|
||||||
|
╭────────────────────╮
|
||||||
|
│Title │ ← No space after │
|
||||||
|
│Content here │
|
||||||
|
╰────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ CORRECT - Consistent 2-char padding:**
|
||||||
|
```
|
||||||
|
╭────────────────────╮
|
||||||
|
│ Title │ ← 2 spaces after │
|
||||||
|
│ Content here │
|
||||||
|
╰────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text column alignment:**
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────╮
|
||||||
|
│ Label: Value │ ← Aligned columns
|
||||||
|
│ Name: Sarah Johnson │
|
||||||
|
│ Email: sarah@example.com │
|
||||||
|
│ Status: Active │
|
||||||
|
╰──────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Center alignment for titles:**
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Welcome Back │ ← Centered
|
||||||
|
│ Sign in to continue │ ← Centered
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Right alignment for metadata:**
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────╮
|
||||||
|
│ Post Title │
|
||||||
|
│ u/username · 3h ago [123] │ ← Right-aligned vote count
|
||||||
|
╰──────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Grid Layouts - Multi-Column Screens
|
||||||
|
|
||||||
|
**2-column grid with proper spacing:**
|
||||||
|
|
||||||
|
**❌ INCORRECT - Inconsistent gaps, misaligned:**
|
||||||
|
```
|
||||||
|
╭──────────╮╭──────────╮ ← No gap
|
||||||
|
│ Card 1 ││ Card 2 │
|
||||||
|
╰──────────╯╰──────────╯
|
||||||
|
|
||||||
|
╭──────────╮ ╭──────────╮ ← Different gap
|
||||||
|
│ Card 3 │ │ Card 4 │
|
||||||
|
╰──────────╯ ╰──────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ CORRECT - Consistent 2-char gaps, aligned columns:**
|
||||||
|
```
|
||||||
|
╭──────────╮ ╭──────────╮
|
||||||
|
│ Card 1 │ │ Card 2 │
|
||||||
|
╰──────────╯ ╰──────────╯
|
||||||
|
|
||||||
|
╭──────────╮ ╭──────────╮
|
||||||
|
│ Card 3 │ │ Card 4 │
|
||||||
|
╰──────────╯ ╰──────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**3-column grid:**
|
||||||
|
```
|
||||||
|
╭────────╮ ╭────────╮ ╭────────╮
|
||||||
|
│ Card 1 │ │ Card 2 │ │ Card 3 │
|
||||||
|
╰────────╯ ╰────────╯ ╰────────╯
|
||||||
|
|
||||||
|
╭────────╮ ╭────────╮ ╭────────╮
|
||||||
|
│ Card 4 │ │ Card 5 │ │ Card 6 │
|
||||||
|
╰────────╯ ╰────────╯ ╰────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sidebar + main layout:**
|
||||||
|
```
|
||||||
|
╭─────────╮ ╭──────────────────────────────╮
|
||||||
|
│ Sidebar │ │ Main Content │
|
||||||
|
│ │ │ │
|
||||||
|
│ • Home │ │ ╭─────────────────────╮ │
|
||||||
|
│ • Users │ │ │ Content Card │ │
|
||||||
|
│ • Data │ │ ╰─────────────────────╯ │
|
||||||
|
│ │ │ │
|
||||||
|
╰─────────╯ ╰──────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Screen Composition Rules
|
||||||
|
|
||||||
|
**Full screen example combining all rules:**
|
||||||
|
|
||||||
|
```
|
||||||
|
╔══════════════════════════════════════════════════════╗
|
||||||
|
║ ║
|
||||||
|
║ Screen Title ║ ← Centered, 1 line padding
|
||||||
|
║ ║
|
||||||
|
╠══════════════════════════════════════════════════════╣
|
||||||
|
║ Navigation Bar ║ ← 2-char padding
|
||||||
|
╠══════════════════════════════════════════════════════╣
|
||||||
|
║ ║ ← 1 line section gap
|
||||||
|
║ ╭────────────────────────╮ ╭────────────────────╮ ║
|
||||||
|
║ │ Left Card │ │ Right Card │ ║ ← 2-col grid, 2-char gap
|
||||||
|
║ │ │ │ │ ║
|
||||||
|
║ │ Content with 2-char │ │ Aligned content │ ║ ← All content inside borders
|
||||||
|
║ │ left padding │ │ stays in bounds │ ║
|
||||||
|
║ │ │ │ │ ║
|
||||||
|
║ ╰────────────────────────╯ ╰────────────────────╯ ║
|
||||||
|
║ ║ ← 2 line component gap
|
||||||
|
║ ║
|
||||||
|
║ ╭──────────────────────────────────────────────╮ ║
|
||||||
|
║ │ Full-width Component │ ║ ← Centered in parent
|
||||||
|
║ │ │ ║
|
||||||
|
║ │ Text content properly padded │ ║
|
||||||
|
║ │ │ ║
|
||||||
|
║ ╰──────────────────────────────────────────────╯ ║
|
||||||
|
║ ║
|
||||||
|
╚══════════════════════════════════════════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Common Patterns - Feed Layouts
|
||||||
|
|
||||||
|
**YouTube-style feed (CORRECT):**
|
||||||
|
```
|
||||||
|
╔═══════════════════════════╗ ╔═══════════════════════════╗
|
||||||
|
║ ║ ║ ║
|
||||||
|
║ Thumbnail Area ║ ║ Thumbnail Area ║
|
||||||
|
║ ║ ║ ║
|
||||||
|
║ ┌────────┐ ║ ║ ┌────────┐ ║
|
||||||
|
║ │ 12:34 │ ║ ║ │ 8:45 │ ║
|
||||||
|
║ └────────┘ ║ ║ └────────┘ ║
|
||||||
|
║ ║ ║ ║
|
||||||
|
║ DON'T TRY THIS ║ ║ Watermelon Candy VS REAL ║
|
||||||
|
║ ║ ║ ║
|
||||||
|
║ MrBeast ✓ ║ ║ Crafty Panda ✓ ║
|
||||||
|
║ 24M views · 2 days ago ║ ║ 543K views · 1 day ago ║
|
||||||
|
╚═══════════════════════════╝ ╚═══════════════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reddit-style post feed (CORRECT):**
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────────────────────╮
|
||||||
|
│ ▲ Which bank offers best savings in 2025? │
|
||||||
|
│ 342 │
|
||||||
|
│ ▼ Posted by u/financeguru · 3h ago │
|
||||||
|
│ │
|
||||||
|
│ I've been researching high-yield savings │
|
||||||
|
│ accounts and comparing rates... │
|
||||||
|
│ │
|
||||||
|
│ 💬 156 comments 📤 Share 🔖 Save │
|
||||||
|
╰──────────────────────────────────────────────────────╯
|
||||||
|
|
||||||
|
╭──────────────────────────────────────────────────────╮
|
||||||
|
│ ▲ PSA: Chase added Zelle fraud protection │
|
||||||
|
│ 289 │
|
||||||
|
│ ▼ Posted by u/bankwatcher · 5h ago │
|
||||||
|
│ │
|
||||||
|
│ Just got an email that Chase is now offering │
|
||||||
|
│ Zelle fraud reimbursement... │
|
||||||
|
│ │
|
||||||
|
│ 💬 87 comments 📤 Share 🔖 Save │
|
||||||
|
╰──────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Overflow Handling
|
||||||
|
|
||||||
|
**When content is too long:**
|
||||||
|
|
||||||
|
**❌ INCORRECT - Breaking borders:**
|
||||||
|
```
|
||||||
|
╭──────────────╮
|
||||||
|
│ This is a very long title that goes past the border │
|
||||||
|
╰──────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ CORRECT - Truncate with ellipsis:**
|
||||||
|
```
|
||||||
|
╭──────────────────────────╮
|
||||||
|
│ This is a very long ti...│
|
||||||
|
╰──────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ CORRECT - Wrap within bounds:**
|
||||||
|
```
|
||||||
|
╭──────────────────────────╮
|
||||||
|
│ This is a very long │
|
||||||
|
│ title that wraps to │
|
||||||
|
│ multiple lines │
|
||||||
|
╰──────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. Alignment Checklist for Screen Composition
|
||||||
|
|
||||||
|
Before saving any `.rendered.md` file, verify:
|
||||||
|
|
||||||
|
- [ ] **Boundary**: All text is inside component borders (no floating text)
|
||||||
|
- [ ] **Vertical**: Consistent gaps between components (1-3 lines)
|
||||||
|
- [ ] **Horizontal**: Consistent left padding (2-4 chars minimum)
|
||||||
|
- [ ] **Grid**: Multi-column layouts have aligned columns and consistent gaps
|
||||||
|
- [ ] **Overflow**: Long content is truncated or wrapped, never breaking borders
|
||||||
|
- [ ] **Nested**: Inner components have proper padding from outer containers
|
||||||
|
- [ ] **Metadata**: Related info (timestamps, counts) aligned consistently
|
||||||
|
|
||||||
|
### Quick Reference - Standard Measurements
|
||||||
|
|
||||||
|
```
|
||||||
|
Padding (horizontal): 2-4 chars (standard)
|
||||||
|
Gaps (vertical): 1-2 lines (standard)
|
||||||
|
Grid gaps: 2 chars (between columns)
|
||||||
|
Section breaks: 3 lines
|
||||||
|
Border padding: 1 line top/bottom
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips for Consistent Patterns
|
||||||
|
|
||||||
|
1. **Choose one border style per component** - Don't mix light/rounded/heavy
|
||||||
|
2. **Use consistent spacing** - Multiples of 4 characters work well
|
||||||
|
3. **Align related elements** - Keep boxes at same width when stacked
|
||||||
|
4. **Test in monospace** - Always preview in monospace font
|
||||||
|
5. **Consider state transitions** - Visual changes should be clear
|
||||||
|
|
||||||
|
## Copy-Paste Ready
|
||||||
|
|
||||||
|
```
|
||||||
|
Light box: ┌─┐│└┘
|
||||||
|
Rounded: ╭─╮│╰╯
|
||||||
|
Double: ╔═╗║╚╝
|
||||||
|
Heavy: ┏━┓┃┗┛
|
||||||
|
|
||||||
|
Checkbox: [□] [✓]
|
||||||
|
Radio: ○ ◉
|
||||||
|
Status: ✅ ❌ ⚠️ ℹ️
|
||||||
|
Arrows: ↑↓←→ ↗↘
|
||||||
|
```
|
||||||
|
|
||||||
|
Use these patterns to create beautiful, consistent uxscii components!
|
||||||
205
skills/fluxwing-component-creator/docs/07-schema-reference.md
Normal file
205
skills/fluxwing-component-creator/docs/07-schema-reference.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# Schema Reference
|
||||||
|
|
||||||
|
Pointer to the definitive uxscii component schema and how to use it.
|
||||||
|
|
||||||
|
## The Schema File
|
||||||
|
|
||||||
|
**Location**: `../schema/uxm-component.schema.json`
|
||||||
|
|
||||||
|
This is the **definitive source of truth** for the uxscii `.uxm` file format.
|
||||||
|
|
||||||
|
## What the Schema Defines
|
||||||
|
|
||||||
|
The schema specifies:
|
||||||
|
- All valid field names and types
|
||||||
|
- Required vs optional fields
|
||||||
|
- Validation rules (min/max, patterns, enums)
|
||||||
|
- Nested object structures
|
||||||
|
- Field descriptions
|
||||||
|
|
||||||
|
## How to Use the Schema
|
||||||
|
|
||||||
|
### For Reference
|
||||||
|
|
||||||
|
When creating components, refer to the schema to understand:
|
||||||
|
- What fields are available
|
||||||
|
- What values are allowed
|
||||||
|
- What's required vs optional
|
||||||
|
|
||||||
|
### For Autocompletion
|
||||||
|
|
||||||
|
Many editors can use the schema for:
|
||||||
|
- Field suggestions
|
||||||
|
- Type checking
|
||||||
|
- Documentation on hover
|
||||||
|
|
||||||
|
Add to your `.uxm` file:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "../data/schema/uxm-component.schema.json",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Schema Sections
|
||||||
|
|
||||||
|
### Required Root Fields
|
||||||
|
|
||||||
|
```
|
||||||
|
id - Component identifier (kebab-case, 2-64 chars)
|
||||||
|
type - Component type (enum or "custom")
|
||||||
|
version - Semantic version (X.Y.Z)
|
||||||
|
metadata - Component metadata object
|
||||||
|
props - Component properties object
|
||||||
|
ascii - ASCII template reference
|
||||||
|
```
|
||||||
|
|
||||||
|
### Metadata Object (required fields)
|
||||||
|
|
||||||
|
```
|
||||||
|
name - Human-readable name (1-100 chars)
|
||||||
|
created - ISO 8601 timestamp
|
||||||
|
modified - ISO 8601 timestamp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Metadata Object (optional fields)
|
||||||
|
|
||||||
|
```
|
||||||
|
description - Component description (max 500 chars)
|
||||||
|
author - Creator name (max 100 chars)
|
||||||
|
tags - Array of search tags (max 10)
|
||||||
|
category - Component category (enum)
|
||||||
|
```
|
||||||
|
|
||||||
|
### ASCII Object (required)
|
||||||
|
|
||||||
|
```
|
||||||
|
templateFile - Template filename (must end in .md)
|
||||||
|
width - Character width (1-120)
|
||||||
|
height - Character height (1-50)
|
||||||
|
```
|
||||||
|
|
||||||
|
### ASCII Object (optional)
|
||||||
|
|
||||||
|
```
|
||||||
|
variables - Array of template variable definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
### Behavior Object (optional)
|
||||||
|
|
||||||
|
```
|
||||||
|
states - Array of component states
|
||||||
|
interactions - Array of user interactions
|
||||||
|
animations - Array of animation definitions
|
||||||
|
accessibility- Accessibility attributes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Types (Enum)
|
||||||
|
|
||||||
|
Valid `type` values:
|
||||||
|
```
|
||||||
|
button, input, card, navigation, form, list, modal,
|
||||||
|
table, badge, alert, container, text, image, divider, custom
|
||||||
|
```
|
||||||
|
|
||||||
|
## Categories (Enum)
|
||||||
|
|
||||||
|
Valid `metadata.category` values:
|
||||||
|
```
|
||||||
|
layout, input, display, navigation, feedback,
|
||||||
|
utility, overlay, custom
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variable Types (Enum)
|
||||||
|
|
||||||
|
Valid `ascii.variables[].type` values:
|
||||||
|
```
|
||||||
|
string, number, boolean, color, size, array, object
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference Examples
|
||||||
|
|
||||||
|
### Minimal Valid Component
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "simple",
|
||||||
|
"type": "button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Simple",
|
||||||
|
"created": "2024-01-01T00:00:00Z",
|
||||||
|
"modified": "2024-01-01T00:00:00Z"
|
||||||
|
},
|
||||||
|
"props": {},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "simple.md",
|
||||||
|
"width": 10,
|
||||||
|
"height": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Complete Component
|
||||||
|
|
||||||
|
See `../examples/primary-button.uxm` for a fully-featured example using all available fields.
|
||||||
|
|
||||||
|
## Schema Version
|
||||||
|
|
||||||
|
Current schema version: **1.1.0**
|
||||||
|
|
||||||
|
Schema follows semantic versioning:
|
||||||
|
- Major: Breaking changes to structure
|
||||||
|
- Minor: New optional fields
|
||||||
|
- Patch: Documentation or validation updates
|
||||||
|
|
||||||
|
## Validation Rules Summary
|
||||||
|
|
||||||
|
### ID Rules
|
||||||
|
- Pattern: `^[a-z0-9][a-z0-9-]*[a-z0-9]$`
|
||||||
|
- Length: 2-64 characters
|
||||||
|
- Format: kebab-case only
|
||||||
|
|
||||||
|
### Version Rules
|
||||||
|
- Pattern: `^\d+\.\d+\.\d+(?:-[a-zA-Z0-9-]+)?$`
|
||||||
|
- Examples: `1.0.0`, `2.1.3`, `1.0.0-beta`
|
||||||
|
|
||||||
|
### Timestamp Rules
|
||||||
|
- Format: ISO 8601 date-time
|
||||||
|
- Example: `2024-10-11T12:00:00Z`
|
||||||
|
|
||||||
|
### Variable Name Rules
|
||||||
|
- Pattern: `^[a-zA-Z][a-zA-Z0-9_]*$`
|
||||||
|
- Format: camelCase recommended
|
||||||
|
- Examples: `text`, `userName`, `isDisabled`
|
||||||
|
|
||||||
|
## Common Schema Errors
|
||||||
|
|
||||||
|
**"Additional property not allowed"**
|
||||||
|
- You used a field name that doesn't exist in the schema
|
||||||
|
- Check field spelling and location in object hierarchy
|
||||||
|
|
||||||
|
**"Missing required property"**
|
||||||
|
- You omitted a required field
|
||||||
|
- Add the field with a valid value
|
||||||
|
|
||||||
|
**"Value does not match pattern"**
|
||||||
|
- Field value doesn't match regex pattern
|
||||||
|
- Check format requirements (kebab-case, semver, etc.)
|
||||||
|
|
||||||
|
**"Value not in enum"**
|
||||||
|
- You used a value not in the allowed list
|
||||||
|
- Use one of the valid enum values
|
||||||
|
|
||||||
|
## Deep Dive
|
||||||
|
|
||||||
|
For comprehensive schema documentation, see:
|
||||||
|
- **Full guide**: `UXSCII_SCHEMA_GUIDE.md` (detailed explanations)
|
||||||
|
- **Agent guide**: `UXSCII_AGENT_GUIDE.md` (practical examples)
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- **View schema**: Open `../schema/uxm-component.schema.json`
|
||||||
|
- **See examples**: Browse `../examples/*.uxm`
|
||||||
|
|
||||||
|
The schema ensures all uxscii components are compatible and machine-readable!
|
||||||
@@ -0,0 +1,386 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$id": "https://uxscii.org/schemas/uxm-component.json",
|
||||||
|
"title": "UXM Component Schema",
|
||||||
|
"description": "JSON schema for .uxm component metadata files",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$",
|
||||||
|
"minLength": 2,
|
||||||
|
"maxLength": 64,
|
||||||
|
"description": "Unique identifier for the component using kebab-case"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"button",
|
||||||
|
"input",
|
||||||
|
"checkbox",
|
||||||
|
"card",
|
||||||
|
"navigation",
|
||||||
|
"form",
|
||||||
|
"list",
|
||||||
|
"modal",
|
||||||
|
"table",
|
||||||
|
"badge",
|
||||||
|
"icon",
|
||||||
|
"alert",
|
||||||
|
"container",
|
||||||
|
"text",
|
||||||
|
"image",
|
||||||
|
"divider",
|
||||||
|
"custom"
|
||||||
|
],
|
||||||
|
"description": "Component type from the standard library or 'custom'"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^\\d+\\.\\d+\\.\\d+(?:-[a-zA-Z0-9-]+)?$",
|
||||||
|
"description": "Semantic version of the component specification"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"maxLength": 100,
|
||||||
|
"description": "Human-readable name for the component"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 500,
|
||||||
|
"description": "Optional description of the component's purpose and usage"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 100,
|
||||||
|
"description": "Component author or organization"
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"description": "ISO 8601 timestamp when component was created"
|
||||||
|
},
|
||||||
|
"modified": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"description": "ISO 8601 timestamp when component was last modified"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$"
|
||||||
|
},
|
||||||
|
"maxItems": 10,
|
||||||
|
"uniqueItems": true,
|
||||||
|
"description": "Tags for categorization and search"
|
||||||
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["layout", "input", "display", "navigation", "feedback", "utility", "overlay", "custom"],
|
||||||
|
"description": "Primary category for component organization"
|
||||||
|
},
|
||||||
|
"fidelity": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["sketch", "basic", "detailed", "production"],
|
||||||
|
"description": "Component design fidelity level",
|
||||||
|
"default": "detailed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "description", "created", "modified", "tags", "category", "fidelity"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Component properties and configuration",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"states": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/componentState"
|
||||||
|
},
|
||||||
|
"description": "Possible states the component can be in"
|
||||||
|
},
|
||||||
|
"interactions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/componentInteraction"
|
||||||
|
},
|
||||||
|
"description": "User interactions the component responds to"
|
||||||
|
},
|
||||||
|
"animations": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/componentAnimation"
|
||||||
|
},
|
||||||
|
"description": "Animations associated with state changes"
|
||||||
|
},
|
||||||
|
"accessibility": {
|
||||||
|
"$ref": "#/$defs/accessibilitySpec",
|
||||||
|
"description": "Accessibility requirements and ARIA attributes"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"positioning": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["static", "relative", "absolute", "fixed", "sticky"],
|
||||||
|
"default": "static"
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["block", "inline", "inline-block", "flex", "grid", "none", "overlay", "table"],
|
||||||
|
"default": "block"
|
||||||
|
},
|
||||||
|
"spacing": {
|
||||||
|
"$ref": "#/$defs/spacingSpec"
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"$ref": "#/$defs/sizingSpec"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"templateFile": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-zA-Z0-9._-]+\\.md$",
|
||||||
|
"description": "Filename of the associated ASCII template"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 120,
|
||||||
|
"description": "Character width of the ASCII representation"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 50,
|
||||||
|
"description": "Character height of the ASCII representation"
|
||||||
|
},
|
||||||
|
"variables": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/templateVariable"
|
||||||
|
},
|
||||||
|
"description": "Variables used in the ASCII template"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["templateFile", "width", "height"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"extends": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Component ID that this component extends"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "type", "version", "metadata", "props", "ascii"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"$defs": {
|
||||||
|
"componentState": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"triggers": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "properties"],
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"componentInteraction": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"trigger": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["click", "hover", "focus", "blur", "keydown", "keyup", "change", "submit", "timeout", "reset", "field-change"]
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Optional target element or component"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Optional condition for the interaction"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["trigger", "action"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"componentAnimation": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"easing": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["linear", "ease", "ease-in", "ease-out", "ease-in-out", "cubic-bezier"]
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "duration", "properties"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"accessibilitySpec": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"role": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "ARIA role for the component"
|
||||||
|
},
|
||||||
|
"ariaLabel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Accessible label for screen readers"
|
||||||
|
},
|
||||||
|
"ariaDescribedBy": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "IDs of elements that describe this component"
|
||||||
|
},
|
||||||
|
"ariaLabelledBy": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "IDs of elements that label this component"
|
||||||
|
},
|
||||||
|
"keyboardSupport": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "Keyboard interactions supported"
|
||||||
|
},
|
||||||
|
"focusable": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Whether the component can receive focus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"spacingSpec": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"margin": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "number"},
|
||||||
|
{"$ref": "#/$defs/boxSpacing"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"padding": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "number"},
|
||||||
|
{"$ref": "#/$defs/boxSpacing"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"boxSpacing": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"top": {"type": "number"},
|
||||||
|
"right": {"type": "number"},
|
||||||
|
"bottom": {"type": "number"},
|
||||||
|
"left": {"type": "number"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sizingSpec": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"width": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "number"},
|
||||||
|
{"type": "string", "enum": ["auto", "fit-content", "max-content", "min-content"]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "number"},
|
||||||
|
{"type": "string", "enum": ["auto", "fit-content", "max-content", "min-content"]}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minWidth": {"type": "number"},
|
||||||
|
"maxWidth": {"type": "number"},
|
||||||
|
"minHeight": {"type": "number"},
|
||||||
|
"maxHeight": {"type": "number"}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"templateVariable": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
|
||||||
|
"description": "Variable name using camelCase"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["string", "number", "boolean", "color", "size", "array", "object"],
|
||||||
|
"description": "Data type of the variable"
|
||||||
|
},
|
||||||
|
"defaultValue": {
|
||||||
|
"description": "Default value for the variable"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 200,
|
||||||
|
"description": "Description of the variable's purpose"
|
||||||
|
},
|
||||||
|
"required": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Whether the variable is required"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"min": {"type": "number"},
|
||||||
|
"max": {"type": "number"},
|
||||||
|
"pattern": {"type": "string"},
|
||||||
|
"enum": {"type": "array"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "type"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
101
skills/fluxwing-component-creator/scripts/quick_validate.py
Normal file
101
skills/fluxwing-component-creator/scripts/quick_validate.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Quick validation wrapper for component creation workflow.
|
||||||
|
Returns simple pass/fail + actionable error messages.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python quick_validate.py <component.uxm> <schema.json>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Exit 0 if valid, Exit 1 if errors found
|
||||||
|
Human-friendly output for Claude to read
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
try:
|
||||||
|
from jsonschema import validate, ValidationError, Draft7Validator
|
||||||
|
except ImportError:
|
||||||
|
print("✗ Error: jsonschema library not found")
|
||||||
|
print(" Install with: pip install jsonschema")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
def quick_validate(uxm_file: str, schema_file: str) -> None:
|
||||||
|
"""Validate and print result in Claude-friendly format."""
|
||||||
|
try:
|
||||||
|
# Load component
|
||||||
|
with open(uxm_file) as f:
|
||||||
|
component = json.load(f)
|
||||||
|
|
||||||
|
# Load schema
|
||||||
|
with open(schema_file) as f:
|
||||||
|
schema = json.load(f)
|
||||||
|
|
||||||
|
# Validate against schema
|
||||||
|
validator = Draft7Validator(schema)
|
||||||
|
errors = list(validator.iter_errors(component))
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
# Schema validation failed
|
||||||
|
print(f"✗ Validation Failed: {uxm_file}")
|
||||||
|
print()
|
||||||
|
for i, error in enumerate(errors[:3], 1): # Show first 3 errors
|
||||||
|
path = " → ".join(map(str, error.path)) if error.path else "root"
|
||||||
|
print(f" Error {i}: {error.message}")
|
||||||
|
print(f" Location: {path}")
|
||||||
|
print()
|
||||||
|
if len(errors) > 3:
|
||||||
|
print(f" ... and {len(errors) - 3} more errors")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Schema validation passed - check for .md file
|
||||||
|
md_file = uxm_file.replace('.uxm', '.md')
|
||||||
|
if not Path(md_file).exists():
|
||||||
|
print(f"✗ Missing ASCII File: {md_file}")
|
||||||
|
print(f" Component .uxm is valid, but .md template file not found")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Success!
|
||||||
|
comp_name = component['metadata']['name']
|
||||||
|
comp_id = component['id']
|
||||||
|
comp_type = component['type']
|
||||||
|
state_count = len(component.get('behavior', {}).get('states', []))
|
||||||
|
prop_count = len(component.get('props', {}))
|
||||||
|
|
||||||
|
print(f"✓ Valid: {comp_name} ({comp_id})")
|
||||||
|
print(f" Type: {comp_type}")
|
||||||
|
print(f" States: {state_count}")
|
||||||
|
print(f" Props: {prop_count}")
|
||||||
|
print(f" Files: {uxm_file} + {Path(md_file).name}")
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"✗ Invalid JSON: {uxm_file}")
|
||||||
|
print(f" Error: {e.msg} at line {e.lineno}, column {e.colno}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
print(f"✗ File Not Found: {e.filename}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Unexpected Error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("Usage: quick_validate.py <component.uxm> <schema.json>")
|
||||||
|
print()
|
||||||
|
print("Quickly validates a uxscii component for use in workflows.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
quick_validate(sys.argv[1], sys.argv[2])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
207
skills/fluxwing-component-creator/scripts/validate_component.py
Normal file
207
skills/fluxwing-component-creator/scripts/validate_component.py
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Fast, deterministic component validation using JSON Schema.
|
||||||
|
Replaces the removed fluxwing-validator agent with reliable script.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python validate_component.py <component.uxm> <schema.json>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Exit 0 if valid, Exit 1 if errors found
|
||||||
|
JSON output with validation results
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
try:
|
||||||
|
from jsonschema import validate, ValidationError, Draft7Validator
|
||||||
|
except ImportError:
|
||||||
|
print("Error: jsonschema library not found. Install with: pip install jsonschema")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_component(uxm_file_path: str, schema_path: str) -> dict:
|
||||||
|
"""
|
||||||
|
Validate a .uxm component file against the schema.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{
|
||||||
|
"valid": bool,
|
||||||
|
"errors": [list of error messages],
|
||||||
|
"warnings": [list of warnings],
|
||||||
|
"stats": {component stats}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# Load component
|
||||||
|
try:
|
||||||
|
with open(uxm_file_path) as f:
|
||||||
|
component = json.load(f)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"errors": [{
|
||||||
|
"path": [],
|
||||||
|
"message": f"Invalid JSON: {e.msg} at line {e.lineno}",
|
||||||
|
"type": "json_error"
|
||||||
|
}],
|
||||||
|
"warnings": [],
|
||||||
|
"stats": {}
|
||||||
|
}
|
||||||
|
except FileNotFoundError:
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"errors": [{
|
||||||
|
"path": [],
|
||||||
|
"message": f"Component file not found: {uxm_file_path}",
|
||||||
|
"type": "file_not_found"
|
||||||
|
}],
|
||||||
|
"warnings": [],
|
||||||
|
"stats": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load schema
|
||||||
|
try:
|
||||||
|
with open(schema_path) as f:
|
||||||
|
schema = json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"errors": [{
|
||||||
|
"path": [],
|
||||||
|
"message": f"Schema file not found: {schema_path}",
|
||||||
|
"type": "file_not_found"
|
||||||
|
}],
|
||||||
|
"warnings": [],
|
||||||
|
"stats": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate against schema
|
||||||
|
validator = Draft7Validator(schema)
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for error in validator.iter_errors(component):
|
||||||
|
errors.append({
|
||||||
|
"path": list(error.path),
|
||||||
|
"message": error.message,
|
||||||
|
"type": "schema_violation"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Additional uxscii-specific checks
|
||||||
|
warnings = []
|
||||||
|
|
||||||
|
# Check 1: ASCII file exists
|
||||||
|
md_file = uxm_file_path.replace('.uxm', '.md')
|
||||||
|
if not Path(md_file).exists():
|
||||||
|
errors.append({
|
||||||
|
"path": ["ascii", "templateFile"],
|
||||||
|
"message": f"ASCII template file not found: {md_file}",
|
||||||
|
"type": "missing_file"
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
# Check 2: Template variables match
|
||||||
|
with open(md_file) as f:
|
||||||
|
md_content = f.read()
|
||||||
|
|
||||||
|
# Extract {{variables}} from markdown
|
||||||
|
md_vars = set(re.findall(r'\{\{(\w+)\}\}', md_content))
|
||||||
|
|
||||||
|
# Get variables from .uxm (handle both formats: array of strings or array of objects)
|
||||||
|
ascii_vars = component.get('ascii', {}).get('variables', [])
|
||||||
|
if ascii_vars and isinstance(ascii_vars[0], dict):
|
||||||
|
# Array of objects format: [{"name": "text", "type": "string"}]
|
||||||
|
uxm_vars = {v['name'] for v in ascii_vars if isinstance(v, dict) and 'name' in v}
|
||||||
|
else:
|
||||||
|
# Array of strings format: ["text", "value"]
|
||||||
|
uxm_vars = set(ascii_vars)
|
||||||
|
|
||||||
|
missing = md_vars - uxm_vars
|
||||||
|
if missing:
|
||||||
|
warnings.append({
|
||||||
|
"path": ["ascii", "variables"],
|
||||||
|
"message": f"Variables in .md but not defined in .uxm: {', '.join(sorted(missing))}",
|
||||||
|
"type": "variable_mismatch"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Check 3: Accessibility requirements
|
||||||
|
if component.get('behavior', {}).get('interactive'):
|
||||||
|
accessibility = component.get('accessibility', {})
|
||||||
|
if not accessibility.get('role'):
|
||||||
|
warnings.append({
|
||||||
|
"path": ["accessibility", "role"],
|
||||||
|
"message": "Interactive component should have ARIA role",
|
||||||
|
"type": "accessibility"
|
||||||
|
})
|
||||||
|
if not accessibility.get('focusable'):
|
||||||
|
warnings.append({
|
||||||
|
"path": ["accessibility", "focusable"],
|
||||||
|
"message": "Interactive component should be focusable",
|
||||||
|
"type": "accessibility"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Check 4: ASCII dimensions
|
||||||
|
ascii_config = component.get('ascii', {})
|
||||||
|
width = ascii_config.get('width', 0)
|
||||||
|
height = ascii_config.get('height', 0)
|
||||||
|
|
||||||
|
if width > 120:
|
||||||
|
warnings.append({
|
||||||
|
"path": ["ascii", "width"],
|
||||||
|
"message": f"Width {width} exceeds recommended max of 120",
|
||||||
|
"type": "dimensions"
|
||||||
|
})
|
||||||
|
|
||||||
|
if height > 50:
|
||||||
|
warnings.append({
|
||||||
|
"path": ["ascii", "height"],
|
||||||
|
"message": f"Height {height} exceeds recommended max of 50",
|
||||||
|
"type": "dimensions"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Check 5: States should have properties
|
||||||
|
states = component.get('behavior', {}).get('states', [])
|
||||||
|
for state in states:
|
||||||
|
if 'properties' not in state:
|
||||||
|
warnings.append({
|
||||||
|
"path": ["behavior", "states", state.get('name', 'unknown')],
|
||||||
|
"message": f"State '{state.get('name')}' has no properties defined",
|
||||||
|
"type": "incomplete_state"
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"valid": len(errors) == 0,
|
||||||
|
"errors": errors,
|
||||||
|
"warnings": warnings,
|
||||||
|
"stats": {
|
||||||
|
"id": component.get('id'),
|
||||||
|
"type": component.get('type'),
|
||||||
|
"version": component.get('version'),
|
||||||
|
"states": len(component.get('behavior', {}).get('states', [])),
|
||||||
|
"props": len(component.get('props', {})),
|
||||||
|
"interactive": component.get('behavior', {}).get('interactive', False),
|
||||||
|
"has_accessibility": bool(component.get('accessibility'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("Usage: validate_component.py <component.uxm> <schema.json>")
|
||||||
|
print()
|
||||||
|
print("Validates a uxscii component against the JSON schema.")
|
||||||
|
print("Returns JSON with validation results and exits 0 if valid, 1 if invalid.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
uxm_file = sys.argv[1]
|
||||||
|
schema_file = sys.argv[2]
|
||||||
|
|
||||||
|
result = validate_component(uxm_file, schema_file)
|
||||||
|
print(json.dumps(result, indent=2))
|
||||||
|
|
||||||
|
sys.exit(0 if result["valid"] else 1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
389
skills/fluxwing-component-creator/templates/alert.md
Normal file
389
skills/fluxwing-component-creator/templates/alert.md
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
# Alert/Notification Component
|
||||||
|
|
||||||
|
Alert and notification messages for user feedback, system status, and important information display with various styles and interactive capabilities.
|
||||||
|
|
||||||
|
## Success Alert
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ✓ {{title}} │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{message}} │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Undo │ │ View │ │
|
||||||
|
│ └──────────┘ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Warning Alert
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ⚠ Warning: Check Your Input │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Some fields contain invalid data. Please │
|
||||||
|
│ review and correct before proceeding. │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Review │ │ Continue │ │
|
||||||
|
│ └──────────┘ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Alert
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ✗ Error: Failed to Save │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ An error occurred while saving your changes. │
|
||||||
|
│ Please try again or contact support. │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Try Again│ │ Support │ │
|
||||||
|
│ └──────────┘ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Info Alert
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ℹ Information │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ New features are available! Check out the │
|
||||||
|
│ latest updates in your dashboard. │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Learn More│ │ Dismiss │ │
|
||||||
|
│ └──────────┘ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compact Alert
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ ✓ Saved successfully! │✕│
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alert without Actions
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ✓ {{title}} │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{message}} │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alert without Border
|
||||||
|
|
||||||
|
```
|
||||||
|
✓ {{title}} ✕
|
||||||
|
|
||||||
|
{{message}}
|
||||||
|
|
||||||
|
┌──────────┐ ┌──────────┐
|
||||||
|
│ Undo │ │ View │
|
||||||
|
└──────────┘ └──────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Toast Notification Style
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ ✓ Changes saved! │✕│
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Progress Alert
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ⏳ Uploading Files... │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Please wait while we upload your files. │
|
||||||
|
│ │
|
||||||
|
│ ████████████████░░░░ 75% │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ │
|
||||||
|
│ │ Cancel │ │
|
||||||
|
│ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Persistent Alert (No Auto-dismiss)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ⚠ Action Required │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Your subscription expires in 3 days. │
|
||||||
|
│ Please update your payment method. │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Update │ │ Remind Later│ │
|
||||||
|
│ └──────────┘ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alert Stack (Multiple Alerts)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ✓ File uploaded successfully! │✕│
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ℹ New message received │✕│
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ⚠ Storage almost full │✕│
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading Alert
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ⟳ Processing Request... │░│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Please wait while we process your request. │
|
||||||
|
│ This may take a few moments. │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alert with Rich Content
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ 🎉 Welcome to Premium! │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ You've successfully upgraded to Premium plan. │
|
||||||
|
│ │
|
||||||
|
│ ✓ Unlimited storage │
|
||||||
|
│ ✓ Priority support │
|
||||||
|
│ ✓ Advanced features │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Get Started│ │ Learn More│ │
|
||||||
|
│ └──────────┘ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Status Message Alert
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ 🔄 System Maintenance │░│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ We're performing scheduled maintenance. │
|
||||||
|
│ Expected completion: 2:00 AM EST │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ │
|
||||||
|
│ │ Status Page│ │
|
||||||
|
│ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inline Alert (Within Form)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ Name: ┌─────────────────────────────────────┐ │
|
||||||
|
│ │ John Doe │ │
|
||||||
|
│ └─────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Email: ┌────────────────────────────────────┐ │
|
||||||
|
│ │ invalid-email │ │
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
│ ┌─────────────────────────────────────────────┐ │
|
||||||
|
│ │ ⚠ Please enter a valid email address │ │
|
||||||
|
│ └─────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dismissible with Timer
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ✓ Message sent! │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Your message has been delivered successfully. │
|
||||||
|
│ │
|
||||||
|
│ ░░░░░░░░░░░░░░░████ Auto-dismiss in 3s │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- **Standard Width**: 40-60 characters
|
||||||
|
- **Compact Width**: 30-45 characters
|
||||||
|
- **Toast Width**: 25-40 characters
|
||||||
|
- **Height**: 4-10 lines depending on content
|
||||||
|
- **Action Button Height**: 3 characters
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `title` (string): Alert heading text (max 50 characters)
|
||||||
|
- `message` (string, required): Main alert content (max 200 characters)
|
||||||
|
- `variant` (string): Alert type ("success", "warning", "error", "info", "default")
|
||||||
|
- `icon` (string): Icon character for alert type (max 5 characters)
|
||||||
|
- `dismissible` (boolean): Show close button (default: true)
|
||||||
|
- `persistent` (boolean): Prevent auto-dismiss (default: false)
|
||||||
|
- `actions` (array): Action buttons with text and callbacks (max 3)
|
||||||
|
- `compact` (boolean): Use minimal spacing (default: false)
|
||||||
|
- `bordered` (boolean): Show container border (default: true)
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: alert (for important messages) or status (for less critical)
|
||||||
|
- **Focusable**: Yes, for dismissible alerts
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Escape: Dismiss alert (if dismissible)
|
||||||
|
- Tab: Navigate through action buttons
|
||||||
|
- Enter: Activate focused button
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-live`: "assertive" for alerts, "polite" for status
|
||||||
|
- `aria-label`: Descriptive label including alert type
|
||||||
|
- `aria-describedby`: Link to message content
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Form Validation
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ✗ Validation Error │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Please correct the following errors: │
|
||||||
|
│ • Email address is required │
|
||||||
|
│ • Password must be at least 8 characters │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Save Confirmation
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ ✓ Draft Saved │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Your draft has been automatically saved. │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ │
|
||||||
|
│ │ Continue │ │
|
||||||
|
│ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### System Status
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ 🔧 Maintenance Mode │░│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ The system is currently undergoing maintenance. │
|
||||||
|
│ Please try again in a few minutes. │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Achievement Notification
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ 🏆 Achievement Unlocked! │✕│
|
||||||
|
├──────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ You've completed 100 tasks! Keep up the │
|
||||||
|
│ great work. │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ Share │ │ View Badge│ │
|
||||||
|
│ └──────────┘ └──────────┘ │
|
||||||
|
└──────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Auto-Dismiss Functionality
|
||||||
|
|
||||||
|
1. **Timer-based**: Automatically dismiss after specified duration
|
||||||
|
2. **Pause on Hover**: Stop timer when user hovers over alert
|
||||||
|
3. **Resume on Leave**: Continue timer when mouse leaves
|
||||||
|
4. **Persistent Override**: Some alerts stay until manually dismissed
|
||||||
|
|
||||||
|
### Animation States
|
||||||
|
|
||||||
|
- **Enter**: Slide in from edge with fade
|
||||||
|
- **Exit**: Slide out to edge with fade
|
||||||
|
- **Hover**: Subtle highlight or shadow
|
||||||
|
- **Progress**: Animated progress bars for loading states
|
||||||
|
|
||||||
|
### Stacking Behavior
|
||||||
|
|
||||||
|
- **Multiple Alerts**: Stack vertically with spacing
|
||||||
|
- **Max Count**: Limit visible alerts, queue excess
|
||||||
|
- **Position Management**: Handle overlapping and positioning
|
||||||
|
- **Cleanup**: Remove dismissed alerts from DOM
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Visual Elements
|
||||||
|
- `┌─┐└┘─│` = Alert container borders
|
||||||
|
- `✓` = Success icon
|
||||||
|
- `⚠` = Warning icon
|
||||||
|
- `✗` = Error icon
|
||||||
|
- `ℹ` = Info icon
|
||||||
|
- `⟳🔄` = Loading/processing icons
|
||||||
|
- `✕` = Dismiss button
|
||||||
|
- `░` = Disabled/loading state
|
||||||
|
|
||||||
|
### Color Mapping (ASCII patterns)
|
||||||
|
- **Success**: `█` = Green background
|
||||||
|
- **Warning**: `▓` = Yellow/orange background
|
||||||
|
- **Error**: `▓` = Red background
|
||||||
|
- **Info**: `▓` = Blue background
|
||||||
|
- **Default**: `░` = Gray background
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- **Toast**: Temporary notification messages
|
||||||
|
- **Banner**: Persistent page-level announcements
|
||||||
|
- **Modal**: Blocking dialog for critical alerts
|
||||||
|
- **Tooltip**: Contextual help and information
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This ASCII representation demonstrates alert patterns and states. When implementing:
|
||||||
|
|
||||||
|
1. **Animation System**: Smooth enter/exit transitions
|
||||||
|
2. **Position Management**: Handle multiple alerts and positioning
|
||||||
|
3. **Timer Management**: Accurate auto-dismiss with pause/resume
|
||||||
|
4. **Accessibility**: Proper announcement to screen readers
|
||||||
|
5. **Performance**: Efficient rendering and cleanup
|
||||||
|
6. **Mobile Adaptation**: Responsive sizing and positioning
|
||||||
|
|
||||||
|
## Variants
|
||||||
|
|
||||||
|
- **Toast Notification**: Temporary messages that auto-dismiss
|
||||||
|
- **Banner Alert**: Full-width page announcements
|
||||||
|
- **Inline Alert**: Contextual messages within content
|
||||||
|
- **Modal Alert**: Blocking alerts requiring user action
|
||||||
|
- **Status Alert**: System status and maintenance messages
|
||||||
|
- **Progress Alert**: Loading and operation progress indicators
|
||||||
204
skills/fluxwing-component-creator/templates/alert.uxm
Normal file
204
skills/fluxwing-component-creator/templates/alert.uxm
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
{
|
||||||
|
"id": "alert",
|
||||||
|
"type": "alert",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Alert/Notification Component",
|
||||||
|
"description": "Alert and notification messages for user feedback, system status, and important information display",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["alert", "notification", "message", "feedback"],
|
||||||
|
"category": "feedback",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "Success!",
|
||||||
|
"message": "Your changes have been saved successfully.",
|
||||||
|
"variant": "success",
|
||||||
|
"icon": "✓",
|
||||||
|
"dismissible": true,
|
||||||
|
"persistent": false,
|
||||||
|
"position": "top-right",
|
||||||
|
"duration": 5000,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"text": "Undo",
|
||||||
|
"action": "undo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "View",
|
||||||
|
"action": "view"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"showProgress": false,
|
||||||
|
"bordered": true,
|
||||||
|
"compact": false
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "visible",
|
||||||
|
"properties": {
|
||||||
|
"opacity": 1,
|
||||||
|
"transform": "translateX(0)",
|
||||||
|
"zIndex": 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entering",
|
||||||
|
"properties": {
|
||||||
|
"opacity": 0.5,
|
||||||
|
"transform": "translateX(100%)",
|
||||||
|
"transition": "all 0.3s ease"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "exiting",
|
||||||
|
"properties": {
|
||||||
|
"opacity": 0,
|
||||||
|
"transform": "translateX(100%)",
|
||||||
|
"transition": "all 0.3s ease"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hidden",
|
||||||
|
"properties": {
|
||||||
|
"display": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "dismiss-alert",
|
||||||
|
"condition": "dismissible && target.isCloseButton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "execute-action",
|
||||||
|
"condition": "target.isActionButton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "dismiss-alert",
|
||||||
|
"condition": "key === 'Escape' && dismissible"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "timeout",
|
||||||
|
"action": "auto-dismiss",
|
||||||
|
"condition": "!persistent && duration > 0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timing": {
|
||||||
|
"autoDismiss": true,
|
||||||
|
"defaultDuration": 5000,
|
||||||
|
"pauseOnHover": true,
|
||||||
|
"resumeOnLeave": true
|
||||||
|
},
|
||||||
|
"accessibility": {
|
||||||
|
"role": "alert",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Escape", "Tab", "Enter"],
|
||||||
|
"ariaLabel": "{{variant}} alert: {{title}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "block",
|
||||||
|
"positioning": "fixed",
|
||||||
|
"spacing": {
|
||||||
|
"padding": {
|
||||||
|
"top": 2,
|
||||||
|
"right": 3,
|
||||||
|
"bottom": 2,
|
||||||
|
"left": 3
|
||||||
|
},
|
||||||
|
"margin": {
|
||||||
|
"bottom": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"minWidth": 30,
|
||||||
|
"maxWidth": 50,
|
||||||
|
"height": "auto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "alert.md",
|
||||||
|
"width": 50,
|
||||||
|
"height": 8,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Success!",
|
||||||
|
"description": "Alert title/heading text",
|
||||||
|
"required": false,
|
||||||
|
"validation": {
|
||||||
|
"max": 50
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Operation completed successfully.",
|
||||||
|
"description": "Main alert message content",
|
||||||
|
"required": true,
|
||||||
|
"validation": {
|
||||||
|
"max": 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "variant",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "success",
|
||||||
|
"description": "Alert type and styling",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["success", "warning", "error", "info", "default"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "icon",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "✓",
|
||||||
|
"description": "Icon character for alert type",
|
||||||
|
"validation": {
|
||||||
|
"max": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dismissible",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Whether alert can be manually dismissed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "persistent",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Whether alert stays until manually dismissed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "actions",
|
||||||
|
"type": "array",
|
||||||
|
"defaultValue": [],
|
||||||
|
"description": "Action buttons for the alert",
|
||||||
|
"validation": {
|
||||||
|
"max": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "compact",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Use minimal spacing and height"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bordered",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Show border around alert"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
340
skills/fluxwing-component-creator/templates/badge.md
Normal file
340
skills/fluxwing-component-creator/templates/badge.md
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
# Badge/Tag Component
|
||||||
|
|
||||||
|
Status indicators, labels, and informational tags with various styles and interactive capabilities for content labeling and notifications.
|
||||||
|
|
||||||
|
## Default Badge Styles
|
||||||
|
|
||||||
|
### Filled Badges
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓ ░░░░░░░░░░░░ ▓▓▓▓▓▓▓▓▓▓ ████████████
|
||||||
|
▓ {{text}} ▓ ░ Secondary ░ ▓ Primary ▓ █ Success █
|
||||||
|
▓▓▓▓▓▓▓▓ ░░░░░░░░░░░░ ▓▓▓▓▓▓▓▓▓▓ ████████████
|
||||||
|
```
|
||||||
|
|
||||||
|
### Outlined Badges
|
||||||
|
```
|
||||||
|
┌──────┐ ┌────────────┐ ┌──────────┐ ┌────────────┐
|
||||||
|
│ {{text}} │ │ Secondary │ │ Primary │ │ Success │
|
||||||
|
└──────┘ └────────────┘ └──────────┘ └────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Badge Variants
|
||||||
|
|
||||||
|
### Success Badge
|
||||||
|
```
|
||||||
|
████████
|
||||||
|
█ Done! █
|
||||||
|
████████
|
||||||
|
```
|
||||||
|
|
||||||
|
### Warning Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Warning ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Failed ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Info Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓
|
||||||
|
▓ Info ▓
|
||||||
|
▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Size Variations
|
||||||
|
|
||||||
|
### Small Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓▓
|
||||||
|
▓ S ▓
|
||||||
|
▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Medium Badge (Default)
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓
|
||||||
|
▓ Med ▓
|
||||||
|
▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Large Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Large ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interactive Badges
|
||||||
|
|
||||||
|
### Clickable Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Click Me ▓ ← Clickable
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Removable Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Remove │✕▓ ← Removable
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hover State
|
||||||
|
```
|
||||||
|
████████████
|
||||||
|
█ Hovered █ ← Darkened on hover
|
||||||
|
████████████
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pill-Shaped Badges
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────╮
|
||||||
|
│ {{text}} │
|
||||||
|
╰──────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notification Count Badges
|
||||||
|
|
||||||
|
### Number Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓
|
||||||
|
▓ 5 ▓
|
||||||
|
▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### High Count Badge
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓
|
||||||
|
▓ 99+ ▓
|
||||||
|
▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Count with Icon
|
||||||
|
```
|
||||||
|
📧 ▓▓▓▓
|
||||||
|
▓ 3 ▓
|
||||||
|
▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dot Indicators
|
||||||
|
|
||||||
|
### Simple Dot
|
||||||
|
```
|
||||||
|
●
|
||||||
|
```
|
||||||
|
|
||||||
|
### Colored Dots
|
||||||
|
```
|
||||||
|
● ● ● ●
|
||||||
|
```
|
||||||
|
|
||||||
|
### Positioned Dot (with content)
|
||||||
|
```
|
||||||
|
📧 ● ← Notification dot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Badge Groups/Tags
|
||||||
|
|
||||||
|
### Tag List
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ React ▓ ▓ TypeScript ▓ ▓ JavaScript ▓ ▓ Frontend ▓
|
||||||
|
▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Removable Tags
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Tag1 │✕ ▓ Tag2 │✕ ▓ Tag3 │✕
|
||||||
|
▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Status Indicators
|
||||||
|
|
||||||
|
### Online/Offline Status
|
||||||
|
```
|
||||||
|
● Online ○ Offline ◐ Away ◑ Busy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Priority Badges
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓
|
||||||
|
▓ High ▓ ▓ Medium ▓ ▓ Low ▓
|
||||||
|
▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Progress Status
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓ ████████████ ░░░░░░░░░░░░
|
||||||
|
▓ In Progress ▓ █ Complete █ ░ Pending ░
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓ ████████████ ░░░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Badge with Icons
|
||||||
|
|
||||||
|
```
|
||||||
|
🔥 ▓▓▓▓▓▓▓▓ ⭐ ▓▓▓▓▓▓▓▓▓▓ ⚠ ▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Hot ▓ ▓ Featured ▓ ▓ Warning ▓
|
||||||
|
▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compact Badges (Single Line)
|
||||||
|
|
||||||
|
```
|
||||||
|
[{{text}}] ({{text}}) <{{text}}> |{{text}}|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- **Small**: 3-8 characters wide, 1 character high
|
||||||
|
- **Medium**: 4-12 characters wide, 1 character high
|
||||||
|
- **Large**: 5-16 characters wide, 1 character high
|
||||||
|
- **Dot**: 1 character (●)
|
||||||
|
- **Count**: 3-6 characters wide depending on number
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): Badge text content (max 20 characters)
|
||||||
|
- `variant` (string): Style variant ("default", "success", "warning", "error", "info", "secondary")
|
||||||
|
- `size` (string): Size variant ("small", "medium", "large")
|
||||||
|
- `removable` (boolean): Show remove button (default: false)
|
||||||
|
- `clickable` (boolean): Enable click interactions (default: false)
|
||||||
|
- `outlined` (boolean): Use outline style instead of filled (default: false)
|
||||||
|
- `pill` (boolean): Use rounded pill shape (default: false)
|
||||||
|
- `count` (number): Numeric count for notifications (0-999)
|
||||||
|
- `dot` (boolean): Show as dot indicator without text (default: false)
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: status (informational) or button (if clickable)
|
||||||
|
- **Focusable**: Yes, if clickable or removable
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Enter: Activate badge (if clickable)
|
||||||
|
- Delete/Backspace: Remove badge (if removable)
|
||||||
|
- Tab: Navigate to next focusable element
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-label`: Descriptive label for badge purpose
|
||||||
|
- `aria-hidden`: "true" for purely decorative badges
|
||||||
|
- `aria-live`: "polite" for dynamic count badges
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Product Tags
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ New ▓ ▓ Sale ▓ ▓ Featured ▓
|
||||||
|
▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### User Roles
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Admin ▓ ░ User ░ ▓ Moderator ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notification Counts
|
||||||
|
```
|
||||||
|
📧 ▓▓▓▓ 🔔 ▓▓▓▓▓ 💬 ▓▓▓▓▓▓
|
||||||
|
▓ 5 ▓ ▓ 12 ▓ ▓ 99+ ▓
|
||||||
|
▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Status Indicators
|
||||||
|
```
|
||||||
|
● Online ▓▓▓▓▓▓▓▓▓▓ ○ Offline
|
||||||
|
▓ Active ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Skill Tags (Removable)
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓│✕ ▓▓▓▓▓▓▓▓▓▓▓▓▓│✕ ▓▓▓▓▓▓▓▓▓▓▓▓│✕
|
||||||
|
▓ JavaScript ▓ TypeScript ▓ React
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Priority Levels
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░
|
||||||
|
▓ Critical ▓ ▓ Important ▓ ░ Normal ░
|
||||||
|
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Click Interactions
|
||||||
|
|
||||||
|
1. **Clickable Badges**: Emit click events for filtering or navigation
|
||||||
|
2. **Remove Functionality**: X button removes badge from collection
|
||||||
|
3. **Toggle States**: Some badges can toggle between states
|
||||||
|
4. **Hover Effects**: Visual feedback on interactive badges
|
||||||
|
|
||||||
|
### Dynamic Updates
|
||||||
|
|
||||||
|
- **Count Changes**: Automatically update notification counts
|
||||||
|
- **Status Changes**: Update colors and text based on state
|
||||||
|
- **Add/Remove**: Dynamic badge collections
|
||||||
|
- **Animations**: Smooth transitions for state changes
|
||||||
|
|
||||||
|
### Grouping Behavior
|
||||||
|
|
||||||
|
- **Tag Lists**: Multiple badges displayed together
|
||||||
|
- **Max Display**: Show limited number with "... +N more"
|
||||||
|
- **Filtering**: Click badges to filter content
|
||||||
|
- **Auto-complete**: Add new badges via text input
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Visual Elements
|
||||||
|
- `▓` = Primary filled background
|
||||||
|
- `█` = Success/positive state
|
||||||
|
- `░` = Secondary/muted state
|
||||||
|
- `┌─┐└┘` = Outlined badge borders
|
||||||
|
- `╭─╮╰─╯` = Pill-shaped borders
|
||||||
|
- `●○◐◑` = Dot indicators
|
||||||
|
- `✕` = Remove button
|
||||||
|
|
||||||
|
### Color Mapping
|
||||||
|
- **Primary**: Blue/Brand color (▓)
|
||||||
|
- **Success**: Green (█)
|
||||||
|
- **Warning**: Yellow/Orange (▓)
|
||||||
|
- **Error**: Red (▓)
|
||||||
|
- **Info**: Blue (▓)
|
||||||
|
- **Secondary**: Gray (░)
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- **Chip**: Similar but often larger with more content
|
||||||
|
- **Button**: Interactive elements with different styling
|
||||||
|
- **Label**: Form field labels and descriptions
|
||||||
|
- **Tooltip**: Additional information on hover
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This ASCII representation demonstrates badge patterns and states. When implementing:
|
||||||
|
|
||||||
|
1. **Color System**: Map ASCII patterns to actual color schemes
|
||||||
|
2. **Animations**: Smooth transitions for hover and state changes
|
||||||
|
3. **Accessibility**: Proper focus management and screen reader support
|
||||||
|
4. **Performance**: Efficient rendering for large badge collections
|
||||||
|
5. **Responsive**: Adapt sizing and spacing for different screen sizes
|
||||||
|
6. **Customization**: Support for custom colors and shapes
|
||||||
|
|
||||||
|
## Variants
|
||||||
|
|
||||||
|
- **Label Badge**: Simple text labels
|
||||||
|
- **Count Badge**: Numeric indicators
|
||||||
|
- **Status Badge**: State indicators with colors
|
||||||
|
- **Action Badge**: Clickable elements
|
||||||
|
- **Notification Badge**: Alert indicators
|
||||||
|
- **Tag Badge**: Removable filter tags
|
||||||
201
skills/fluxwing-component-creator/templates/badge.uxm
Normal file
201
skills/fluxwing-component-creator/templates/badge.uxm
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
{
|
||||||
|
"id": "badge",
|
||||||
|
"type": "badge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Badge/Tag Component",
|
||||||
|
"description": "Status indicators, labels, and informational tags with various styles and interactive capabilities",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["badge", "tag", "label", "status", "indicator"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"text": "New",
|
||||||
|
"variant": "default",
|
||||||
|
"size": "medium",
|
||||||
|
"removable": false,
|
||||||
|
"clickable": false,
|
||||||
|
"icon": "",
|
||||||
|
"color": "primary",
|
||||||
|
"outlined": false,
|
||||||
|
"pill": false,
|
||||||
|
"count": null,
|
||||||
|
"maxCount": 99,
|
||||||
|
"dot": false
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"background": "primary",
|
||||||
|
"textColor": "white",
|
||||||
|
"border": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"background": "primary-dark",
|
||||||
|
"textColor": "white",
|
||||||
|
"cursor": "pointer"
|
||||||
|
},
|
||||||
|
"triggers": ["mouseenter"],
|
||||||
|
"condition": "clickable || removable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "active",
|
||||||
|
"properties": {
|
||||||
|
"background": "primary-darker",
|
||||||
|
"textColor": "white",
|
||||||
|
"transform": "scale(0.95)"
|
||||||
|
},
|
||||||
|
"triggers": ["mousedown"],
|
||||||
|
"condition": "clickable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disabled",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-light",
|
||||||
|
"textColor": "gray-dark",
|
||||||
|
"opacity": 0.6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "emit-click-event",
|
||||||
|
"condition": "clickable && !disabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "remove-badge",
|
||||||
|
"condition": "removable && target.isRemoveButton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "emit-click-event",
|
||||||
|
"condition": "key === 'Enter' && clickable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "remove-badge",
|
||||||
|
"condition": "key === 'Delete' && removable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "status",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Enter", "Delete"],
|
||||||
|
"ariaLabel": "{{text}} badge"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "inline-block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"padding": {
|
||||||
|
"top": 0,
|
||||||
|
"right": 1,
|
||||||
|
"bottom": 0,
|
||||||
|
"left": 1
|
||||||
|
},
|
||||||
|
"margin": {
|
||||||
|
"right": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"small": {
|
||||||
|
"height": 1,
|
||||||
|
"minWidth": 3
|
||||||
|
},
|
||||||
|
"medium": {
|
||||||
|
"height": 1,
|
||||||
|
"minWidth": 4
|
||||||
|
},
|
||||||
|
"large": {
|
||||||
|
"height": 1,
|
||||||
|
"minWidth": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "badge.md",
|
||||||
|
"width": 12,
|
||||||
|
"height": 1,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "text",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "New",
|
||||||
|
"description": "Badge text content",
|
||||||
|
"required": true,
|
||||||
|
"validation": {
|
||||||
|
"max": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "variant",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "default",
|
||||||
|
"description": "Badge style variant",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["default", "success", "warning", "error", "info", "secondary"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "size",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "medium",
|
||||||
|
"description": "Badge size variant",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["small", "medium", "large"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "removable",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Whether badge can be removed with X button"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "clickable",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Whether badge responds to click events"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "outlined",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Use outline style instead of filled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pill",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Use rounded pill shape"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "count",
|
||||||
|
"type": "number",
|
||||||
|
"defaultValue": null,
|
||||||
|
"description": "Numeric count for notification badges",
|
||||||
|
"validation": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 999
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dot",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Show as small dot indicator without text"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
216
skills/fluxwing-component-creator/templates/card.md
Normal file
216
skills/fluxwing-component-creator/templates/card.md
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
# Card Component
|
||||||
|
|
||||||
|
A flexible container for grouping related content with optional header, body, and footer sections.
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ {{title}} │
|
||||||
|
│ {{subtitle}} │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{content}} │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## With Footer
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ {{title}} │
|
||||||
|
│ {{subtitle}} │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{content}} │
|
||||||
|
│ │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ {{footer}} │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hover State
|
||||||
|
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ {{title}} ┃
|
||||||
|
┃ {{subtitle}} ┃
|
||||||
|
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||||
|
┃ ┃
|
||||||
|
┃ {{content}} ┃
|
||||||
|
┃ ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
## Focus State (Interactive Card)
|
||||||
|
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ {{title}} ✨ ┃
|
||||||
|
┃ {{subtitle}} ┃
|
||||||
|
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||||
|
┃ ┃
|
||||||
|
┃ {{content}} ┃
|
||||||
|
┃ ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `title` (string): The main heading of the card
|
||||||
|
- `subtitle` (string): Optional subtitle or secondary heading
|
||||||
|
- `content` (string): The main content or body text of the card
|
||||||
|
- `footer` (string): Optional footer content like actions or metadata
|
||||||
|
- `width` (number): Card width in characters (20-80, default 40)
|
||||||
|
- `hasHeader` (boolean): Whether to show the header section
|
||||||
|
- `hasFooter` (boolean): Whether to show the footer section
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: article (for content cards) or region (for layout cards)
|
||||||
|
- **Focusable**: Only if interactive (clickable)
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Tab: Focus if interactive
|
||||||
|
- Enter/Space: Activate if clickable
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-label`: Uses title for accessible name
|
||||||
|
- `aria-describedby`: Links to content for description
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Basic Content Card
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ Welcome to UXscii │
|
||||||
|
│ Getting started guide │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ UXscii helps you create beautiful │
|
||||||
|
│ ASCII representations of UI │
|
||||||
|
│ components for design documentation. │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Product Card
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ Professional Plan │
|
||||||
|
│ For growing teams │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ • Up to 100 components │
|
||||||
|
│ • Advanced customization │
|
||||||
|
│ • Priority support │
|
||||||
|
│ • Team collaboration │
|
||||||
|
│ │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ $29/month [Subscribe] │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Article Card
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ Building Better Interfaces │
|
||||||
|
│ Design Systems Blog │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Learn how to create consistent and │
|
||||||
|
│ scalable design systems that help │
|
||||||
|
│ your team build better products. │
|
||||||
|
│ │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ Published: Jan 15, 2024 5 min read │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notification Card
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ System Update │
|
||||||
|
│ Important notice │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ A new version of the system is │
|
||||||
|
│ available. Please update at your │
|
||||||
|
│ earliest convenience. │
|
||||||
|
│ │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ [Update Now] [Later] │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Interactive States
|
||||||
|
|
||||||
|
Cards can be:
|
||||||
|
- **Static**: Display-only containers
|
||||||
|
- **Clickable**: Navigate or trigger actions
|
||||||
|
- **Expandable**: Show/hide additional content
|
||||||
|
|
||||||
|
### Content Organization
|
||||||
|
|
||||||
|
- **Header**: Title, subtitle, metadata
|
||||||
|
- **Body**: Main content, lists, descriptions
|
||||||
|
- **Footer**: Actions, timestamps, secondary info
|
||||||
|
|
||||||
|
### Responsive Behavior
|
||||||
|
|
||||||
|
- **Width**: Adapts to container width
|
||||||
|
- **Content**: Text wraps within boundaries
|
||||||
|
- **Actions**: Footer buttons stack on narrow widths
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Border Styles
|
||||||
|
- `┌─┐` = Default border (light gray)
|
||||||
|
- `┏━┓` = Hover/focus border (primary color)
|
||||||
|
- `├─┤` = Section dividers
|
||||||
|
|
||||||
|
### Layout Patterns
|
||||||
|
- Header: Title + optional subtitle
|
||||||
|
- Body: Main content with padding
|
||||||
|
- Footer: Actions or metadata
|
||||||
|
|
||||||
|
### Spacing
|
||||||
|
- Internal padding: 1-2 characters
|
||||||
|
- Section spacing: Divider lines
|
||||||
|
- External margin: 2 characters bottom
|
||||||
|
|
||||||
|
## Card Variants
|
||||||
|
|
||||||
|
### Content Types
|
||||||
|
- **Article Card**: News, blog posts, documentation
|
||||||
|
- **Product Card**: Features, pricing, services
|
||||||
|
- **Profile Card**: User info, contact details
|
||||||
|
- **Status Card**: Notifications, alerts, updates
|
||||||
|
|
||||||
|
### Visual Styles
|
||||||
|
- **Elevated**: With shadow effect
|
||||||
|
- **Outlined**: Border-only styling
|
||||||
|
- **Flat**: Minimal visual separation
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- Container: Generic layout wrapper
|
||||||
|
- Modal: Overlay card variant
|
||||||
|
- Accordion: Expandable card variant
|
||||||
|
- List Item: Simplified card for lists
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
When implementing in actual UI frameworks:
|
||||||
|
|
||||||
|
1. Use semantic HTML structure (article, section, header, footer)
|
||||||
|
2. Implement proper focus management for interactive cards
|
||||||
|
3. Support responsive design with CSS Grid/Flexbox
|
||||||
|
4. Provide clear visual hierarchy with typography
|
||||||
|
5. Add appropriate hover and focus states
|
||||||
|
6. Support keyboard navigation for interactive elements
|
||||||
|
7. Implement proper ARIA attributes for accessibility
|
||||||
|
8. Consider loading states for dynamic content
|
||||||
|
9. Support various content layouts (text, images, actions)
|
||||||
|
10. Provide consistent spacing and alignment systems
|
||||||
147
skills/fluxwing-component-creator/templates/card.uxm
Normal file
147
skills/fluxwing-component-creator/templates/card.uxm
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
{
|
||||||
|
"id": "card",
|
||||||
|
"type": "card",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Card Component",
|
||||||
|
"description": "A flexible container for grouping related content with optional header, body, and footer sections",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["card", "container", "layout"],
|
||||||
|
"category": "layout",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "",
|
||||||
|
"subtitle": "",
|
||||||
|
"content": "",
|
||||||
|
"footer": "",
|
||||||
|
"elevated": true,
|
||||||
|
"padding": "medium",
|
||||||
|
"borderRadius": "medium",
|
||||||
|
"maxWidth": 40,
|
||||||
|
"hasHeader": true,
|
||||||
|
"hasFooter": false
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"background": "white",
|
||||||
|
"border": "solid-gray-light",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"shadow": "subtle"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"background": "white",
|
||||||
|
"border": "solid-gray-medium",
|
||||||
|
"borderWidth": 1,
|
||||||
|
"shadow": "elevated"
|
||||||
|
},
|
||||||
|
"triggers": ["hover"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "focus",
|
||||||
|
"properties": {
|
||||||
|
"background": "white",
|
||||||
|
"border": "solid-primary",
|
||||||
|
"borderWidth": 2,
|
||||||
|
"shadow": "elevated",
|
||||||
|
"outline": "primary"
|
||||||
|
},
|
||||||
|
"triggers": ["focus"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "emit-card-click"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "article",
|
||||||
|
"focusable": false,
|
||||||
|
"keyboardSupport": [],
|
||||||
|
"ariaLabel": "{{title}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"margin": {
|
||||||
|
"bottom": 2
|
||||||
|
},
|
||||||
|
"padding": {
|
||||||
|
"top": 1,
|
||||||
|
"right": 2,
|
||||||
|
"bottom": 1,
|
||||||
|
"left": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"width": 40,
|
||||||
|
"height": "auto",
|
||||||
|
"minWidth": 20,
|
||||||
|
"maxWidth": 80
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "card.md",
|
||||||
|
"width": 40,
|
||||||
|
"height": 12,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Card Title",
|
||||||
|
"description": "The main heading of the card"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "subtitle",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "Optional subtitle or secondary heading"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "content",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Card content goes here. This is the main body text that provides details or information.",
|
||||||
|
"description": "The main content or body text of the card"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "footer",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "Optional footer content like actions or metadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "width",
|
||||||
|
"type": "number",
|
||||||
|
"defaultValue": 40,
|
||||||
|
"description": "Card width in characters",
|
||||||
|
"validation": {
|
||||||
|
"min": 20,
|
||||||
|
"max": 80
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hasHeader",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Whether to show the header section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hasFooter",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Whether to show the footer section"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
261
skills/fluxwing-component-creator/templates/custom-widget.md
Normal file
261
skills/fluxwing-component-creator/templates/custom-widget.md
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
# Custom Widget Component
|
||||||
|
|
||||||
|
A specialized widget component that extends the basic card with custom properties for dashboard and data display.
|
||||||
|
|
||||||
|
## Dashboard Widget
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ {{data.icon}} {{data.title}} │⟳│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{data.value}} │
|
||||||
|
│ │
|
||||||
|
│ {{data.trend}} │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compact Widget
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ {{data.icon}} {{data.title}} │⟳│
|
||||||
|
│ {{data.value}} {{data.trend}} │
|
||||||
|
└─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading State
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ ⟳ Loading... │
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
||||||
|
│ │
|
||||||
|
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error State
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ ⚠ Error Loading Data │
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Failed to load widget data. │
|
||||||
|
│ Click to retry. │
|
||||||
|
│ │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Widget Types
|
||||||
|
|
||||||
|
### Analytics Widget
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ 📊 Sales Analytics │⟳│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ $45,678 │
|
||||||
|
│ │
|
||||||
|
│ ↗ +23% vs last month │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Status Widget
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ 🟢 System Status │⟳│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ All Systems │
|
||||||
|
│ Operational │
|
||||||
|
│ │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Counter Widget
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ 👥 Active Users │⟳│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 2,347 │
|
||||||
|
│ │
|
||||||
|
│ +12 new this hour │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Progress Widget
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ 🎯 Goal Progress │⟳│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ████████████████░░░░ 80% │
|
||||||
|
│ │
|
||||||
|
│ 8 of 10 completed │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interactive Elements
|
||||||
|
|
||||||
|
### Refreshable Widget
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ {{data.icon}} {{data.title}} │⟳│ ← Click to refresh
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{data.value}} │
|
||||||
|
│ │
|
||||||
|
│ Last updated: 2 min ago │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auto-Refresh Indicator
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ {{data.icon}} {{data.title}} │●│ ← Auto-refresh active
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{data.value}} │
|
||||||
|
│ │
|
||||||
|
│ Next refresh: 25s │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- **Standard**: 30×8 characters
|
||||||
|
- **Compact**: 25×4 characters
|
||||||
|
- **Large**: 40×10 characters
|
||||||
|
- **Full Width**: Responsive to container
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `widgetType` (string): Type of widget ("dashboard", "analytics", "status", "counter", "progress")
|
||||||
|
- `data` (object): Widget data with title, value, trend, and icon
|
||||||
|
- `title` (string): Widget heading
|
||||||
|
- `value` (string): Main display value
|
||||||
|
- `trend` (string): Trend indicator (optional)
|
||||||
|
- `icon` (string): Widget icon (emoji or symbol)
|
||||||
|
- `refreshable` (boolean): Whether widget can be manually refreshed
|
||||||
|
- `autoRefresh` (number): Auto-refresh interval in milliseconds (0 = disabled)
|
||||||
|
- `compact` (boolean): Use compact layout
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: widget or region
|
||||||
|
- **Focusable**: Yes, if interactive
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Enter/Space: Refresh widget (if refreshable)
|
||||||
|
- Tab: Navigate to next widget
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-label`: Widget title and current value
|
||||||
|
- `aria-live`: "polite" for auto-updating widgets
|
||||||
|
- `aria-busy`: "true" when loading
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Sales Dashboard Widget
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ 💰 Monthly Revenue │⟳│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ $127,890 │
|
||||||
|
│ │
|
||||||
|
│ ↗ +15.3% vs last month │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server Monitoring Widget
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ 🖥 Server Load │⟳│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ CPU: 45% │
|
||||||
|
│ RAM: 67% │
|
||||||
|
│ Disk: 23% │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task Progress Widget
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ ✅ Sprint Progress │⟳│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ████████████░░░░░░░░ 67% │
|
||||||
|
│ │
|
||||||
|
│ 12 of 18 tasks completed │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Data Loading
|
||||||
|
|
||||||
|
1. **Initial Load**: Show loading state while fetching data
|
||||||
|
2. **Error Handling**: Display error message and retry option
|
||||||
|
3. **Success**: Show formatted data with trend indicators
|
||||||
|
4. **Auto-Refresh**: Periodically update data in background
|
||||||
|
|
||||||
|
### Refresh Functionality
|
||||||
|
|
||||||
|
- **Manual Refresh**: Click refresh icon to update immediately
|
||||||
|
- **Auto-Refresh**: Configurable interval for automatic updates
|
||||||
|
- **Loading Feedback**: Visual indication during data fetch
|
||||||
|
- **Error Recovery**: Retry mechanism for failed requests
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
- **Loading**: Semi-transparent with spinner
|
||||||
|
- **Error**: Red border with error message
|
||||||
|
- **Success**: Normal display with data
|
||||||
|
- **Stale**: Subtle indication when data is outdated
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Visual Elements
|
||||||
|
- `┌─┐└┘─│` = Widget container borders
|
||||||
|
- `⟳` = Refresh button icon
|
||||||
|
- `●` = Auto-refresh active indicator
|
||||||
|
- `░` = Loading placeholder blocks
|
||||||
|
- `⚠` = Error/warning indicator
|
||||||
|
- Emoji icons for widget types (📊, 💰, 🟢, etc.)
|
||||||
|
|
||||||
|
### Status Colors
|
||||||
|
- **Success**: Green indicators for positive trends
|
||||||
|
- **Warning**: Yellow for caution states
|
||||||
|
- **Error**: Red for error states
|
||||||
|
- **Neutral**: Gray for normal/inactive states
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- **Card**: Base component that this extends
|
||||||
|
- **Dashboard**: Container for multiple widgets
|
||||||
|
- **Chart**: Data visualization components
|
||||||
|
- **Metric**: Simple numeric display components
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This widget component extends the base card component with:
|
||||||
|
|
||||||
|
1. **Data Management**: Built-in data fetching and state management
|
||||||
|
2. **Auto-Refresh**: Configurable automatic data updates
|
||||||
|
3. **Error Handling**: Robust error states and recovery
|
||||||
|
4. **Accessibility**: Full screen reader and keyboard support
|
||||||
|
5. **Customization**: Flexible widget types and layouts
|
||||||
|
6. **Performance**: Efficient rendering and update mechanisms
|
||||||
|
|
||||||
|
## Extension Points
|
||||||
|
|
||||||
|
- **Custom Widget Types**: Add new widget variations
|
||||||
|
- **Data Sources**: Integrate with different APIs
|
||||||
|
- **Visualization**: Add charts and graphs
|
||||||
|
- **Theming**: Custom color schemes and layouts
|
||||||
|
- **Interactions**: Additional click handlers and actions
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"id": "custom-widget",
|
||||||
|
"type": "custom",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"extends": "card",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Custom Widget",
|
||||||
|
"description": "A specialized widget component that extends the basic card with custom properties",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["widget", "custom", "card"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"widgetType": "dashboard",
|
||||||
|
"data": {
|
||||||
|
"title": "Sales Overview",
|
||||||
|
"value": "1,234",
|
||||||
|
"trend": "+12%",
|
||||||
|
"icon": "📊"
|
||||||
|
},
|
||||||
|
"refreshable": true,
|
||||||
|
"autoRefresh": 30000,
|
||||||
|
"compact": false
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "loading",
|
||||||
|
"properties": {
|
||||||
|
"opacity": 0.6,
|
||||||
|
"cursor": "wait"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "error",
|
||||||
|
"properties": {
|
||||||
|
"borderColor": "red",
|
||||||
|
"background": "error-light"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "refresh-widget",
|
||||||
|
"condition": "refreshable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "change",
|
||||||
|
"action": "auto-refresh",
|
||||||
|
"condition": "autoRefresh > 0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "custom-widget.md",
|
||||||
|
"width": 30,
|
||||||
|
"height": 8,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "widgetType",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "dashboard",
|
||||||
|
"description": "Type of widget to display"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "{\"title\":\"Widget Title\",\"value\":\"N/A\",\"trend\":\"\",\"icon\":\"📊\"}",
|
||||||
|
"description": "Widget data to display (JSON string)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "refreshable",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Whether widget can be refreshed"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
196
skills/fluxwing-component-creator/templates/email-input.md
Normal file
196
skills/fluxwing-component-creator/templates/email-input.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# Email Input Field Component
|
||||||
|
|
||||||
|
A specialized input field for email addresses with built-in validation.
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
{{label}} *
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ {{value || placeholder}} │ @
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
{{helpText}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Focus State
|
||||||
|
|
||||||
|
```
|
||||||
|
{{label}} *
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ {{value || placeholder}} ┃ @
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
{{helpText}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Valid State
|
||||||
|
|
||||||
|
```
|
||||||
|
{{label}} *
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ {{value}} │ ✓
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
{{helpText}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error State
|
||||||
|
|
||||||
|
```
|
||||||
|
{{label}} *
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ {{value || placeholder}} │ ⚠️
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
❌ {{errorMessage}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabled State
|
||||||
|
|
||||||
|
```
|
||||||
|
{{label}} *
|
||||||
|
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
|
||||||
|
│ {{value || placeholder}} │ @
|
||||||
|
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
|
||||||
|
{{helpText}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `label` (string): The label displayed above the input field
|
||||||
|
- `placeholder` (string): Placeholder text for email format guidance
|
||||||
|
- `value` (string): Current email address value
|
||||||
|
- `width` (number): Input field width in characters (20-60, default 35)
|
||||||
|
- `errorMessage` (string): Validation error message
|
||||||
|
- `helpText` (string): Privacy or usage information
|
||||||
|
- `isValid` (boolean): Whether the current email is valid
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: textbox
|
||||||
|
- **Focusable**: Yes
|
||||||
|
- **Input Mode**: email (shows @ symbol on mobile keyboards)
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Tab: Move focus to/from field
|
||||||
|
- All text input keys: Enter email
|
||||||
|
- @ key: Email-specific character
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-label`: Email address input field
|
||||||
|
- `aria-required`: true (email usually required)
|
||||||
|
- `aria-invalid`: Set when email format is invalid
|
||||||
|
- `aria-describedby`: Links to help text or error message
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Basic Email Input
|
||||||
|
```
|
||||||
|
Email Address *
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ name@example.com │ @
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
We'll never share your email address
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Valid Email
|
||||||
|
```
|
||||||
|
Work Email *
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ john.doe@company.com │ ✓
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
Used for account notifications
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Validation Error
|
||||||
|
```
|
||||||
|
Email Address *
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ invalid-email │ ⚠️
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
❌ Please enter a valid email address
|
||||||
|
```
|
||||||
|
|
||||||
|
### Registration Form
|
||||||
|
```
|
||||||
|
Email Address *
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ user@domain.co │ ✓
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
This will be your login username
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Email Validation
|
||||||
|
|
||||||
|
Real-time validation checks:
|
||||||
|
1. **Format**: Contains @ symbol and domain
|
||||||
|
2. **Structure**: Basic email pattern matching
|
||||||
|
3. **Domain**: Has valid domain extension
|
||||||
|
4. **Length**: Within reasonable email limits
|
||||||
|
|
||||||
|
### State Transitions
|
||||||
|
|
||||||
|
1. **Default → Focus**: User clicks or tabs into field
|
||||||
|
2. **Focus → Typing**: User starts entering email
|
||||||
|
3. **Typing → Valid**: Email format becomes valid
|
||||||
|
4. **Typing → Error**: Email format is invalid
|
||||||
|
5. **Valid/Error → Default**: User leaves field
|
||||||
|
|
||||||
|
### Validation Timing
|
||||||
|
|
||||||
|
- **Real-time**: Basic format checking as user types
|
||||||
|
- **On Blur**: Complete validation when leaving field
|
||||||
|
- **On Submit**: Final validation before form submission
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Visual Indicators
|
||||||
|
- `@` = Email input indicator
|
||||||
|
- `✓` = Valid email confirmation
|
||||||
|
- `⚠️` = Format error warning
|
||||||
|
- `*` = Required field indicator
|
||||||
|
|
||||||
|
### Border Styles
|
||||||
|
- `┌─┐` = Default border
|
||||||
|
- `┏━┓` = Focus border (primary color)
|
||||||
|
- `┌ ─ ┐` = Disabled border (dashed)
|
||||||
|
|
||||||
|
### Colors
|
||||||
|
- Default: Standard input styling
|
||||||
|
- Valid: Light green background with green border
|
||||||
|
- Error: Light red background with red border
|
||||||
|
- Focus: Primary color border
|
||||||
|
|
||||||
|
## Email-Specific Features
|
||||||
|
|
||||||
|
### Autocomplete Support
|
||||||
|
- Suggests common email domains
|
||||||
|
- Remembers previously entered emails
|
||||||
|
- Integration with browser autofill
|
||||||
|
|
||||||
|
### Mobile Optimization
|
||||||
|
- Shows email-optimized keyboard
|
||||||
|
- Includes @ and . keys prominently
|
||||||
|
- Prevents autocorrect/autocapitalize
|
||||||
|
|
||||||
|
### Privacy Considerations
|
||||||
|
- Clear privacy policy reference
|
||||||
|
- Secure handling of email data
|
||||||
|
- Optional email verification flow
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- Text Input: Base input component
|
||||||
|
- Password Input: For password entry
|
||||||
|
- Confirmation Email Input: For email verification
|
||||||
|
- Newsletter Signup: Specialized email collection
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
When implementing in actual UI frameworks:
|
||||||
|
|
||||||
|
1. Use `type="email"` for HTML5 validation
|
||||||
|
2. Set `autocomplete="email"` for autofill
|
||||||
|
3. Set `inputmode="email"` for mobile keyboards
|
||||||
|
4. Implement proper email validation regex
|
||||||
|
5. Consider email verification workflow
|
||||||
|
6. Provide clear privacy information
|
||||||
|
7. Handle international domain names
|
||||||
|
8. Support paste operations for long emails
|
||||||
168
skills/fluxwing-component-creator/templates/email-input.uxm
Normal file
168
skills/fluxwing-component-creator/templates/email-input.uxm
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
{
|
||||||
|
"id": "email-input",
|
||||||
|
"type": "input",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Email Input Field",
|
||||||
|
"description": "A specialized input field for email addresses with built-in validation",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["input", "email", "form", "validation"],
|
||||||
|
"category": "input",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"extends": "text-input",
|
||||||
|
"props": {
|
||||||
|
"label": "Email Address",
|
||||||
|
"placeholder": "name@example.com",
|
||||||
|
"value": "",
|
||||||
|
"disabled": false,
|
||||||
|
"required": true,
|
||||||
|
"autocomplete": "email",
|
||||||
|
"ariaLabel": "",
|
||||||
|
"errorMessage": "",
|
||||||
|
"helpText": "We'll never share your email address"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"background": "white",
|
||||||
|
"textColor": "black",
|
||||||
|
"border": "solid-gray",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "focus",
|
||||||
|
"properties": {
|
||||||
|
"background": "white",
|
||||||
|
"textColor": "black",
|
||||||
|
"border": "solid-primary",
|
||||||
|
"borderWidth": 2,
|
||||||
|
"outline": "primary"
|
||||||
|
},
|
||||||
|
"triggers": ["focus"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "valid",
|
||||||
|
"properties": {
|
||||||
|
"background": "success-light",
|
||||||
|
"textColor": "success-dark",
|
||||||
|
"border": "solid-success",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "error",
|
||||||
|
"properties": {
|
||||||
|
"background": "error-light",
|
||||||
|
"textColor": "error-dark",
|
||||||
|
"border": "solid-error",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disabled",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-lighter",
|
||||||
|
"textColor": "gray-medium",
|
||||||
|
"border": "dashed-gray",
|
||||||
|
"borderWidth": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "change",
|
||||||
|
"action": "validate-email-format"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "blur",
|
||||||
|
"action": "validate-email-complete"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "textbox",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Tab", "Shift+Tab", "All text input keys"],
|
||||||
|
"ariaLabel": "{{ariaLabel || label}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"margin": {
|
||||||
|
"bottom": 1
|
||||||
|
},
|
||||||
|
"padding": {
|
||||||
|
"top": 1,
|
||||||
|
"right": 1,
|
||||||
|
"bottom": 1,
|
||||||
|
"left": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"width": 35,
|
||||||
|
"height": 3,
|
||||||
|
"minWidth": 20,
|
||||||
|
"maxWidth": 60
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "email-input.md",
|
||||||
|
"width": 35,
|
||||||
|
"height": 5,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "label",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Email Address",
|
||||||
|
"description": "The label displayed above the input field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "placeholder",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "name@example.com",
|
||||||
|
"description": "Placeholder text shown when input is empty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "Current email address value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "width",
|
||||||
|
"type": "number",
|
||||||
|
"defaultValue": 35,
|
||||||
|
"description": "Input field width in characters",
|
||||||
|
"validation": {
|
||||||
|
"min": 20,
|
||||||
|
"max": 60
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "errorMessage",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "",
|
||||||
|
"description": "Error message to display below the input"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "helpText",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "We'll never share your email address",
|
||||||
|
"description": "Help text to display below the input"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "isValid",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Whether the current email is valid"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
309
skills/fluxwing-component-creator/templates/form.md
Normal file
309
skills/fluxwing-component-creator/templates/form.md
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
# Form Container Component
|
||||||
|
|
||||||
|
A comprehensive form container with field grouping, validation state management, and submission handling.
|
||||||
|
|
||||||
|
## Standard Vertical Form Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────┐
|
||||||
|
│ {{title}} │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{fields[0].label}} {{fields[0].required ? '*' : ''}} │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ {{fields[0].placeholder}} │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ {{fields[1].label}} {{fields[1].required ? '*' : ''}} │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ {{fields[1].placeholder}} │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ {{fields[2].label}} {{fields[2].required ? '*' : ''}} │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ {{fields[2].placeholder}} │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ {{showReset ? '┌─────────────┐' : ''}} │
|
||||||
|
│ │ {{submitText}} │ {{showReset ? '│ Reset │' : ''}} │
|
||||||
|
│ └─────────────┘ {{showReset ? '└─────────────┘' : ''}} │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Horizontal Form Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
|
│ {{title}} │
|
||||||
|
├────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{fields[0].label}}* {{fields[1].label}}* │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ {{fields[0].placeholder}} │ │ {{fields[1].placeholder}} │ │
|
||||||
|
│ └─────────────────┘ └─────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ {{fields[2].label}} │
|
||||||
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ {{fields[2].placeholder}} │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └──────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ ┌─────────────┐ │
|
||||||
|
│ │ {{submitText}} │ │ Reset │ │
|
||||||
|
│ └─────────────┘ └─────────────┘ │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation States
|
||||||
|
|
||||||
|
### Valid Form
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────┐
|
||||||
|
│ Contact Form │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Full Name * ✓ │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ John Doe │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Email Address * ✓ │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ john@example.com │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░░░░ │
|
||||||
|
│ ▓ Submit ▓ ░ Reset ░ │
|
||||||
|
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░░░░ │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form with Validation Errors
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────┐
|
||||||
|
│ Contact Form │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Full Name * ✗ │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ ⚠ This field is required │
|
||||||
|
│ │
|
||||||
|
│ Email Address * ✗ │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ invalid-email │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ ⚠ Please enter a valid email address │
|
||||||
|
│ │
|
||||||
|
│ ░░░░░░░░░░░░░ ░░░░░░░░░░░░░ │
|
||||||
|
│ ░ Submit ░ ░ Reset ░ │
|
||||||
|
│ ░░░░░░░░░░░░░ ░░░░░░░░░░░░░ │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Grid Layout (2x2)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ {{title}} │
|
||||||
|
├──────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ First Name * Last Name * │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ First name │ │ Last name │ │
|
||||||
|
│ └─────────────────┘ └─────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Email * Phone │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ Email address │ │ Phone number │ │
|
||||||
|
│ └─────────────────┘ └─────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ ┌─────────────┐ │
|
||||||
|
│ │ {{submitText}} │ │ Reset │ │
|
||||||
|
│ └─────────────┘ └─────────────┘ │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compact Form
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Login │
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ Username: ┌───────────────────────┐ │
|
||||||
|
│ │ Enter username │ │
|
||||||
|
│ └───────────────────────┘ │
|
||||||
|
│ Password: ┌───────────────────────┐ │
|
||||||
|
│ │ •••••••••••••••••••• │ │
|
||||||
|
│ └───────────────────────┘ │
|
||||||
|
│ ┌─────────┐ ┌─────────┐ │
|
||||||
|
│ │ Login │ │ Cancel │ │
|
||||||
|
│ └─────────┘ └─────────┘ │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- **Standard Width**: 50-80 characters
|
||||||
|
- **Compact Width**: 30-50 characters
|
||||||
|
- **Height**: Variable based on field count
|
||||||
|
- **Field Height**: 3 characters (single line), 5+ characters (textarea)
|
||||||
|
- **Button Height**: 3 characters
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `title` (string): Form heading text (max 50 characters)
|
||||||
|
- `fields` (array, required): Form field definitions
|
||||||
|
- Each field: `{id, type, label, placeholder, required, validation}`
|
||||||
|
- Min: 1 field, Max: 15 fields
|
||||||
|
- Types: "text", "email", "password", "textarea", "select", "checkbox"
|
||||||
|
- `layout` (string): "vertical", "horizontal", or "grid" (default: "vertical")
|
||||||
|
- `submitText` (string): Submit button label (default: "Submit")
|
||||||
|
- `showReset` (boolean): Whether to display reset button (default: true)
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: form
|
||||||
|
- **Focusable**: Yes, tab navigation through fields
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Tab/Shift+Tab: Navigate between fields
|
||||||
|
- Enter: Submit form (if valid)
|
||||||
|
- Ctrl+Enter: Force submit
|
||||||
|
- Escape: Cancel/reset (if applicable)
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-label`: Form title or purpose
|
||||||
|
- `aria-required`: "true" for required fields
|
||||||
|
- `aria-invalid`: "true" for fields with errors
|
||||||
|
- `aria-describedby`: Link to error messages
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Contact Form
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────┐
|
||||||
|
│ Contact Us │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Your Name * │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Enter your full name │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Email Address * │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ your@email.com │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Subject │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Brief description │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Message │
|
||||||
|
│ ┌────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Your message here... │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░░░░ │
|
||||||
|
│ ▓ Send Message │ ░ Clear ░ │
|
||||||
|
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░░░░ │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Registration Form
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
|
│ Create Account │
|
||||||
|
├──────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ First Name * Last Name * │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ First name │ │ Last name │ │
|
||||||
|
│ └─────────────────┘ └─────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Email Address * │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────┐│
|
||||||
|
│ │ Enter a valid email address ││
|
||||||
|
│ └─────────────────────────────────────────────────────────┘│
|
||||||
|
│ │
|
||||||
|
│ Password * Confirm Password * │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ ••••••••••••••• │ │ •••••••••••••••••••••••••••••• │ │
|
||||||
|
│ └─────────────────┘ └─────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ☐ I agree to the Terms of Service │
|
||||||
|
│ │
|
||||||
|
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░░░░ │
|
||||||
|
│ ▓ Create Account ▓ ░ Cancel ░ │
|
||||||
|
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░░░░ │
|
||||||
|
└──────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Form Validation
|
||||||
|
|
||||||
|
1. **Real-time Validation**: Fields validate as user types or on blur
|
||||||
|
2. **Form-level Validation**: Overall form state based on all fields
|
||||||
|
3. **Error Display**: Clear error messages below invalid fields
|
||||||
|
4. **Submit Prevention**: Disabled submit button for invalid forms
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
- **Pristine**: Form has not been modified
|
||||||
|
- **Dirty**: Form has been modified
|
||||||
|
- **Valid**: All validation rules pass
|
||||||
|
- **Invalid**: One or more validation errors
|
||||||
|
- **Submitting**: Form submission in progress
|
||||||
|
- **Submitted**: Form successfully submitted
|
||||||
|
|
||||||
|
### Field Types
|
||||||
|
|
||||||
|
- **Text**: Single-line text input
|
||||||
|
- **Email**: Email validation with format checking
|
||||||
|
- **Password**: Hidden input with strength indicators
|
||||||
|
- **Textarea**: Multi-line text input
|
||||||
|
- **Select**: Dropdown selection
|
||||||
|
- **Checkbox**: Boolean toggle options
|
||||||
|
- **Radio**: Single selection from multiple options
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Visual Elements
|
||||||
|
- `┌─┐└┘─│` = Form and field borders
|
||||||
|
- `▓` = Primary submit button
|
||||||
|
- `░` = Secondary/reset button
|
||||||
|
- `✓` = Valid field indicator
|
||||||
|
- `✗` = Invalid field indicator
|
||||||
|
- `⚠` = Error/warning symbol
|
||||||
|
- `*` = Required field marker
|
||||||
|
|
||||||
|
### Status Colors (represented by patterns)
|
||||||
|
- Solid borders = Default/active state
|
||||||
|
- Dashed borders = Disabled state
|
||||||
|
- Double borders = Focus state
|
||||||
|
- `▓` pattern = Primary/submit actions
|
||||||
|
- `░` pattern = Secondary/cancel actions
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- **Input Field**: Individual form field components
|
||||||
|
- **Button**: Submit and reset button components
|
||||||
|
- **Validation Message**: Error and success message components
|
||||||
|
- **Field Group**: Related field grouping components
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This ASCII representation demonstrates form structure and validation states. When implementing:
|
||||||
|
|
||||||
|
1. **Progressive Enhancement**: Start with basic HTML form functionality
|
||||||
|
2. **Validation Strategy**: Combine client-side and server-side validation
|
||||||
|
3. **Error Handling**: Graceful error recovery and clear messaging
|
||||||
|
4. **Accessibility**: Full keyboard navigation and screen reader support
|
||||||
|
5. **Mobile Responsiveness**: Adapt layout for small screens
|
||||||
|
6. **Security**: Proper data sanitization and CSRF protection
|
||||||
196
skills/fluxwing-component-creator/templates/form.uxm
Normal file
196
skills/fluxwing-component-creator/templates/form.uxm
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
{
|
||||||
|
"id": "form",
|
||||||
|
"type": "form",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Form Container",
|
||||||
|
"description": "A form container with field grouping, validation state management, and submission handling",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["form", "container", "validation"],
|
||||||
|
"category": "input",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "Contact Form",
|
||||||
|
"method": "POST",
|
||||||
|
"action": "/submit",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"id": "name",
|
||||||
|
"type": "text",
|
||||||
|
"label": "Full Name",
|
||||||
|
"placeholder": "Enter your full name",
|
||||||
|
"required": true,
|
||||||
|
"validation": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "email",
|
||||||
|
"type": "email",
|
||||||
|
"label": "Email Address",
|
||||||
|
"placeholder": "your@email.com",
|
||||||
|
"required": true,
|
||||||
|
"validation": "email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "message",
|
||||||
|
"type": "textarea",
|
||||||
|
"label": "Message",
|
||||||
|
"placeholder": "Your message here...",
|
||||||
|
"required": false,
|
||||||
|
"validation": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"submitText": "Submit",
|
||||||
|
"resetText": "Reset",
|
||||||
|
"showReset": true,
|
||||||
|
"layout": "vertical",
|
||||||
|
"spacing": "normal"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"border": "solid",
|
||||||
|
"background": "white",
|
||||||
|
"textColor": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "valid",
|
||||||
|
"properties": {
|
||||||
|
"border": "solid",
|
||||||
|
"borderColor": "success",
|
||||||
|
"background": "success-light"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "invalid",
|
||||||
|
"properties": {
|
||||||
|
"border": "solid",
|
||||||
|
"borderColor": "error",
|
||||||
|
"background": "error-light"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "submitting",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-light",
|
||||||
|
"textColor": "gray-dark",
|
||||||
|
"cursor": "wait"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "submit",
|
||||||
|
"action": "validate-and-submit",
|
||||||
|
"condition": "form.valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "reset",
|
||||||
|
"action": "clear-all-fields"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "field-change",
|
||||||
|
"action": "validate-field",
|
||||||
|
"condition": "field.validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "submit-form",
|
||||||
|
"condition": "key === 'Enter' && ctrlKey"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validation": {
|
||||||
|
"realTime": true,
|
||||||
|
"showErrorsOnBlur": true,
|
||||||
|
"preventSubmitIfInvalid": true,
|
||||||
|
"customValidators": []
|
||||||
|
},
|
||||||
|
"accessibility": {
|
||||||
|
"role": "form",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Tab", "Shift+Tab", "Enter"],
|
||||||
|
"ariaLabel": "{{title}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"padding": {
|
||||||
|
"top": 2,
|
||||||
|
"right": 3,
|
||||||
|
"bottom": 2,
|
||||||
|
"left": 3
|
||||||
|
},
|
||||||
|
"margin": {
|
||||||
|
"bottom": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"minWidth": 30,
|
||||||
|
"maxWidth": 80,
|
||||||
|
"height": "auto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "form.md",
|
||||||
|
"width": 50,
|
||||||
|
"height": 20,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Contact Form",
|
||||||
|
"description": "Form heading/title text",
|
||||||
|
"required": false,
|
||||||
|
"validation": {
|
||||||
|
"max": 50
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fields",
|
||||||
|
"type": "array",
|
||||||
|
"defaultValue": [
|
||||||
|
{"id": "name", "type": "text", "label": "Full Name", "required": true},
|
||||||
|
{"id": "email", "type": "email", "label": "Email", "required": true},
|
||||||
|
{"id": "message", "type": "textarea", "label": "Message", "required": false}
|
||||||
|
],
|
||||||
|
"description": "Form fields with labels, types, and validation",
|
||||||
|
"required": true,
|
||||||
|
"validation": {
|
||||||
|
"min": 1,
|
||||||
|
"max": 15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "layout",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "vertical",
|
||||||
|
"description": "Field layout arrangement",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["vertical", "horizontal", "grid"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "submitText",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Submit",
|
||||||
|
"description": "Submit button text",
|
||||||
|
"validation": {
|
||||||
|
"max": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "showReset",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Whether to show reset button"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
309
skills/fluxwing-component-creator/templates/list.md
Normal file
309
skills/fluxwing-component-creator/templates/list.md
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
# List Component
|
||||||
|
|
||||||
|
Ordered and unordered list component with various display patterns, selection support, and keyboard navigation.
|
||||||
|
|
||||||
|
## Unordered List (Default)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ {{marker}} {{items[0].text}} │
|
||||||
|
│ {{marker}} {{items[1].text}} │
|
||||||
|
│ {{marker}} {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ordered List
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ 1. {{items[0].text}} │
|
||||||
|
│ 2. {{items[1].text}} │
|
||||||
|
│ 3. {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Selectable List with Selection
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ {{items[0].text}} │
|
||||||
|
│ ▓▓{{items[1].text}}▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │
|
||||||
|
│ {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checklist Variant
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ ☐ {{items[0].text}} │
|
||||||
|
│ ☑ {{items[1].text}} │
|
||||||
|
│ ☐ {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multi-Select List
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ ☑ {{items[0].text}} │
|
||||||
|
│ ☑ {{items[1].text}} │
|
||||||
|
│ ☐ {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compact List (No Borders)
|
||||||
|
|
||||||
|
```
|
||||||
|
{{marker}} {{items[0].text}}
|
||||||
|
{{marker}} {{items[1].text}}
|
||||||
|
{{marker}} {{items[2].text}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Striped List
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ {{marker}} {{items[0].text}} │
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
│ {{marker}} {{items[1].text}} │
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
│ {{marker}} {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Definition List
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ {{items[0].text}} │
|
||||||
|
│ {{items[0].description}} │
|
||||||
|
│ │
|
||||||
|
│ {{items[1].text}} │
|
||||||
|
│ {{items[1].description}} │
|
||||||
|
│ │
|
||||||
|
│ {{items[2].text}} │
|
||||||
|
│ {{items[2].description}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Menu-Style List
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ > {{items[0].text}} │
|
||||||
|
│ {{items[1].text}} │
|
||||||
|
│ {{items[2].text}} │
|
||||||
|
│ {{items[3].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Numbered List Variants
|
||||||
|
|
||||||
|
### Decimal (Default)
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ 1. {{items[0].text}} │
|
||||||
|
│ 2. {{items[1].text}} │
|
||||||
|
│ 3. {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alphabetic
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ a. {{items[0].text}} │
|
||||||
|
│ b. {{items[1].text}} │
|
||||||
|
│ c. {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Roman Numerals
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ i. {{items[0].text}} │
|
||||||
|
│ ii. {{items[1].text}} │
|
||||||
|
│ iii.{{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabled Items
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ {{marker}} {{items[0].text}} │
|
||||||
|
│ ░ {{items[1].text}} (disabled) │
|
||||||
|
│ {{marker}} {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interactive States
|
||||||
|
|
||||||
|
### Hover State
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ {{marker}} {{items[0].text}} │
|
||||||
|
│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│
|
||||||
|
│▓{{marker}} {{items[1].text}} ▓│
|
||||||
|
│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│
|
||||||
|
│ {{marker}} {{items[2].text}} │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- **Standard Width**: 30-60 characters
|
||||||
|
- **Compact Width**: 20-40 characters
|
||||||
|
- **Item Height**: 1 character (compact), 2-3 characters (normal)
|
||||||
|
- **Container Height**: Variable based on item count
|
||||||
|
- **Marker Width**: 2-4 characters depending on type
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `items` (array, required): List items with text and metadata
|
||||||
|
- Each item: `{id, text, selected?, disabled?, description?}`
|
||||||
|
- Min: 1 item, Max: 50 items
|
||||||
|
- `type` (string): "unordered", "ordered", "definition", or "checklist"
|
||||||
|
- `marker` (string): Bullet character for unordered lists (default: "•")
|
||||||
|
- `numbering` (string): Style for ordered lists ("decimal", "alpha", "roman", "roman-upper")
|
||||||
|
- `selectable` (boolean): Whether items can be selected (default: true)
|
||||||
|
- `multiSelect` (boolean): Allow multiple selections (default: false)
|
||||||
|
- `bordered` (boolean): Show container border (default: true)
|
||||||
|
- `compact` (boolean): Use minimal spacing (default: false)
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: list (or listbox if selectable)
|
||||||
|
- **Item Role**: listitem (or option if selectable)
|
||||||
|
- **Focusable**: Yes, if selectable
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Arrow Up/Down: Navigate between items
|
||||||
|
- Enter/Space: Select item
|
||||||
|
- Ctrl+A: Select all (if multiSelect)
|
||||||
|
- Home/End: Jump to first/last item
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-multiselectable`: "true" if multiSelect enabled
|
||||||
|
- `aria-selected`: "true" for selected items
|
||||||
|
- `aria-disabled`: "true" for disabled items
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Navigation Menu
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ > Dashboard │
|
||||||
|
│ Products │
|
||||||
|
│ Orders │
|
||||||
|
│ Customers │
|
||||||
|
│ Settings │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task List
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ ☑ Complete project proposal │
|
||||||
|
│ ☑ Review team feedback │
|
||||||
|
│ ☐ Update documentation │
|
||||||
|
│ ☐ Schedule client meeting │
|
||||||
|
│ ☐ Prepare presentation │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Browser
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ 📁 Documents │
|
||||||
|
│ 📁 Downloads │
|
||||||
|
│ 📁 Pictures │
|
||||||
|
│ 📄 README.md │
|
||||||
|
│ 📄 package.json │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Settings Menu
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ • General Settings │
|
||||||
|
│ • Privacy & Security │
|
||||||
|
│ • Notifications │
|
||||||
|
│ • Account Management │
|
||||||
|
│ • Help & Support │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Select Options
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ ☑ Email Notifications │
|
||||||
|
│ ☐ SMS Alerts │
|
||||||
|
│ ☑ Push Notifications │
|
||||||
|
│ ☐ Weekly Digest │
|
||||||
|
│ ☑ Marketing Updates │
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Selection Management
|
||||||
|
|
||||||
|
1. **Single Select**: Only one item selected at a time
|
||||||
|
2. **Multi Select**: Multiple items can be selected simultaneously
|
||||||
|
3. **Toggle Selection**: Click to select/deselect items
|
||||||
|
4. **Keyboard Navigation**: Arrow keys move focus, Enter/Space selects
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
- **Default**: No items selected
|
||||||
|
- **Selected**: One or more items selected
|
||||||
|
- **Focused**: Current keyboard focus position
|
||||||
|
- **Disabled**: Items that cannot be interacted with
|
||||||
|
|
||||||
|
### Visual Feedback
|
||||||
|
|
||||||
|
- **Selection**: Highlighted background for selected items
|
||||||
|
- **Hover**: Temporary highlight on mouse over
|
||||||
|
- **Focus**: Keyboard focus indicator
|
||||||
|
- **Disabled**: Grayed out appearance
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Visual Elements
|
||||||
|
- `┌─┐└┘─│` = List container borders
|
||||||
|
- `{{marker}}` = Configurable bullet points (•, -, *, ►)
|
||||||
|
- `▓` = Selection/hover background
|
||||||
|
- `░` = Disabled state indicator
|
||||||
|
- `☐☑` = Checkbox states (unchecked/checked)
|
||||||
|
- `>` = Active/current item indicator
|
||||||
|
|
||||||
|
### List Markers
|
||||||
|
- **Bullets**: •, -, *, ►, ○, ■, ♦
|
||||||
|
- **Numbers**: 1., a., i., I., (1), [1]
|
||||||
|
- **Custom**: Any single character or short string
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- **Menu**: Dropdown or context menu with list items
|
||||||
|
- **Navigation**: Hierarchical navigation lists
|
||||||
|
- **Table**: Tabular data display with rows
|
||||||
|
- **Tree**: Hierarchical list with expand/collapse
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This ASCII representation demonstrates list patterns and interactions. When implementing:
|
||||||
|
|
||||||
|
1. **Virtual Scrolling**: Handle large lists efficiently
|
||||||
|
2. **Keyboard Navigation**: Full accessibility support
|
||||||
|
3. **Selection Persistence**: Maintain selection state across updates
|
||||||
|
4. **Performance**: Optimize rendering for large item counts
|
||||||
|
5. **Customization**: Support custom markers and styling
|
||||||
|
6. **Search/Filter**: Add search capabilities for long lists
|
||||||
|
|
||||||
|
## Variants
|
||||||
|
|
||||||
|
- **Simple List**: Basic display without interaction
|
||||||
|
- **Selectable List**: Single or multi-selection support
|
||||||
|
- **Menu List**: Navigation and action items
|
||||||
|
- **Checklist**: Task management with completion states
|
||||||
|
- **Definition List**: Term and description pairs
|
||||||
|
- **Nested List**: Hierarchical list structures
|
||||||
206
skills/fluxwing-component-creator/templates/list.uxm
Normal file
206
skills/fluxwing-component-creator/templates/list.uxm
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
{
|
||||||
|
"id": "list",
|
||||||
|
"type": "list",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "List Component",
|
||||||
|
"description": "Ordered and unordered list component with various display patterns and interaction support",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["list", "menu", "navigation", "display"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"text": "First item",
|
||||||
|
"selected": false,
|
||||||
|
"disabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"text": "Second item",
|
||||||
|
"selected": true,
|
||||||
|
"disabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"text": "Third item",
|
||||||
|
"selected": false,
|
||||||
|
"disabled": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "unordered",
|
||||||
|
"selectable": true,
|
||||||
|
"multiSelect": false,
|
||||||
|
"variant": "default",
|
||||||
|
"marker": "•",
|
||||||
|
"numbering": "decimal",
|
||||||
|
"compact": false,
|
||||||
|
"bordered": true,
|
||||||
|
"striped": false
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"background": "transparent",
|
||||||
|
"textColor": "default",
|
||||||
|
"border": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "selected",
|
||||||
|
"properties": {
|
||||||
|
"background": "primary-light",
|
||||||
|
"textColor": "primary-dark",
|
||||||
|
"border": "solid",
|
||||||
|
"fontWeight": "medium"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-light",
|
||||||
|
"textColor": "default"
|
||||||
|
},
|
||||||
|
"triggers": ["mouseenter"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disabled",
|
||||||
|
"properties": {
|
||||||
|
"background": "transparent",
|
||||||
|
"textColor": "gray-light",
|
||||||
|
"opacity": 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "select-item",
|
||||||
|
"condition": "selectable && !disabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "select-next",
|
||||||
|
"condition": "key === 'ArrowDown'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "select-previous",
|
||||||
|
"condition": "key === 'ArrowUp'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "select-item",
|
||||||
|
"condition": "key === 'Enter' || key === ' '"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "toggle-select",
|
||||||
|
"condition": "key === 'Space' && multiSelect"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "list",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["ArrowUp", "ArrowDown", "Enter", "Space", "Home", "End"],
|
||||||
|
"ariaLabel": "List of items"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"padding": {
|
||||||
|
"top": 1,
|
||||||
|
"right": 1,
|
||||||
|
"bottom": 1,
|
||||||
|
"left": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"minWidth": 15,
|
||||||
|
"maxWidth": 60,
|
||||||
|
"height": "auto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "list.md",
|
||||||
|
"width": 40,
|
||||||
|
"height": 10,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "items",
|
||||||
|
"type": "array",
|
||||||
|
"defaultValue": [
|
||||||
|
{"id": "1", "text": "First item", "selected": false},
|
||||||
|
{"id": "2", "text": "Second item", "selected": true},
|
||||||
|
{"id": "3", "text": "Third item", "selected": false}
|
||||||
|
],
|
||||||
|
"description": "List items with text, selection state, and metadata",
|
||||||
|
"required": true,
|
||||||
|
"validation": {
|
||||||
|
"min": 1,
|
||||||
|
"max": 50
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "unordered",
|
||||||
|
"description": "List type: ordered, unordered, or definition",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["ordered", "unordered", "definition", "checklist"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "marker",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "•",
|
||||||
|
"description": "Bullet character for unordered lists",
|
||||||
|
"validation": {
|
||||||
|
"max": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "numbering",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "decimal",
|
||||||
|
"description": "Numbering style for ordered lists",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["decimal", "alpha", "roman", "roman-upper"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "selectable",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Whether items can be selected"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "multiSelect",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Allow multiple item selection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bordered",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Show border around list"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "compact",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": false,
|
||||||
|
"description": "Use compact spacing between items"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "badge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["badge", "{{screenContext}}"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"text": "{{text}}"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "status",
|
||||||
|
"focusable": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 15,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["button", "{{screenContext}}"],
|
||||||
|
"category": "form",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"label": "{{label}}"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"focusable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 20,
|
||||||
|
"height": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "card",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["card", "{{screenContext}}"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "{{title}}",
|
||||||
|
"content": "{{content}}"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "article",
|
||||||
|
"focusable": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 50,
|
||||||
|
"height": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "checkbox",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["checkbox", "{{screenContext}}"],
|
||||||
|
"category": "form",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"label": "{{label}}",
|
||||||
|
"checked": false
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "checkbox",
|
||||||
|
"focusable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 30,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "container",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["container", "{{screenContext}}"],
|
||||||
|
"category": "layout",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "region",
|
||||||
|
"focusable": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 80,
|
||||||
|
"height": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "form",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["form", "{{screenContext}}"],
|
||||||
|
"category": "form",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"fields": []
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "form",
|
||||||
|
"focusable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 60,
|
||||||
|
"height": 25
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "icon",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["icon", "{{screenContext}}"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"symbol": "{{symbol}}"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "img",
|
||||||
|
"focusable": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 3,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "input",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["input", "{{screenContext}}"],
|
||||||
|
"category": "form",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"placeholder": "{{placeholder}}",
|
||||||
|
"label": "{{label}}"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "textbox",
|
||||||
|
"focusable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 40,
|
||||||
|
"height": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "list",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["list", "{{screenContext}}"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"items": []
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "list",
|
||||||
|
"focusable": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 60,
|
||||||
|
"height": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "modal",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["modal", "{{screenContext}}"],
|
||||||
|
"category": "overlay",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "{{title}}",
|
||||||
|
"content": "{{content}}"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "dialog",
|
||||||
|
"focusable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 60,
|
||||||
|
"height": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": "{{id}}",
|
||||||
|
"type": "navigation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"created": "{{timestamp}}",
|
||||||
|
"modified": "{{timestamp}}",
|
||||||
|
"tags": ["navigation", "{{screenContext}}"],
|
||||||
|
"category": "navigation",
|
||||||
|
"fidelity": "sketch"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"items": []
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "navigation",
|
||||||
|
"focusable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "{{id}}.md",
|
||||||
|
"width": 80,
|
||||||
|
"height": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
341
skills/fluxwing-component-creator/templates/modal.md
Normal file
341
skills/fluxwing-component-creator/templates/modal.md
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
# Modal Dialog Component
|
||||||
|
|
||||||
|
Modal dialog overlay with focus management, backdrop, and configurable content areas for confirmations, forms, and content display.
|
||||||
|
|
||||||
|
## Standard Modal (Medium Size)
|
||||||
|
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░░░┌────────────────────────────────┐░░░░░░░░
|
||||||
|
░░░░░░░░░░│ {{title}} │✕│░░░░░░░░
|
||||||
|
░░░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░░░│ {{content}} │░░░░░░░░
|
||||||
|
░░░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░░░│ ┌─────────┐ ┌─────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░░░│ │ Cancel │ │ {{buttons[1].text}} │ │░░░░░░░░
|
||||||
|
░░░░░░░░░░│ └─────────┘ └─────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░░░└────────────────────────────────┘░░░░░░░░
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Small Modal (Confirmation)
|
||||||
|
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░┌──────────────────────────┐░░░░░
|
||||||
|
░░░░░░░░│ {{title}} │✕│░░░░░
|
||||||
|
░░░░░░░░├──────────────────────────┤░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░
|
||||||
|
░░░░░░░░│ {{content}} │░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░
|
||||||
|
░░░░░░░░├──────────────────────────┤░░░░░
|
||||||
|
░░░░░░░░│ ┌──────┐ ┌────────────┐ │░░░░░
|
||||||
|
░░░░░░░░│ │ No │ │ Yes │ │░░░░░
|
||||||
|
░░░░░░░░│ └──────┘ └────────────┘ │░░░░░
|
||||||
|
░░░░░░░░└──────────────────────────┘░░░░░
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Large Modal (Form/Content)
|
||||||
|
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░┌────────────────────────────────────────────────────────┐░░░░░░░░
|
||||||
|
░░░░░░░░│ {{title}} │✕│░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ {{content}} │░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ Name: ┌─────────────────────────────────────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ Enter your name │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ └─────────────────────────────────────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ Email: ┌────────────────────────────────────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ your@email.com │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ └────────────────────────────────────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ Cancel │ │ Save │ │ Submit │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ └──────────┘ └──────────────┘ └──────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░└────────────────────────────────────────────────────────┘░░░░░░░░
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fullscreen Modal
|
||||||
|
|
||||||
|
```
|
||||||
|
╔══════════════════════════════════════════════════════════════════════════╗
|
||||||
|
║ {{title}} ✕ ║
|
||||||
|
╠══════════════════════════════════════════════════════════════════════════╣
|
||||||
|
║ ║
|
||||||
|
║ {{content}} ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
║ ║
|
||||||
|
╠══════════════════════════════════════════════════════════════════════════╣
|
||||||
|
║ ┌──────────┐ ┌──────────┐ ║
|
||||||
|
║ │ Cancel │ │ OK │ ║
|
||||||
|
║ └──────────┘ └──────────┘ ║
|
||||||
|
╚══════════════════════════════════════════════════════════════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modal Variants
|
||||||
|
|
||||||
|
### Warning Modal
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░┌────────────────────────────────┐░░░░░░░░
|
||||||
|
░░░░░░░░│ ⚠ Warning │✕│░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ This action cannot be undone. │░░░░░░░░
|
||||||
|
░░░░░░░░│ Are you sure you want to │░░░░░░░░
|
||||||
|
░░░░░░░░│ continue? │░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ ┌─────────┐ ┌─────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ Cancel │ │ Delete │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ └─────────┘ └─────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░└────────────────────────────────┘░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Modal
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░┌────────────────────────────────┐░░░░░░░░
|
||||||
|
░░░░░░░░│ ✗ Error │✕│░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ An error occurred while │░░░░░░░░
|
||||||
|
░░░░░░░░│ processing your request. │░░░░░░░░
|
||||||
|
░░░░░░░░│ Please try again later. │░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ ┌─────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ OK │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ └─────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░└────────────────────────────────┘░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
### Success Modal
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░┌────────────────────────────────┐░░░░░░░░
|
||||||
|
░░░░░░░░│ ✓ Success │✕│░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ Your changes have been saved │░░░░░░░░
|
||||||
|
░░░░░░░░│ successfully! │░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ ┌─────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ Continue │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ └─────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░└────────────────────────────────┘░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modal Without Backdrop
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ {{title}} │✕│
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{content}} │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ ┌─────────┐ ┌─────────────┐ │
|
||||||
|
│ │ Cancel │ │ Confirm │ │
|
||||||
|
│ └─────────┘ └─────────────┘ │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modal with Custom Content
|
||||||
|
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░░░░░░░░┌────────────────────────────────┐░░░░░░░░
|
||||||
|
░░░░░░░░│ Image Gallery │✕│░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ ┌────────────────────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ IMAGE_CONTENT │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ [PHOTO] │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ │ │░░░░░░░░
|
||||||
|
░░░░░░░░│ └────────────────────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ Photo 1 of 5 │░░░░░░░░
|
||||||
|
░░░░░░░░├────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ ┌─────┐ ┌─────┐ ┌─────────────┐│░░░░░░░░
|
||||||
|
░░░░░░░░│ │ Prev│ │Next │ │ Close ││░░░░░░░░
|
||||||
|
░░░░░░░░│ └─────┘ └─────┘ └─────────────┘│░░░░░░░░
|
||||||
|
░░░░░░░░└────────────────────────────────┘░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- **Small**: 30×15 characters
|
||||||
|
- **Medium**: 50×20 characters (default)
|
||||||
|
- **Large**: 70×30 characters
|
||||||
|
- **Fullscreen**: Full viewport dimensions
|
||||||
|
- **Custom**: Configurable width/height
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `title` (string): Modal header title (max 60 characters)
|
||||||
|
- `content` (string, required): Main modal content (max 500 characters)
|
||||||
|
- `size` (string): Modal size ("small", "medium", "large", "fullscreen")
|
||||||
|
- `buttons` (array): Action buttons with text, variant, and action
|
||||||
|
- `showCloseButton` (boolean): Show X button in header (default: true)
|
||||||
|
- `backdrop` (boolean): Show backdrop overlay (default: true)
|
||||||
|
- `variant` (string): Style variant ("default", "warning", "error", "success", "info")
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: dialog
|
||||||
|
- **Focus Management**:
|
||||||
|
- Trap focus within modal
|
||||||
|
- Return focus to trigger element on close
|
||||||
|
- Initial focus on first button or specified element
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Escape: Close modal (if closable)
|
||||||
|
- Tab/Shift+Tab: Navigate within modal
|
||||||
|
- Enter: Activate focused button
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-labelledby`: References title element
|
||||||
|
- `aria-describedby`: References content element
|
||||||
|
- `aria-modal`: "true"
|
||||||
|
- `aria-hidden`: "true" on background content when modal open
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Confirmation Dialog
|
||||||
|
```
|
||||||
|
░░░░░░░░┌──────────────────────────┐░░░░░
|
||||||
|
░░░░░░░░│ Delete Item │✕│░░░░░
|
||||||
|
░░░░░░░░├──────────────────────────┤░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░
|
||||||
|
░░░░░░░░│ Are you sure you want to │░░░░░
|
||||||
|
░░░░░░░░│ delete this item? This │░░░░░
|
||||||
|
░░░░░░░░│ action cannot be undone. │░░░░░
|
||||||
|
░░░░░░░░├──────────────────────────┤░░░░░
|
||||||
|
░░░░░░░░│ ┌──────┐ ┌────────────┐ │░░░░░
|
||||||
|
░░░░░░░░│ │Cancel│ │ Delete │ │░░░░░
|
||||||
|
░░░░░░░░│ └──────┘ └────────────┘ │░░░░░
|
||||||
|
░░░░░░░░└──────────────────────────┘░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loading Modal
|
||||||
|
```
|
||||||
|
░░░░░░░░┌──────────────────────────┐░░░░░
|
||||||
|
░░░░░░░░│ Processing... │░│░░░░░
|
||||||
|
░░░░░░░░├──────────────────────────┤░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░
|
||||||
|
░░░░░░░░│ Please wait while we │░░░░░
|
||||||
|
░░░░░░░░│ process your request... │░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░
|
||||||
|
░░░░░░░░│ ████████████████░░░░ 80% │░░░░░
|
||||||
|
░░░░░░░░└──────────────────────────┘░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
### Settings Modal
|
||||||
|
```
|
||||||
|
░░░░░░░░┌──────────────────────────────────┐░░░░░░░░
|
||||||
|
░░░░░░░░│ Preferences │✕│░░░░░░░░
|
||||||
|
░░░░░░░░├──────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ ☑ Enable notifications │░░░░░░░░
|
||||||
|
░░░░░░░░│ ☐ Auto-save changes │░░░░░░░░
|
||||||
|
░░░░░░░░│ ☑ Dark mode │░░░░░░░░
|
||||||
|
░░░░░░░░│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ Language: ┌───────────────────┐ │░░░░░░░░
|
||||||
|
░░░░░░░░│ │ English ▼│ │░░░░░░░░
|
||||||
|
░░░░░░░░│ └───────────────────┘ │░░░░░░░░
|
||||||
|
░░░░░░░░├──────────────────────────────────┤░░░░░░░░
|
||||||
|
░░░░░░░░│ ┌─────────┐ ┌─────────────────┐│░░░░░░░░
|
||||||
|
░░░░░░░░│ │ Cancel │ │ Save ││░░░░░░░░
|
||||||
|
░░░░░░░░│ └─────────┘ └─────────────────┘│░░░░░░░░
|
||||||
|
░░░░░░░░└──────────────────────────────────┘░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Modal Lifecycle
|
||||||
|
|
||||||
|
1. **Trigger**: User action opens modal
|
||||||
|
2. **Opening**: Modal animates into view
|
||||||
|
3. **Open**: Modal fully visible and interactive
|
||||||
|
4. **Interaction**: User interacts with modal content
|
||||||
|
5. **Closing**: Modal closes via button, escape, or backdrop
|
||||||
|
6. **Closed**: Modal hidden, focus restored
|
||||||
|
|
||||||
|
### Focus Management
|
||||||
|
|
||||||
|
- **Focus Trap**: Tab navigation stays within modal
|
||||||
|
- **Initial Focus**: First focusable element (usually close button)
|
||||||
|
- **Focus Restoration**: Return to trigger element on close
|
||||||
|
- **Focus Indicators**: Clear visual feedback for keyboard users
|
||||||
|
|
||||||
|
### Backdrop Behavior
|
||||||
|
|
||||||
|
- **Backdrop Click**: Close modal if `backdropClosable` is true
|
||||||
|
- **Backdrop Scroll**: Prevent page scrolling when modal open
|
||||||
|
- **Multiple Modals**: Handle stacking and z-index management
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Visual Elements
|
||||||
|
- `░` = Backdrop overlay (semi-transparent)
|
||||||
|
- `┌─┐└┘─│` = Modal border and dividers
|
||||||
|
- `╔═╗╚╝═║` = Fullscreen modal borders
|
||||||
|
- `✕` = Close button icon
|
||||||
|
- `⚠✗✓` = Status icons for variants
|
||||||
|
|
||||||
|
### Spacing
|
||||||
|
- **Padding**: 2-3 characters internal spacing
|
||||||
|
- **Margins**: Centered positioning with backdrop
|
||||||
|
- **Button Spacing**: 2-3 characters between buttons
|
||||||
|
- **Content Spacing**: 1-2 lines between sections
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- **Popup**: Smaller, contextual overlays
|
||||||
|
- **Tooltip**: Informational overlays
|
||||||
|
- **Dropdown**: Menu-style overlays
|
||||||
|
- **Sidebar**: Panel-style content areas
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This ASCII representation demonstrates modal overlay patterns. When implementing:
|
||||||
|
|
||||||
|
1. **Focus Management**: Robust focus trapping and restoration
|
||||||
|
2. **Backdrop Management**: Proper event handling and scroll prevention
|
||||||
|
3. **Animation**: Smooth open/close transitions
|
||||||
|
4. **Mobile Adaptation**: Responsive sizing and touch interactions
|
||||||
|
5. **Performance**: Efficient rendering and memory management
|
||||||
|
6. **Accessibility**: Full screen reader and keyboard support
|
||||||
|
|
||||||
|
## Variants
|
||||||
|
|
||||||
|
- **Alert Dialog**: Simple message with OK button
|
||||||
|
- **Confirmation Dialog**: Yes/No or Cancel/Confirm actions
|
||||||
|
- **Form Modal**: Data input and submission
|
||||||
|
- **Content Modal**: Rich content display (images, videos, etc.)
|
||||||
|
- **Loading Modal**: Progress indication during operations
|
||||||
207
skills/fluxwing-component-creator/templates/modal.uxm
Normal file
207
skills/fluxwing-component-creator/templates/modal.uxm
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
{
|
||||||
|
"id": "modal",
|
||||||
|
"type": "modal",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Modal Dialog",
|
||||||
|
"description": "Modal dialog overlay with focus management, backdrop, and configurable content areas",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["modal", "dialog", "overlay", "popup"],
|
||||||
|
"category": "overlay",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "Confirm Action",
|
||||||
|
"content": "Are you sure you want to proceed with this action?",
|
||||||
|
"size": "medium",
|
||||||
|
"showCloseButton": true,
|
||||||
|
"closable": true,
|
||||||
|
"backdrop": true,
|
||||||
|
"backdropClosable": true,
|
||||||
|
"centered": true,
|
||||||
|
"buttons": [
|
||||||
|
{
|
||||||
|
"text": "Cancel",
|
||||||
|
"variant": "secondary",
|
||||||
|
"action": "close"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Confirm",
|
||||||
|
"variant": "primary",
|
||||||
|
"action": "confirm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"icon": "",
|
||||||
|
"variant": "default"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "closed",
|
||||||
|
"properties": {
|
||||||
|
"visible": false,
|
||||||
|
"zIndex": -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opening",
|
||||||
|
"properties": {
|
||||||
|
"visible": true,
|
||||||
|
"zIndex": 1000,
|
||||||
|
"opacity": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "open",
|
||||||
|
"properties": {
|
||||||
|
"visible": true,
|
||||||
|
"zIndex": 1000,
|
||||||
|
"opacity": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "closing",
|
||||||
|
"properties": {
|
||||||
|
"visible": true,
|
||||||
|
"zIndex": 1000,
|
||||||
|
"opacity": 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "close-modal",
|
||||||
|
"condition": "backdrop && backdropClosable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "close-modal",
|
||||||
|
"condition": "key === 'Escape' && closable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "cycle-focus",
|
||||||
|
"condition": "key === 'Tab'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "button-action",
|
||||||
|
"condition": "target.type === 'button'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"focusManagement": {
|
||||||
|
"trapFocus": true,
|
||||||
|
"restoreFocus": true,
|
||||||
|
"initialFocus": "first-button",
|
||||||
|
"focusableElements": ["button", "input", "select", "textarea"]
|
||||||
|
},
|
||||||
|
"accessibility": {
|
||||||
|
"role": "dialog",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Escape", "Tab", "Shift+Tab", "Enter"],
|
||||||
|
"ariaLabel": "{{title}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "overlay",
|
||||||
|
"positioning": "fixed",
|
||||||
|
"spacing": {
|
||||||
|
"padding": {
|
||||||
|
"top": 2,
|
||||||
|
"right": 3,
|
||||||
|
"bottom": 2,
|
||||||
|
"left": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"small": {
|
||||||
|
"width": 30,
|
||||||
|
"height": 15
|
||||||
|
},
|
||||||
|
"medium": {
|
||||||
|
"width": 50,
|
||||||
|
"height": 20
|
||||||
|
},
|
||||||
|
"large": {
|
||||||
|
"width": 70,
|
||||||
|
"height": 30
|
||||||
|
},
|
||||||
|
"fullscreen": {
|
||||||
|
"width": "100vw",
|
||||||
|
"height": "100vh"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "modal.md",
|
||||||
|
"width": 50,
|
||||||
|
"height": 20,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Confirm Action",
|
||||||
|
"description": "Modal dialog title/header text",
|
||||||
|
"required": false,
|
||||||
|
"validation": {
|
||||||
|
"max": 60
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "content",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Are you sure you want to proceed?",
|
||||||
|
"description": "Main content/message text",
|
||||||
|
"required": true,
|
||||||
|
"validation": {
|
||||||
|
"max": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "size",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "medium",
|
||||||
|
"description": "Modal size variant",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["small", "medium", "large", "fullscreen"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "buttons",
|
||||||
|
"type": "array",
|
||||||
|
"defaultValue": [
|
||||||
|
{"text": "Cancel", "variant": "secondary", "action": "close"},
|
||||||
|
{"text": "Confirm", "variant": "primary", "action": "confirm"}
|
||||||
|
],
|
||||||
|
"description": "Modal action buttons",
|
||||||
|
"validation": {
|
||||||
|
"max": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "showCloseButton",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Show X close button in header"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "backdrop",
|
||||||
|
"type": "boolean",
|
||||||
|
"defaultValue": true,
|
||||||
|
"description": "Show backdrop overlay behind modal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "variant",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "default",
|
||||||
|
"description": "Modal style variant",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["default", "warning", "error", "success", "info"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
199
skills/fluxwing-component-creator/templates/navigation.md
Normal file
199
skills/fluxwing-component-creator/templates/navigation.md
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# Navigation Component
|
||||||
|
|
||||||
|
A horizontal or vertical navigation component with active state management and keyboard support.
|
||||||
|
|
||||||
|
## Horizontal Navigation (Default)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ [*] {{items[0].label}} {{separator}} {{items[1].label}} {{separator}} {{items[2].label}} │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Vertical Navigation
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ [*] {{items[0].label}} │
|
||||||
|
│ {{items[1].label}} │
|
||||||
|
│ {{items[2].label}} │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Active State Indicator
|
||||||
|
|
||||||
|
Active items are marked with the `{{activeIndicator}}` symbol:
|
||||||
|
|
||||||
|
### Horizontal Active Example
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ [*] Home {{separator}} About {{separator}} Contact │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vertical Active Example
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ [*] Home │
|
||||||
|
│ About │
|
||||||
|
│ Contact │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hover State
|
||||||
|
|
||||||
|
Items show visual emphasis when hovered:
|
||||||
|
|
||||||
|
### Horizontal Hover
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ [*] Home {{separator}} ▓About▓ {{separator}} Contact │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vertical Hover
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ [*] Home │
|
||||||
|
│ ▓▓▓ About ▓▓▓ │
|
||||||
|
│ Contact │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compact Horizontal Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
[*] Home | About | Contact
|
||||||
|
```
|
||||||
|
|
||||||
|
## Breadcrumb Style
|
||||||
|
|
||||||
|
```
|
||||||
|
Home > Category > {{items[2].label}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tab Style Navigation
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────┐ ┌─────┐ ┌─────┐
|
||||||
|
│ Home│ │About│ │Help │
|
||||||
|
├─────┘ └─────┘ └─────┤
|
||||||
|
│ │
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- **Horizontal**: Width adjusts to content, height 3 characters
|
||||||
|
- **Vertical**: Width adjusts to longest item, height scales with item count
|
||||||
|
- **Compact**: Single line, width adjusts to content
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `items` (array, required): Navigation items with label, href, and active properties
|
||||||
|
- Each item: `{label: string, href: string, active: boolean}`
|
||||||
|
- Min: 1 item, Max: 10 items
|
||||||
|
- `orientation` (string): "horizontal" or "vertical" (default: "horizontal")
|
||||||
|
- `separator` (string): Character(s) between horizontal items (default: " | ")
|
||||||
|
- `activeIndicator` (string): Symbol marking active item (default: "[*]")
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: navigation
|
||||||
|
- **Focusable**: Yes, each navigation item is focusable
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Arrow Keys: Navigate between items
|
||||||
|
- Enter/Space: Activate navigation item
|
||||||
|
- Tab: Move to next focusable element
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-label`: "Main navigation" or custom label
|
||||||
|
- `aria-current`: "page" for active navigation item
|
||||||
|
- `role="navigation"` on container
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Basic Three-Item Menu
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ [*] Dashboard | Products | Settings │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sidebar Navigation
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ [*] Dashboard │
|
||||||
|
│ Products │
|
||||||
|
│ Orders │
|
||||||
|
│ Customers │
|
||||||
|
│ Settings │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Header Navigation with Icons
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ [*] 🏠 Home | 📋 About | 📞 Contact | 🔧 Settings │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### Navigation Flow
|
||||||
|
|
||||||
|
1. **Item Selection**: Click or Enter/Space activates navigation
|
||||||
|
2. **Active State**: Only one item can be active at a time
|
||||||
|
3. **Focus Management**: Arrow keys move focus between items
|
||||||
|
4. **URL Updates**: Navigation typically updates browser URL
|
||||||
|
|
||||||
|
### Keyboard Navigation
|
||||||
|
|
||||||
|
- **Horizontal**: Left/Right arrows move between items
|
||||||
|
- **Vertical**: Up/Down arrows move between items
|
||||||
|
- **Enter/Space**: Activate current focused item
|
||||||
|
- **Tab**: Exit navigation to next focusable element
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
- **Active Item**: Visually distinct with indicator
|
||||||
|
- **Hover State**: Temporary emphasis on mouse over
|
||||||
|
- **Focus State**: Keyboard navigation indicator
|
||||||
|
- **Disabled Items**: Optional grayed-out non-interactive items
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Visual Elements
|
||||||
|
- `┌─┐└┘─│` = Border characters for containers
|
||||||
|
- `▓` = Hover/emphasis background
|
||||||
|
- `[*]` = Default active indicator (customizable)
|
||||||
|
- `|` = Default separator (customizable)
|
||||||
|
|
||||||
|
### Spacing
|
||||||
|
- Internal padding: 1 character around content
|
||||||
|
- Item spacing: Determined by separator in horizontal mode
|
||||||
|
- Vertical spacing: 1 line between items in vertical mode
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- **Breadcrumb**: Linear navigation showing hierarchy
|
||||||
|
- **Tab Navigation**: Content switching interface
|
||||||
|
- **Menu**: Dropdown or popup navigation
|
||||||
|
- **Sidebar**: Persistent vertical navigation panel
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This ASCII representation demonstrates navigation patterns. When implementing:
|
||||||
|
|
||||||
|
1. **Active State Management**: Ensure only one item is active
|
||||||
|
2. **Keyboard Accessibility**: Full arrow key navigation support
|
||||||
|
3. **Focus Indicators**: Clear visual feedback for keyboard users
|
||||||
|
4. **Responsive Behavior**: Consider mobile/narrow screen adaptations
|
||||||
|
5. **URL Integration**: Sync with browser history and routing
|
||||||
|
6. **Loading States**: Handle navigation during page transitions
|
||||||
|
|
||||||
|
## Variants
|
||||||
|
|
||||||
|
- **Primary Navigation**: Main site navigation
|
||||||
|
- **Secondary Navigation**: Subsection or category navigation
|
||||||
|
- **Breadcrumb Navigation**: Hierarchical path display
|
||||||
|
- **Tab Navigation**: Content area switching
|
||||||
|
- **Pagination**: Numeric page navigation
|
||||||
163
skills/fluxwing-component-creator/templates/navigation.uxm
Normal file
163
skills/fluxwing-component-creator/templates/navigation.uxm
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"id": "navigation",
|
||||||
|
"type": "navigation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Navigation Menu",
|
||||||
|
"description": "A horizontal or vertical navigation component with active state management",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["navigation", "menu", "nav"],
|
||||||
|
"category": "navigation",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"label": "Home",
|
||||||
|
"href": "/",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "About",
|
||||||
|
"href": "/about",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Contact",
|
||||||
|
"href": "/contact",
|
||||||
|
"active": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orientation": "horizontal",
|
||||||
|
"variant": "default",
|
||||||
|
"separator": " | ",
|
||||||
|
"activeIndicator": "[*]"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"textColor": "default",
|
||||||
|
"background": "transparent"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "active",
|
||||||
|
"properties": {
|
||||||
|
"textColor": "primary",
|
||||||
|
"background": "primary-light",
|
||||||
|
"fontWeight": "bold"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"textColor": "primary-dark",
|
||||||
|
"background": "gray-light"
|
||||||
|
},
|
||||||
|
"triggers": ["mouseenter"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "navigate",
|
||||||
|
"condition": "item.href"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "navigate-next",
|
||||||
|
"condition": "key === 'ArrowRight' && orientation === 'horizontal'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "navigate-previous",
|
||||||
|
"condition": "key === 'ArrowLeft' && orientation === 'horizontal'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "navigate-next",
|
||||||
|
"condition": "key === 'ArrowDown' && orientation === 'vertical'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "navigate-previous",
|
||||||
|
"condition": "key === 'ArrowUp' && orientation === 'vertical'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "navigation",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Enter", "Space"],
|
||||||
|
"ariaLabel": "Main navigation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"padding": {
|
||||||
|
"top": 1,
|
||||||
|
"right": 1,
|
||||||
|
"bottom": 1,
|
||||||
|
"left": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"minWidth": 20,
|
||||||
|
"height": "auto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "navigation.md",
|
||||||
|
"width": 60,
|
||||||
|
"height": 3,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "items",
|
||||||
|
"type": "array",
|
||||||
|
"defaultValue": [
|
||||||
|
{"label": "Home", "href": "/", "active": true},
|
||||||
|
{"label": "About", "href": "/about", "active": false},
|
||||||
|
{"label": "Contact", "href": "/contact", "active": false}
|
||||||
|
],
|
||||||
|
"description": "Navigation menu items with labels, links, and active states",
|
||||||
|
"required": true,
|
||||||
|
"validation": {
|
||||||
|
"min": 1,
|
||||||
|
"max": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "orientation",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "horizontal",
|
||||||
|
"description": "Layout orientation of navigation items",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["horizontal", "vertical"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "separator",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": " | ",
|
||||||
|
"description": "Separator between navigation items (horizontal only)",
|
||||||
|
"validation": {
|
||||||
|
"max": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "activeIndicator",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "[*]",
|
||||||
|
"description": "Visual indicator for active navigation item",
|
||||||
|
"validation": {
|
||||||
|
"max": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
131
skills/fluxwing-component-creator/templates/primary-button.md
Normal file
131
skills/fluxwing-component-creator/templates/primary-button.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# Primary Button Component
|
||||||
|
|
||||||
|
A primary action button with emphasis styling for main user actions.
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ {{text}} ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hover State
|
||||||
|
|
||||||
|
```
|
||||||
|
████████████████
|
||||||
|
█ {{text}} █
|
||||||
|
████████████████
|
||||||
|
```
|
||||||
|
|
||||||
|
## Active/Pressed State
|
||||||
|
|
||||||
|
```
|
||||||
|
░▓▓▓▓▓▓▓▓▓▓▓▓▓▓░
|
||||||
|
░▓ {{text}} ▓░
|
||||||
|
░▓▓▓▓▓▓▓▓▓▓▓▓▓▓░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabled State
|
||||||
|
|
||||||
|
```
|
||||||
|
┌ ─ ─ ─ ─ ─ ─ ─┐
|
||||||
|
│ {{text}} │
|
||||||
|
└ ─ ─ ─ ─ ─ ─ ─┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- Width: {{width}} characters (configurable, min 8, max 40)
|
||||||
|
- Height: 3 characters (fixed)
|
||||||
|
- Text alignment: center
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): Button label text (max 20 characters)
|
||||||
|
- `width` (number): Button width in characters (8-40, default 16)
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: button
|
||||||
|
- **Focusable**: Yes
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Enter: Activates button
|
||||||
|
- Space: Activates button
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-label`: Uses `text` value or custom `ariaLabel` prop
|
||||||
|
- `aria-disabled`: Set to "true" when disabled
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Save ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compact Button
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓
|
||||||
|
▓ OK ▓
|
||||||
|
▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wide Button
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Create Account ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### State Transitions
|
||||||
|
|
||||||
|
1. **Default → Hover**: Mouse enters button area
|
||||||
|
2. **Hover → Active**: Mouse button pressed down
|
||||||
|
3. **Active → Default**: Mouse button released
|
||||||
|
4. **Any → Disabled**: Component disabled property set to true
|
||||||
|
|
||||||
|
### Click Handling
|
||||||
|
|
||||||
|
The button emits a click event when:
|
||||||
|
- Mouse click occurs
|
||||||
|
- Enter key pressed while focused
|
||||||
|
- Space key pressed while focused
|
||||||
|
|
||||||
|
### Focus Management
|
||||||
|
|
||||||
|
- Button receives focus via Tab navigation
|
||||||
|
- Focus visible indicator shown when keyboard navigated
|
||||||
|
- Focus moves to next focusable element on Tab
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Colors (represented by patterns)
|
||||||
|
- `▓` = Primary background color
|
||||||
|
- `█` = Primary hover color
|
||||||
|
- `░` = Shadow/pressed effect
|
||||||
|
- `─` = Disabled border style
|
||||||
|
|
||||||
|
### Spacing
|
||||||
|
- Internal padding: 1 character top/bottom, 2 characters left/right
|
||||||
|
- Minimum touch target: 8×3 characters
|
||||||
|
- Recommended spacing between buttons: 2 characters
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- Secondary Button: Outline style variant
|
||||||
|
- Icon Button: Button with icon instead of text
|
||||||
|
- Button Group: Multiple buttons grouped together
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This ASCII representation demonstrates the visual hierarchy and interaction states. When implementing in actual UI frameworks:
|
||||||
|
|
||||||
|
1. Map the `▓` pattern to the primary brand color
|
||||||
|
2. Ensure proper contrast ratios for accessibility
|
||||||
|
3. Implement smooth hover transitions
|
||||||
|
4. Add appropriate ripple/click effects
|
||||||
|
5. Support all specified keyboard interactions
|
||||||
120
skills/fluxwing-component-creator/templates/primary-button.uxm
Normal file
120
skills/fluxwing-component-creator/templates/primary-button.uxm
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"id": "primary-button",
|
||||||
|
"type": "button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Primary Button",
|
||||||
|
"description": "A primary action button with emphasis styling for main user actions",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-01T00:00:00Z",
|
||||||
|
"modified": "2024-01-01T00:00:00Z",
|
||||||
|
"tags": ["button", "primary", "action"],
|
||||||
|
"category": "input",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"text": "Button",
|
||||||
|
"disabled": false,
|
||||||
|
"size": "medium",
|
||||||
|
"fullWidth": false,
|
||||||
|
"ariaLabel": ""
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"background": "primary",
|
||||||
|
"textColor": "white",
|
||||||
|
"border": "solid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"background": "primary-dark",
|
||||||
|
"textColor": "white",
|
||||||
|
"border": "solid"
|
||||||
|
},
|
||||||
|
"triggers": ["mouseenter"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "active",
|
||||||
|
"properties": {
|
||||||
|
"background": "primary-darker",
|
||||||
|
"textColor": "white",
|
||||||
|
"border": "inset"
|
||||||
|
},
|
||||||
|
"triggers": ["mousedown"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disabled",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-light",
|
||||||
|
"textColor": "gray-dark",
|
||||||
|
"border": "dashed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "emit-click-event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "emit-click-event",
|
||||||
|
"condition": "key === 'Enter' || key === ' '"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Enter", "Space"],
|
||||||
|
"ariaLabel": "{{ariaLabel || text}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "inline-block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"padding": {
|
||||||
|
"top": 1,
|
||||||
|
"right": 2,
|
||||||
|
"bottom": 1,
|
||||||
|
"left": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"minWidth": 8,
|
||||||
|
"height": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "primary-button.md",
|
||||||
|
"width": 16,
|
||||||
|
"height": 3,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "text",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Button",
|
||||||
|
"description": "The text displayed on the button",
|
||||||
|
"required": true,
|
||||||
|
"validation": {
|
||||||
|
"max": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "width",
|
||||||
|
"type": "number",
|
||||||
|
"defaultValue": 16,
|
||||||
|
"description": "Button width in characters",
|
||||||
|
"validation": {
|
||||||
|
"min": 8,
|
||||||
|
"max": 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
139
skills/fluxwing-component-creator/templates/secondary-button.md
Normal file
139
skills/fluxwing-component-creator/templates/secondary-button.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# Secondary Button Component
|
||||||
|
|
||||||
|
A secondary action button with subtle styling for less prominent actions.
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░
|
||||||
|
░ {{text}} ░
|
||||||
|
░░░░░░░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hover State
|
||||||
|
|
||||||
|
```
|
||||||
|
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||||
|
▒ {{text}} ▒
|
||||||
|
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||||
|
```
|
||||||
|
|
||||||
|
## Active/Pressed State
|
||||||
|
|
||||||
|
```
|
||||||
|
████████████████
|
||||||
|
█ {{text}} █
|
||||||
|
████████████████
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabled State
|
||||||
|
|
||||||
|
```
|
||||||
|
┌ ─ ─ ─ ─ ─ ─ ─┐
|
||||||
|
│ {{text}} │
|
||||||
|
└ ─ ─ ─ ─ ─ ─ ─┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dimensions
|
||||||
|
|
||||||
|
- Width: {{width}} characters (configurable, min 8, max 40)
|
||||||
|
- Height: 3 characters (fixed)
|
||||||
|
- Text alignment: center
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): Button label text (max 20 characters)
|
||||||
|
- `width` (number): Button width in characters (8-40, default 16)
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: button
|
||||||
|
- **Focusable**: Yes
|
||||||
|
- **Keyboard Support**:
|
||||||
|
- Enter: Activates button
|
||||||
|
- Space: Activates button
|
||||||
|
- **ARIA**:
|
||||||
|
- `aria-label`: Uses `text` value or custom `ariaLabel` prop
|
||||||
|
- `aria-disabled`: Set to "true" when disabled
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░
|
||||||
|
░ Cancel ░
|
||||||
|
░░░░░░░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compact Button
|
||||||
|
```
|
||||||
|
░░░░░░░░
|
||||||
|
░ No ░
|
||||||
|
░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wide Button
|
||||||
|
```
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
░ Learn More ░
|
||||||
|
░░░░░░░░░░░░░░░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Behavior
|
||||||
|
|
||||||
|
### State Transitions
|
||||||
|
|
||||||
|
1. **Default → Hover**: Mouse enters button area
|
||||||
|
2. **Hover → Active**: Mouse button pressed down
|
||||||
|
3. **Active → Default**: Mouse button released
|
||||||
|
4. **Any → Disabled**: Component disabled property set to true
|
||||||
|
|
||||||
|
### Click Handling
|
||||||
|
|
||||||
|
The button emits a click event when:
|
||||||
|
- Mouse click occurs
|
||||||
|
- Enter key pressed while focused
|
||||||
|
- Space key pressed while focused
|
||||||
|
|
||||||
|
### Focus Management
|
||||||
|
|
||||||
|
- Button receives focus via Tab navigation
|
||||||
|
- Focus visible indicator shown when keyboard navigated
|
||||||
|
- Focus moves to next focusable element on Tab
|
||||||
|
|
||||||
|
## Design Tokens
|
||||||
|
|
||||||
|
### Colors (represented by patterns)
|
||||||
|
- `░` = Light gray background color
|
||||||
|
- `▒` = Medium gray hover color
|
||||||
|
- `█` = Dark gray pressed effect
|
||||||
|
- `─` = Disabled border style
|
||||||
|
|
||||||
|
### Spacing
|
||||||
|
- Internal padding: 1 character top/bottom, 2 characters left/right
|
||||||
|
- Minimum touch target: 8×3 characters
|
||||||
|
- Recommended spacing between buttons: 2 characters
|
||||||
|
|
||||||
|
## Related Components
|
||||||
|
|
||||||
|
- Primary Button: High emphasis style variant
|
||||||
|
- Outline Button: Border-only style variant
|
||||||
|
- Button Group: Multiple buttons grouped together
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
This ASCII representation demonstrates a subtle visual hierarchy. When implementing in actual UI frameworks:
|
||||||
|
|
||||||
|
1. Map the `░` pattern to a light gray background
|
||||||
|
2. Ensure sufficient contrast ratios for accessibility
|
||||||
|
3. Implement smooth hover transitions
|
||||||
|
4. Add appropriate visual feedback for interactions
|
||||||
|
5. Support all specified keyboard interactions
|
||||||
|
|
||||||
|
## Usage Guidelines
|
||||||
|
|
||||||
|
Secondary buttons are ideal for:
|
||||||
|
- Cancel or dismiss actions
|
||||||
|
- Less important navigation
|
||||||
|
- Actions that complement a primary action
|
||||||
|
- Secondary paths in user workflows
|
||||||
120
skills/fluxwing-component-creator/templates/secondary-button.uxm
Normal file
120
skills/fluxwing-component-creator/templates/secondary-button.uxm
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"id": "secondary-button",
|
||||||
|
"type": "button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Secondary Button",
|
||||||
|
"description": "A secondary action button with subtle styling for less prominent actions",
|
||||||
|
"author": "UXscii Team",
|
||||||
|
"created": "2024-01-15T00:00:00Z",
|
||||||
|
"modified": "2024-01-15T00:00:00Z",
|
||||||
|
"tags": ["button", "secondary", "action"],
|
||||||
|
"category": "input",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"text": "Button",
|
||||||
|
"disabled": false,
|
||||||
|
"size": "medium",
|
||||||
|
"fullWidth": false,
|
||||||
|
"ariaLabel": ""
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-light",
|
||||||
|
"textColor": "gray-dark",
|
||||||
|
"border": "solid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-medium",
|
||||||
|
"textColor": "gray-darker",
|
||||||
|
"border": "solid"
|
||||||
|
},
|
||||||
|
"triggers": ["mouseenter"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "active",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-dark",
|
||||||
|
"textColor": "white",
|
||||||
|
"border": "inset"
|
||||||
|
},
|
||||||
|
"triggers": ["mousedown"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disabled",
|
||||||
|
"properties": {
|
||||||
|
"background": "gray-lighter",
|
||||||
|
"textColor": "gray-light",
|
||||||
|
"border": "dashed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "emit-click-event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "keydown",
|
||||||
|
"action": "emit-click-event",
|
||||||
|
"condition": "key === 'Enter' || key === ' '"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Enter", "Space"],
|
||||||
|
"ariaLabel": "{{ariaLabel || text}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "inline-block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"padding": {
|
||||||
|
"top": 1,
|
||||||
|
"right": 2,
|
||||||
|
"bottom": 1,
|
||||||
|
"left": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"minWidth": 8,
|
||||||
|
"height": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "secondary-button.md",
|
||||||
|
"width": 16,
|
||||||
|
"height": 3,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "text",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "Button",
|
||||||
|
"description": "The text displayed on the button",
|
||||||
|
"required": true,
|
||||||
|
"validation": {
|
||||||
|
"max": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "width",
|
||||||
|
"type": "number",
|
||||||
|
"defaultValue": 16,
|
||||||
|
"description": "Button width in characters",
|
||||||
|
"validation": {
|
||||||
|
"min": 8,
|
||||||
|
"max": 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "disabled",
|
||||||
|
"properties": {
|
||||||
|
"opacity": 0.5,
|
||||||
|
"cursor": "not-allowed",
|
||||||
|
"interactive": false,
|
||||||
|
"asciiModifier": "grayed-out"
|
||||||
|
},
|
||||||
|
"description": "Component is not interactive or available"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "error",
|
||||||
|
"properties": {
|
||||||
|
"borderColor": "#cc0000",
|
||||||
|
"backgroundColor": "#fff0f0",
|
||||||
|
"showErrorMessage": true,
|
||||||
|
"asciiModifier": "error-border"
|
||||||
|
},
|
||||||
|
"description": "Component has invalid input or error condition"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "focus",
|
||||||
|
"properties": {
|
||||||
|
"outline": "2px solid #0066cc",
|
||||||
|
"backgroundColor": "#f0f8ff",
|
||||||
|
"asciiModifier": "focus-ring"
|
||||||
|
},
|
||||||
|
"description": "Visual indicator when component has keyboard focus"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"backgroundColor": "#e0e0e0",
|
||||||
|
"cursor": "pointer",
|
||||||
|
"asciiModifier": "bold"
|
||||||
|
},
|
||||||
|
"description": "Visual feedback when user hovers over component"
|
||||||
|
}
|
||||||
343
skills/fluxwing-component-expander/SKILL.md
Normal file
343
skills/fluxwing-component-expander/SKILL.md
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
---
|
||||||
|
name: Fluxwing Component Expander
|
||||||
|
description: Add interaction states like hover, focus, disabled, active, error to existing uxscii components. Use when working with .uxm files, when user wants to expand, enhance, or add states to .uxm components.
|
||||||
|
version: 0.0.1
|
||||||
|
author: Trabian
|
||||||
|
allowed-tools: Read, Write, Edit, Glob, Grep, Bash
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fluxwing Component Expander
|
||||||
|
|
||||||
|
Expand existing uxscii components by adding interaction states.
|
||||||
|
|
||||||
|
## Data Location Rules
|
||||||
|
|
||||||
|
**READ from:**
|
||||||
|
- `./fluxwing/components/` - User-created components to expand
|
||||||
|
- `./fluxwing/library/` - User library components to expand
|
||||||
|
- `{SKILL_ROOT}/docs/` - Documentation for state definitions
|
||||||
|
|
||||||
|
**WRITE to:**
|
||||||
|
- `./fluxwing/components/` - Updated components (overwrite existing)
|
||||||
|
- `./fluxwing/library/` - Updated library components (overwrite existing)
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
Expand an existing uxscii component by adding interaction states.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### 1. Locate Component
|
||||||
|
|
||||||
|
Ask for the component name if not provided:
|
||||||
|
- "Which component do you want to expand?"
|
||||||
|
- Search in `./fluxwing/components/` and `./fluxwing/library/`
|
||||||
|
- Display current states if component found
|
||||||
|
|
||||||
|
### 2. Determine States to Add
|
||||||
|
|
||||||
|
**Default behavior (no instructions)**: Add ALL standard states for component type
|
||||||
|
|
||||||
|
**Smart defaults by type**:
|
||||||
|
- **button**: hover, active, disabled
|
||||||
|
- **input**: focus, error, disabled
|
||||||
|
- **checkbox/radio**: checked, disabled
|
||||||
|
- **select**: open, disabled
|
||||||
|
- **link**: hover, visited, active
|
||||||
|
- **card**: hover, selected
|
||||||
|
- **modal**: open, closing
|
||||||
|
- **alert**: success, warning, error, info
|
||||||
|
- **badge**: active, inactive
|
||||||
|
- **navigation**: active, hover
|
||||||
|
- **toggle**: on, off, disabled
|
||||||
|
- **slider**: dragging, disabled
|
||||||
|
- **tab**: selected, hover
|
||||||
|
- **list**: selected, hover
|
||||||
|
- **table**: sorted, hover
|
||||||
|
|
||||||
|
**User can override**: "Only add hover and focus" or "Add error state only"
|
||||||
|
|
||||||
|
### 3. Read Existing Component Files
|
||||||
|
|
||||||
|
Read both files:
|
||||||
|
- `{component-name}.uxm` - Current JSON metadata
|
||||||
|
- `{component-name}.md` - Current ASCII template
|
||||||
|
|
||||||
|
Extract:
|
||||||
|
- Current states (from behavior.states array)
|
||||||
|
- Component type (from type field)
|
||||||
|
- Visual properties (from ascii section)
|
||||||
|
- Variables (from ascii.variables array)
|
||||||
|
- Border style from default state (from behavior.states[0].properties.border)
|
||||||
|
|
||||||
|
### 4. Generate New States
|
||||||
|
|
||||||
|
For each new state to add:
|
||||||
|
|
||||||
|
**A. Add state definition to .uxm file**
|
||||||
|
|
||||||
|
Insert into the `behavior.states` array. Each state needs:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"border": "heavy",
|
||||||
|
"background": "primary-dark",
|
||||||
|
"textColor": "default"
|
||||||
|
},
|
||||||
|
"triggers": ["mouseenter"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**State property patterns by state name**:
|
||||||
|
|
||||||
|
- **hover**:
|
||||||
|
- border: "heavy"
|
||||||
|
- background: slightly darker than default
|
||||||
|
- triggers: ["mouseenter"]
|
||||||
|
|
||||||
|
- **focus**:
|
||||||
|
- border: "double"
|
||||||
|
- background: same as default
|
||||||
|
- textColor: "primary"
|
||||||
|
- triggers: ["focus"]
|
||||||
|
|
||||||
|
- **active**:
|
||||||
|
- border: "heavy"
|
||||||
|
- background: "filled"
|
||||||
|
- triggers: ["mousedown"]
|
||||||
|
|
||||||
|
- **disabled**:
|
||||||
|
- border: "dashed"
|
||||||
|
- opacity: 0.5
|
||||||
|
- cursor: "not-allowed"
|
||||||
|
|
||||||
|
- **error**:
|
||||||
|
- border: "heavy"
|
||||||
|
- borderColor: "red"
|
||||||
|
- textColor: "error"
|
||||||
|
|
||||||
|
- **success**:
|
||||||
|
- border: "heavy"
|
||||||
|
- borderColor: "green"
|
||||||
|
- textColor: "success"
|
||||||
|
|
||||||
|
- **loading**:
|
||||||
|
- opacity: 0.7
|
||||||
|
- cursor: "wait"
|
||||||
|
|
||||||
|
- **checked** (checkbox/radio):
|
||||||
|
- border: "heavy"
|
||||||
|
- background: "filled"
|
||||||
|
- textColor: "primary"
|
||||||
|
|
||||||
|
- **selected**:
|
||||||
|
- border: "heavy"
|
||||||
|
- background: "highlight"
|
||||||
|
- textColor: "primary"
|
||||||
|
|
||||||
|
- **open** (modal/select):
|
||||||
|
- visible: true
|
||||||
|
- triggers: ["click"]
|
||||||
|
|
||||||
|
- **visited** (link):
|
||||||
|
- textColor: "visited"
|
||||||
|
|
||||||
|
**B. Generate ASCII for new state in .md file**
|
||||||
|
|
||||||
|
Use appropriate box-drawing characters for each state:
|
||||||
|
|
||||||
|
- **hover**: Heavy border `┏━┓┃┗━┛`
|
||||||
|
- **focus**: Double border `╔═╗║╚═╝`
|
||||||
|
- **active**: Heavy filled `┏━┓┃┗━┛` with darker interior
|
||||||
|
- **disabled**: Dashed border `┌┄┄┐┆└┄┄┘`
|
||||||
|
- **error**: Heavy border with indicator `┏━┓┃┗━┛ ⚠`
|
||||||
|
- **success**: Heavy border with indicator `┏━┓┃┗━┛ ✓`
|
||||||
|
- **checked**: Box with checkmark `[✓]` or filled indicator
|
||||||
|
- **selected**: Heavy border with highlight background
|
||||||
|
- **loading**: Spinner or progress indicator
|
||||||
|
|
||||||
|
**Template for new state section in .md file**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## {State Name} State
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
{ASCII art using appropriate border style}
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Update Files
|
||||||
|
|
||||||
|
**Write updated files**:
|
||||||
|
- Overwrite `{component-name}.uxm` with expanded states array
|
||||||
|
- Append new state sections to `{component-name}.md`
|
||||||
|
|
||||||
|
**Preserve**:
|
||||||
|
- All existing metadata (name, description, author, created, tags, category)
|
||||||
|
- All existing variables
|
||||||
|
- All existing states
|
||||||
|
- All existing props
|
||||||
|
- Component ID and version
|
||||||
|
|
||||||
|
**Update**:
|
||||||
|
- `metadata.modified` timestamp (set to current ISO 8601 timestamp)
|
||||||
|
- `behavior.states` array (add new states to end)
|
||||||
|
- `.md` file (append new state sections before Variables section if it exists, otherwise at end)
|
||||||
|
|
||||||
|
**Important**: When updating the .md file, insert new state sections AFTER existing state sections but BEFORE the Variables, Accessibility, and Usage sections.
|
||||||
|
|
||||||
|
### 5a. Validate Expanded Component
|
||||||
|
|
||||||
|
After updating the files, validate the expanded component to ensure quality:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run {SKILL_ROOT}/../uxscii-component-creator/scripts/quick_validate.py \\
|
||||||
|
./fluxwing/components/{component-name}.uxm \\
|
||||||
|
{SKILL_ROOT}/../uxscii-component-creator/schemas/uxm-component.schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation checks:**
|
||||||
|
- ✓ JSON schema compliance
|
||||||
|
- ✓ All states have valid properties
|
||||||
|
- ✓ Variables match between .uxm and .md
|
||||||
|
- ✓ Accessibility requirements met
|
||||||
|
- ✓ No duplicate states
|
||||||
|
|
||||||
|
**If validation fails:**
|
||||||
|
1. Read the error messages
|
||||||
|
2. Fix the issues in the .uxm or .md files
|
||||||
|
3. Re-validate before confirming to user
|
||||||
|
|
||||||
|
**Performance**: ~100ms (very fast!)
|
||||||
|
|
||||||
|
### 6. Confirm Expansion
|
||||||
|
|
||||||
|
Show summary:
|
||||||
|
```
|
||||||
|
✓ Expanded: ./fluxwing/components/{component-name}
|
||||||
|
✓ Added states: hover, active, disabled
|
||||||
|
✓ Total states: 4 (default, hover, active, disabled)
|
||||||
|
|
||||||
|
Preview of hover state:
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ {{text}} ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources Available
|
||||||
|
|
||||||
|
- **State patterns**: `{SKILL_ROOT}/docs/06-ascii-patterns.md` - Box-drawing characters
|
||||||
|
- **Component creation**: `{SKILL_ROOT}/docs/03-component-creation.md` - Component structure
|
||||||
|
|
||||||
|
## Example Interaction
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Expand submit-button with hover state
|
||||||
|
|
||||||
|
Skill: I found `submit-button` in ./fluxwing/components/
|
||||||
|
|
||||||
|
Current states: default
|
||||||
|
|
||||||
|
I'll add the hover state to make the button interactive.
|
||||||
|
|
||||||
|
[Reads submit-button.uxm and submit-button.md]
|
||||||
|
[Generates hover state]
|
||||||
|
[Updates both files]
|
||||||
|
|
||||||
|
✓ Expanded: ./fluxwing/components/submit-button
|
||||||
|
✓ Added states: hover
|
||||||
|
✓ Total states: 2 (default, hover)
|
||||||
|
|
||||||
|
Preview of hover state:
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Submit Form ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
|
||||||
|
Next steps:
|
||||||
|
- Add more states (active, disabled)
|
||||||
|
- Use this button in a screen
|
||||||
|
- View all components
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example with Custom States
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Add focus and error states to email-input
|
||||||
|
|
||||||
|
Skill: I found `email-input` in ./fluxwing/components/
|
||||||
|
|
||||||
|
Current states: default
|
||||||
|
|
||||||
|
I'll add the requested states: focus, error
|
||||||
|
|
||||||
|
[Generates only focus and error states]
|
||||||
|
|
||||||
|
✓ Expanded: ./fluxwing/components/email-input
|
||||||
|
✓ Added states: focus, error
|
||||||
|
✓ Total states: 3 (default, focus, error)
|
||||||
|
|
||||||
|
Note: Standard input states also include 'disabled'.
|
||||||
|
Let me know if you want to add it!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quality Standards
|
||||||
|
|
||||||
|
Ensure expanded components include:
|
||||||
|
- ✓ Valid JSON schema compliance (no breaking changes)
|
||||||
|
- ✓ All new states have definitions in .uxm behavior.states array
|
||||||
|
- ✓ All new states have ASCII sections in .md
|
||||||
|
- ✓ State properties match component type conventions
|
||||||
|
- ✓ Consistent box-drawing character usage
|
||||||
|
- ✓ Updated modification timestamp
|
||||||
|
- ✓ Preserved existing data (no data loss)
|
||||||
|
- ✓ No duplicate states (check before adding)
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
- **Always preserve existing states** (never remove or modify existing ones)
|
||||||
|
- **Detect duplicate states** (skip if state already exists in behavior.states array)
|
||||||
|
- **Validate component after expansion** (ensure valid JSON)
|
||||||
|
- **Use appropriate border styles per state** (refer to patterns doc)
|
||||||
|
- **Match visual style of existing default state** (consistent dimensions and layout)
|
||||||
|
- **Test keyboard navigation** for new interactive states
|
||||||
|
- **Insert .md sections in correct location** (after states, before Variables section)
|
||||||
|
- **Update only the modified timestamp** (preserve created timestamp)
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
If component not found:
|
||||||
|
```
|
||||||
|
✗ Component '{component-name}' not found.
|
||||||
|
|
||||||
|
Searched in:
|
||||||
|
- ./fluxwing/components/
|
||||||
|
- ./fluxwing/library/
|
||||||
|
|
||||||
|
Available components:
|
||||||
|
[List first 10 components found]
|
||||||
|
|
||||||
|
Please check the component name and try again.
|
||||||
|
```
|
||||||
|
|
||||||
|
If state already exists:
|
||||||
|
```
|
||||||
|
⚠ State 'hover' already exists in {component-name}.
|
||||||
|
Skipping duplicate state.
|
||||||
|
|
||||||
|
Current states: default, hover
|
||||||
|
Adding: active, disabled
|
||||||
|
```
|
||||||
|
|
||||||
|
If file write fails:
|
||||||
|
```
|
||||||
|
✗ Failed to update {component-name}.
|
||||||
|
|
||||||
|
Error: [specific error message]
|
||||||
|
|
||||||
|
The component files remain unchanged.
|
||||||
|
Please check file permissions and try again.
|
||||||
|
```
|
||||||
|
|
||||||
|
You're helping users create fully interactive uxscii components!
|
||||||
294
skills/fluxwing-component-expander/docs/03-component-creation.md
Normal file
294
skills/fluxwing-component-expander/docs/03-component-creation.md
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
# Component Creation - Step-by-Step Workflow
|
||||||
|
|
||||||
|
Complete guide for creating uxscii components from scratch.
|
||||||
|
|
||||||
|
## Before You Start
|
||||||
|
|
||||||
|
1. **Understand the concept** - Read `02-core-concepts.md` if you haven't
|
||||||
|
2. **Check examples** - Browse `../examples/` for similar components
|
||||||
|
3. **Plan your component** - Know what you're building
|
||||||
|
|
||||||
|
## Step-by-Step Process
|
||||||
|
|
||||||
|
### Step 1: Plan Your Component
|
||||||
|
|
||||||
|
Answer these questions:
|
||||||
|
- **What is it?** Button, input, card, modal, etc.
|
||||||
|
- **What does it do?** Primary purpose and use case
|
||||||
|
- **What's configurable?** Props users can change
|
||||||
|
- **What states does it have?** Default, hover, focus, disabled, error, success
|
||||||
|
- **How will it look?** Sketch ASCII layout
|
||||||
|
|
||||||
|
### Step 2: Create the .uxm File
|
||||||
|
|
||||||
|
Start with required fields:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "my-component",
|
||||||
|
"type": "button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "My Component",
|
||||||
|
"created": "2024-10-11T12:00:00Z",
|
||||||
|
"modified": "2024-10-11T12:00:00Z"
|
||||||
|
},
|
||||||
|
"props": {},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "my-component.md",
|
||||||
|
"width": 20,
|
||||||
|
"height": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tips:**
|
||||||
|
- ID must be kebab-case, 2-64 characters
|
||||||
|
- Version must be semantic (major.minor.patch)
|
||||||
|
- Timestamps should be ISO 8601 format
|
||||||
|
- Template file must end with `.md`
|
||||||
|
|
||||||
|
### Step 3: Add Props and Variables
|
||||||
|
|
||||||
|
Define what users can configure:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"props": {
|
||||||
|
"text": "Click me", // Default values
|
||||||
|
"variant": "primary",
|
||||||
|
"disabled": false
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "my-component.md",
|
||||||
|
"width": 20,
|
||||||
|
"height": 3,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "text",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"description": "Button label text",
|
||||||
|
"validation": {
|
||||||
|
"min": 1,
|
||||||
|
"max": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "variant",
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "primary",
|
||||||
|
"validation": {
|
||||||
|
"enum": ["primary", "secondary", "outline"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Define Default State and Behaviors
|
||||||
|
|
||||||
|
Components are created with **default state only** for fast MVP prototyping:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"border": "solid",
|
||||||
|
"background": "primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "click",
|
||||||
|
"action": "emit-click-event"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"ariaLabel": "{{text}}",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Enter", "Space"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Adding more states**: After MVP validation, use `/fluxwing-expand-component` to add hover, focus, disabled states. The command will automatically add appropriate states based on component type.
|
||||||
|
|
||||||
|
### Step 5: Add Metadata (Recommended)
|
||||||
|
|
||||||
|
Help others discover and understand your component:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "My Component",
|
||||||
|
"description": "A customizable button component with multiple variants and states",
|
||||||
|
"author": "Your Name",
|
||||||
|
"created": "2024-10-11T12:00:00Z",
|
||||||
|
"modified": "2024-10-11T12:00:00Z",
|
||||||
|
"tags": ["button", "interactive", "form"],
|
||||||
|
"category": "input"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Create the .md Template
|
||||||
|
|
||||||
|
Basic structure:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
# My Component
|
||||||
|
|
||||||
|
Brief description of what this component does.
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ {{text}} │
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): Button label text. Max 20 characters.
|
||||||
|
- `variant` (string): Visual style - "primary", "secondary", or "outline"
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: button
|
||||||
|
- **Focusable**: Yes
|
||||||
|
- **Keyboard**: Enter or Space to activate
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Primary Button
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ Submit Form │
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secondary Button
|
||||||
|
```
|
||||||
|
┌──────────────────┐
|
||||||
|
│ Cancel │
|
||||||
|
└──────────────────┘
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Tips:**
|
||||||
|
- Start with default state only (fast MVP creation)
|
||||||
|
- Document ALL variables used in template
|
||||||
|
- Include usage examples with real data
|
||||||
|
- Add accessibility notes for interactive components
|
||||||
|
|
||||||
|
**Adding more states**: After creating the component, run `/fluxwing-expand-component my-component` to add hover, focus, disabled states automatically.
|
||||||
|
|
||||||
|
### Step 7: Save Files
|
||||||
|
|
||||||
|
Save both files together:
|
||||||
|
```
|
||||||
|
./fluxwing/components/my-component.uxm
|
||||||
|
./fluxwing/components/my-component.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the directory if it doesn't exist:
|
||||||
|
```bash
|
||||||
|
mkdir -p ./fluxwing/components
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Form Input
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "text-input",
|
||||||
|
"type": "input",
|
||||||
|
"props": {
|
||||||
|
"placeholder": "Enter text",
|
||||||
|
"value": "",
|
||||||
|
"error": "",
|
||||||
|
"disabled": false
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": ["default", "focus", "error", "disabled"],
|
||||||
|
"interactions": [
|
||||||
|
{"trigger": "focus", "action": "highlight-field"},
|
||||||
|
{"trigger": "blur", "action": "validate-field"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Card
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "metric-card",
|
||||||
|
"type": "card",
|
||||||
|
"props": {
|
||||||
|
"title": "Metric",
|
||||||
|
"value": "1,234",
|
||||||
|
"change": "+12%",
|
||||||
|
"trend": "up"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modal Dialog
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "confirm-dialog",
|
||||||
|
"type": "modal",
|
||||||
|
"props": {
|
||||||
|
"title": "Confirm",
|
||||||
|
"message": "Are you sure?",
|
||||||
|
"confirmText": "Yes",
|
||||||
|
"cancelText": "No"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": ["open", "closed"],
|
||||||
|
"interactions": [
|
||||||
|
{"trigger": "click", "action": "close-modal", "target": "backdrop"},
|
||||||
|
{"trigger": "keydown", "action": "close-modal", "condition": "key === 'Escape'"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Template file not found"
|
||||||
|
**Fix**: Ensure `.md` file exists and `templateFile` path is correct
|
||||||
|
|
||||||
|
### "Variable not defined"
|
||||||
|
**Fix**: Add variable to `ascii.variables` array in `.uxm`
|
||||||
|
|
||||||
|
### "Invalid JSON"
|
||||||
|
**Fix**: Validate JSON syntax (no trailing commas, proper quotes)
|
||||||
|
|
||||||
|
### "ASCII art looks broken"
|
||||||
|
**Fix**: Use monospace font, check for consistent character width
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
**Required .uxm fields**: id, type, version, metadata (name, created, modified), props, ascii (templateFile, width, height)
|
||||||
|
|
||||||
|
**Required .md sections**: Title, default state, variables documentation
|
||||||
|
|
||||||
|
**Recommended additions**: Multiple states, accessibility attributes, usage examples, detailed metadata
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- **Compose screens**: Use `/fluxwing-scaffold` to build complete screens
|
||||||
|
- **Browse library**: Use `/fluxwing-library` to see all components
|
||||||
|
- **Learn patterns**: Check `06-ascii-patterns.md` for ASCII art reference
|
||||||
|
|
||||||
|
You can now create production-ready uxscii components!
|
||||||
498
skills/fluxwing-component-expander/docs/06-ascii-patterns.md
Normal file
498
skills/fluxwing-component-expander/docs/06-ascii-patterns.md
Normal file
@@ -0,0 +1,498 @@
|
|||||||
|
# ASCII Patterns - Visual Toolkit
|
||||||
|
|
||||||
|
Complete library of ASCII characters and patterns for uxscii components.
|
||||||
|
|
||||||
|
## Box-Drawing Characters
|
||||||
|
|
||||||
|
### Basic Borders
|
||||||
|
|
||||||
|
**Light (Default)**
|
||||||
|
```
|
||||||
|
┌─────┐
|
||||||
|
│ Box │
|
||||||
|
└─────┘
|
||||||
|
```
|
||||||
|
Characters: `┌ ─ ┐ │ └ ┘`
|
||||||
|
|
||||||
|
**Rounded (Friendly)**
|
||||||
|
```
|
||||||
|
╭─────╮
|
||||||
|
│ Box │
|
||||||
|
╰─────╯
|
||||||
|
```
|
||||||
|
Characters: `╭ ─ ╮ │ ╰ ╯`
|
||||||
|
|
||||||
|
**Double (Emphasis)**
|
||||||
|
```
|
||||||
|
╔═════╗
|
||||||
|
║ Box ║
|
||||||
|
╚═════╝
|
||||||
|
```
|
||||||
|
Characters: `╔ ═ ╗ ║ ╚ ╝`
|
||||||
|
|
||||||
|
**Heavy (Strong)**
|
||||||
|
```
|
||||||
|
┏━━━━━┓
|
||||||
|
┃ Box ┃
|
||||||
|
┗━━━━━┛
|
||||||
|
```
|
||||||
|
Characters: `┏ ━ ┓ ┃ ┗ ┛`
|
||||||
|
|
||||||
|
### Complex Borders
|
||||||
|
|
||||||
|
**With Dividers**
|
||||||
|
```
|
||||||
|
┌─────────┬─────────┐
|
||||||
|
│ Col 1 │ Col 2 │
|
||||||
|
├─────────┼─────────┤
|
||||||
|
│ Data │ Data │
|
||||||
|
└─────────┴─────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nested**
|
||||||
|
```
|
||||||
|
╭─────────────────╮
|
||||||
|
│ Outer │
|
||||||
|
│ ┌─────────────┐ │
|
||||||
|
│ │ Inner │ │
|
||||||
|
│ └─────────────┘ │
|
||||||
|
╰─────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component State Patterns
|
||||||
|
|
||||||
|
### Buttons
|
||||||
|
|
||||||
|
**Default**
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Click ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hover (Highlighted)**
|
||||||
|
```
|
||||||
|
░▓▓▓▓▓▓▓▓▓▓░
|
||||||
|
░▓ Click ▓░
|
||||||
|
░▓▓▓▓▓▓▓▓▓▓░
|
||||||
|
```
|
||||||
|
|
||||||
|
**Focus (Ring)**
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━┓✨
|
||||||
|
┃ Click ┃
|
||||||
|
┗━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
**Disabled (Grayed)**
|
||||||
|
```
|
||||||
|
┌ ─ ─ ─ ─ ┐
|
||||||
|
│ Click │
|
||||||
|
└ ─ ─ ─ ─ ┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pressed/Active**
|
||||||
|
```
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓▓Click ▓▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form Inputs
|
||||||
|
|
||||||
|
**Text Input (Empty)**
|
||||||
|
```
|
||||||
|
[____________________]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Filled)**
|
||||||
|
```
|
||||||
|
[john@example.com ]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Focus)**
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃john@example.com│ ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Error)**
|
||||||
|
```
|
||||||
|
[invalid-email ]⚠️
|
||||||
|
❌ Please enter valid email
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Success)**
|
||||||
|
```
|
||||||
|
[john@example.com ]✅
|
||||||
|
```
|
||||||
|
|
||||||
|
**Text Input (Disabled)**
|
||||||
|
```
|
||||||
|
[────────────────────]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Password (Masked)**
|
||||||
|
```
|
||||||
|
[•••••••• ]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Password (With Toggle)**
|
||||||
|
```
|
||||||
|
[•••••••• ]👁️
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checkboxes & Radios
|
||||||
|
|
||||||
|
**Checkbox (Unchecked)**
|
||||||
|
```
|
||||||
|
[□] Option 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Checkbox (Checked)**
|
||||||
|
```
|
||||||
|
[✓] Option 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Checkbox (Indeterminate)**
|
||||||
|
```
|
||||||
|
[▬] Option 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Radio (Unselected)**
|
||||||
|
```
|
||||||
|
○ Option A
|
||||||
|
```
|
||||||
|
|
||||||
|
**Radio (Selected)**
|
||||||
|
```
|
||||||
|
◉ Option A
|
||||||
|
```
|
||||||
|
|
||||||
|
### Selects & Dropdowns
|
||||||
|
|
||||||
|
**Select (Closed)**
|
||||||
|
```
|
||||||
|
[Choose option ▼]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Select (Open)**
|
||||||
|
```
|
||||||
|
[Current option ▼]
|
||||||
|
╭────────────────╮
|
||||||
|
│ Option 1 │
|
||||||
|
│ ● Option 2 │
|
||||||
|
│ Option 3 │
|
||||||
|
╰────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sliders
|
||||||
|
|
||||||
|
**Slider (Basic)**
|
||||||
|
```
|
||||||
|
├────────●───────┤
|
||||||
|
0% 100%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Slider (With Value)**
|
||||||
|
```
|
||||||
|
├────────●───────┤ 45%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Range Slider**
|
||||||
|
```
|
||||||
|
├────●────●──────┤
|
||||||
|
20% 80%
|
||||||
|
```
|
||||||
|
|
||||||
|
## Status Indicators
|
||||||
|
|
||||||
|
### Icons
|
||||||
|
|
||||||
|
**Success**: ✅ ✓ ●
|
||||||
|
**Error**: ❌ ✗ ⚠️ ⛔
|
||||||
|
**Warning**: ⚠️ ⚡ △
|
||||||
|
**Info**: ℹ️ ⓘ ◉
|
||||||
|
**Loading**: ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏
|
||||||
|
|
||||||
|
### Progress Bars
|
||||||
|
|
||||||
|
**Loading (40%)**
|
||||||
|
```
|
||||||
|
████▓▓▓▓▓▓ 40%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Loading (Indeterminate)**
|
||||||
|
```
|
||||||
|
▓▓▓░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
**Steps**
|
||||||
|
```
|
||||||
|
● ━━━ ● ━━━ ○ ━━━ ○
|
||||||
|
```
|
||||||
|
|
||||||
|
### Badges
|
||||||
|
|
||||||
|
**Count Badge**
|
||||||
|
```
|
||||||
|
[Inbox] ●3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Status Badge**
|
||||||
|
```
|
||||||
|
● Online
|
||||||
|
○ Offline
|
||||||
|
```
|
||||||
|
|
||||||
|
**Label Badge**
|
||||||
|
```
|
||||||
|
▓ New ▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Display
|
||||||
|
|
||||||
|
### Lists
|
||||||
|
|
||||||
|
**Unordered**
|
||||||
|
```
|
||||||
|
• Item 1
|
||||||
|
• Item 2
|
||||||
|
• Item 3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ordered**
|
||||||
|
```
|
||||||
|
1. First
|
||||||
|
2. Second
|
||||||
|
3. Third
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nested**
|
||||||
|
```
|
||||||
|
• Parent
|
||||||
|
├─ Child 1
|
||||||
|
├─ Child 2
|
||||||
|
└─ Child 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tables
|
||||||
|
|
||||||
|
**Simple**
|
||||||
|
```
|
||||||
|
┌─────┬─────┬─────┐
|
||||||
|
│ A │ B │ C │
|
||||||
|
├─────┼─────┼─────┤
|
||||||
|
│ 1 │ 2 │ 3 │
|
||||||
|
│ 4 │ 5 │ 6 │
|
||||||
|
└─────┴─────┴─────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Header**
|
||||||
|
```
|
||||||
|
╔═══════╦═══════╦═══════╗
|
||||||
|
║ Name ║ Email ║ Status║
|
||||||
|
╠═══════╬═══════╬═══════╣
|
||||||
|
║ Alice ║ a@... ║ ✓ ║
|
||||||
|
║ Bob ║ b@... ║ ○ ║
|
||||||
|
╚═══════╩═══════╩═══════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cards
|
||||||
|
|
||||||
|
**Basic**
|
||||||
|
```
|
||||||
|
╭─────────────╮
|
||||||
|
│ Title │
|
||||||
|
├─────────────┤
|
||||||
|
│ Content... │
|
||||||
|
╰─────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Actions**
|
||||||
|
```
|
||||||
|
╭─────────────────────╮
|
||||||
|
│ Card Title │
|
||||||
|
├─────────────────────┤
|
||||||
|
│ Content goes here │
|
||||||
|
│ │
|
||||||
|
│ [Action] ─┐ │
|
||||||
|
╰─────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Metric Card**
|
||||||
|
```
|
||||||
|
╭───────────╮
|
||||||
|
│ Revenue │
|
||||||
|
├───────────┤
|
||||||
|
│ $24,567 │
|
||||||
|
│ +12.5% ↗ │
|
||||||
|
╰───────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
|
||||||
|
### Tabs
|
||||||
|
|
||||||
|
**Horizontal**
|
||||||
|
```
|
||||||
|
[Active] [Tab 2] [Tab 3]
|
||||||
|
─────────
|
||||||
|
Content for active tab
|
||||||
|
```
|
||||||
|
|
||||||
|
**Vertical**
|
||||||
|
```
|
||||||
|
┌─ ● Tab 1
|
||||||
|
├─ ○ Tab 2
|
||||||
|
└─ ○ Tab 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Breadcrumbs
|
||||||
|
|
||||||
|
```
|
||||||
|
Home > Products > Details
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pagination
|
||||||
|
|
||||||
|
```
|
||||||
|
‹ Prev [1] 2 3 4 5 Next ›
|
||||||
|
```
|
||||||
|
|
||||||
|
### Menu
|
||||||
|
|
||||||
|
**Dropdown**
|
||||||
|
```
|
||||||
|
File ▼
|
||||||
|
╭──────────╮
|
||||||
|
│ New │
|
||||||
|
│ Open │
|
||||||
|
│ Save │
|
||||||
|
├──────────┤
|
||||||
|
│ Exit │
|
||||||
|
╰──────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sidebar**
|
||||||
|
```
|
||||||
|
╭─────────╮
|
||||||
|
│ • Home │
|
||||||
|
│ • Users │
|
||||||
|
│ • Data │
|
||||||
|
│ • Info │
|
||||||
|
╰─────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modals & Overlays
|
||||||
|
|
||||||
|
### Modal
|
||||||
|
|
||||||
|
**Simple**
|
||||||
|
```
|
||||||
|
╔═══════════════════╗
|
||||||
|
║ Modal Title ║
|
||||||
|
╠═══════════════════╣
|
||||||
|
║ Content here... ║
|
||||||
|
║ ║
|
||||||
|
║ [OK] [Cancel] ║
|
||||||
|
╚═══════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Close**
|
||||||
|
```
|
||||||
|
╔═══════════════════╗✕
|
||||||
|
║ Title ║
|
||||||
|
╠═══════════════════╣
|
||||||
|
║ Content... ║
|
||||||
|
╚═══════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
### Toast/Alert
|
||||||
|
|
||||||
|
**Success**
|
||||||
|
```
|
||||||
|
╭─────────────────╮
|
||||||
|
│ ✅ Saved! │
|
||||||
|
╰─────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error**
|
||||||
|
```
|
||||||
|
╭─────────────────────╮
|
||||||
|
│ ❌ Error: Try again │
|
||||||
|
╰─────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warning**
|
||||||
|
```
|
||||||
|
╭────────────────────╮
|
||||||
|
│ ⚠️ Warning message │
|
||||||
|
╰────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arrows & Indicators
|
||||||
|
|
||||||
|
### Directional
|
||||||
|
```
|
||||||
|
↑ ↓ ← →
|
||||||
|
↗ ↘ ↙ ↖
|
||||||
|
⬆ ⬇ ⬅ ➡
|
||||||
|
▲ ▼ ◀ ▶
|
||||||
|
```
|
||||||
|
|
||||||
|
### Trend
|
||||||
|
```
|
||||||
|
↗️ Up trend
|
||||||
|
→ Flat
|
||||||
|
↘️ Down trend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Expand/Collapse
|
||||||
|
```
|
||||||
|
▼ Expanded
|
||||||
|
▶ Collapsed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Spacing & Alignment
|
||||||
|
|
||||||
|
### Padding Example
|
||||||
|
```
|
||||||
|
╭──────────────╮
|
||||||
|
│ │ ← 1 line padding
|
||||||
|
│ Content │
|
||||||
|
│ │ ← 1 line padding
|
||||||
|
╰──────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grid Alignment
|
||||||
|
```
|
||||||
|
╭────╮╭────╮╭────╮ ← No gap
|
||||||
|
╭────╮ ╭────╮ ╭────╮ ← 1 char gap
|
||||||
|
╭────╮ ╭────╮ ╭────╮ ← 2 char gap
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips for Consistent Patterns
|
||||||
|
|
||||||
|
1. **Choose one border style per component** - Don't mix light/rounded/heavy
|
||||||
|
2. **Use consistent spacing** - Multiples of 4 characters work well
|
||||||
|
3. **Align related elements** - Keep boxes at same width when stacked
|
||||||
|
4. **Test in monospace** - Always preview in monospace font
|
||||||
|
5. **Consider state transitions** - Visual changes should be clear
|
||||||
|
|
||||||
|
## Copy-Paste Ready
|
||||||
|
|
||||||
|
```
|
||||||
|
Light box: ┌─┐│└┘
|
||||||
|
Rounded: ╭─╮│╰╯
|
||||||
|
Double: ╔═╗║╚╝
|
||||||
|
Heavy: ┏━┓┃┗┛
|
||||||
|
|
||||||
|
Checkbox: [□] [✓]
|
||||||
|
Radio: ○ ◉
|
||||||
|
Status: ✅ ❌ ⚠️ ℹ️
|
||||||
|
Arrows: ↑↓←→ ↗↘
|
||||||
|
```
|
||||||
|
|
||||||
|
Use these patterns to create beautiful, consistent uxscii components!
|
||||||
300
skills/fluxwing-component-viewer/SKILL.md
Normal file
300
skills/fluxwing-component-viewer/SKILL.md
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
---
|
||||||
|
name: Fluxwing Component Viewer
|
||||||
|
description: View detailed information about a specific uxscii component including metadata, states, props, and ASCII preview. Use when working with .uxm files, when user wants to see, view, inspect, or get details about a .uxm component.
|
||||||
|
version: 0.0.1
|
||||||
|
author: Trabian
|
||||||
|
allowed-tools: Read, Glob, Grep
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fluxwing Component Viewer
|
||||||
|
|
||||||
|
View detailed information about a specific uxscii component from any source.
|
||||||
|
|
||||||
|
## Data Location Rules
|
||||||
|
|
||||||
|
**READ from (bundled templates - reference only):**
|
||||||
|
- `{SKILL_ROOT}/../uxscii-component-creator/templates/` - 11 component templates
|
||||||
|
- `{SKILL_ROOT}/../uxscii-screen-scaffolder/templates/` - 2 screen examples (if available)
|
||||||
|
|
||||||
|
**READ from (project workspace):**
|
||||||
|
- `./fluxwing/components/` - Your created components
|
||||||
|
- `./fluxwing/library/` - Customized template copies
|
||||||
|
- `./fluxwing/screens/` - Your created screens
|
||||||
|
|
||||||
|
**NEVER write - this is a read-only viewer!**
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
Display comprehensive details about a single uxscii component, including metadata, ASCII template preview, and context-appropriate actions.
|
||||||
|
|
||||||
|
## Component Lookup Process
|
||||||
|
|
||||||
|
### 1. Parse Component Name
|
||||||
|
- Extract component name from user request
|
||||||
|
- If no name provided: Ask "Which component would you like to view?"
|
||||||
|
- Normalize name to lowercase with hyphens
|
||||||
|
|
||||||
|
### 2. Search Priority Order (Stop at First Match)
|
||||||
|
Search these locations in order and stop at the first match:
|
||||||
|
|
||||||
|
1. **Project Components**: `./fluxwing/components/[name].uxm`
|
||||||
|
- User/agent-created custom components
|
||||||
|
- Fully editable
|
||||||
|
- Tag as: "Your Component"
|
||||||
|
|
||||||
|
2. **Project Library**: `./fluxwing/library/[name].uxm`
|
||||||
|
- Customized copies of bundled templates
|
||||||
|
- Fully editable
|
||||||
|
- Tag as: "Your Library"
|
||||||
|
|
||||||
|
3. **Bundled Templates**: `{SKILL_ROOT}/../uxscii-component-creator/templates/[name].uxm`
|
||||||
|
- Read-only reference templates
|
||||||
|
- Must be copied to edit
|
||||||
|
- Tag as: "Bundled Template"
|
||||||
|
|
||||||
|
**Important**: Stop searching after first match. If found in bundled templates, check if it also exists in user's project and add a note: "💡 You also have a customized version in ./fluxwing/library/"
|
||||||
|
|
||||||
|
### 3. Read Component Files
|
||||||
|
For the matched component, read both files:
|
||||||
|
- `[name].uxm` - JSON metadata
|
||||||
|
- `[name].md` - ASCII template
|
||||||
|
|
||||||
|
## Display Format
|
||||||
|
|
||||||
|
### Concise View (Default)
|
||||||
|
|
||||||
|
Present component information in a clean, scannable format:
|
||||||
|
|
||||||
|
```
|
||||||
|
📄 PRIMARY-BUTTON
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
📦 Source: Bundled Template
|
||||||
|
📍 Location: Component Creator Templates
|
||||||
|
⏱️ Modified: 2024-10-11 10:30:00
|
||||||
|
🔖 Version: 1.0.0
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Standard clickable button with hover, focus, and disabled states
|
||||||
|
|
||||||
|
Component Details:
|
||||||
|
• Type: button
|
||||||
|
• Props: text (string), variant (string), disabled (boolean)
|
||||||
|
• States: default, hover, focus, disabled
|
||||||
|
• Accessibility: ✓ Role (button), ✓ Focusable, ✓ Keyboard (Space, Enter)
|
||||||
|
|
||||||
|
ASCII Template Preview (first 20 lines):
|
||||||
|
|
||||||
|
Default State:
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ {{text}} ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
|
||||||
|
Hover State:
|
||||||
|
░▓▓▓▓▓▓▓▓▓▓▓░
|
||||||
|
░▓ {{text}} ▓░
|
||||||
|
░▓▓▓▓▓▓▓▓▓▓░
|
||||||
|
|
||||||
|
Disabled State:
|
||||||
|
┌ ─ ─ ─ ─ ─┐
|
||||||
|
│ {{text}} │
|
||||||
|
└ ─ ─ ─ ─ ─┘
|
||||||
|
|
||||||
|
[... 1 more state]
|
||||||
|
|
||||||
|
Template has 4 states total. View full template?
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format Guidelines
|
||||||
|
|
||||||
|
**Header Section:**
|
||||||
|
- Component name in CAPS
|
||||||
|
- Emoji indicators:
|
||||||
|
- 📦 = Bundled Template
|
||||||
|
- ✏️ = Your Library
|
||||||
|
- 🎨 = Your Component
|
||||||
|
- Full file path for clarity
|
||||||
|
- Last modified timestamp (if available)
|
||||||
|
- Version from metadata
|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
- Use the `metadata.description` field from .uxm file
|
||||||
|
- Keep it concise (1-2 lines)
|
||||||
|
|
||||||
|
**Component Details:**
|
||||||
|
- **Type**: Direct from .uxm `type` field
|
||||||
|
- **Props**: List prop names with types in parentheses
|
||||||
|
- Format: `propName (type)`
|
||||||
|
- Example: `text (string), disabled (boolean)`
|
||||||
|
- **States**: Comma-separated list of state names
|
||||||
|
- **Accessibility**: Show checkmarks for present features
|
||||||
|
- Role, Focusable, Keyboard shortcuts, ARIA labels
|
||||||
|
|
||||||
|
**ASCII Template Preview:**
|
||||||
|
- Show first 20 lines by default
|
||||||
|
- If template has multiple states, show state labels
|
||||||
|
- If template exceeds 20 lines, add: `[... N more states/lines]`
|
||||||
|
- Preserve exact spacing and box-drawing characters
|
||||||
|
- Show variables as `{{variableName}}`
|
||||||
|
|
||||||
|
### Truncation Logic
|
||||||
|
|
||||||
|
If `.md` template exceeds 20 lines:
|
||||||
|
1. Count total states/sections in template
|
||||||
|
2. Show first 2-3 states completely
|
||||||
|
3. Add summary line: `[... N more states]`
|
||||||
|
4. Offer: "View full template?" as interactive option
|
||||||
|
|
||||||
|
## Interactive Options
|
||||||
|
|
||||||
|
After displaying the component, offer context-appropriate actions:
|
||||||
|
|
||||||
|
### For Bundled Templates (read-only)
|
||||||
|
|
||||||
|
```
|
||||||
|
What would you like to do?
|
||||||
|
|
||||||
|
1️⃣ Copy to project library (makes it editable)
|
||||||
|
2️⃣ View full template file (all states)
|
||||||
|
3️⃣ View full metadata (complete .uxm)
|
||||||
|
4️⃣ Browse all components
|
||||||
|
```
|
||||||
|
|
||||||
|
**Action Details:**
|
||||||
|
- **Copy**: Copy both .uxm and .md to `./fluxwing/library/`
|
||||||
|
- **View full template**: Display complete .md file (no truncation)
|
||||||
|
- **View full metadata**: Display complete .uxm JSON
|
||||||
|
- **Browse**: Return to library browser
|
||||||
|
|
||||||
|
### For Project Files (./fluxwing/library/ or ./fluxwing/components/)
|
||||||
|
|
||||||
|
```
|
||||||
|
What would you like to do?
|
||||||
|
|
||||||
|
1️⃣ Edit component (modify .uxm and .md)
|
||||||
|
2️⃣ View full template file (all states)
|
||||||
|
3️⃣ View full metadata (complete .uxm)
|
||||||
|
4️⃣ Delete component (manual: remove files from filesystem)
|
||||||
|
5️⃣ Browse all components
|
||||||
|
```
|
||||||
|
|
||||||
|
**Action Details:**
|
||||||
|
- **Edit**: Open both .uxm and .md for editing
|
||||||
|
- **Delete**: Don't automatically delete - instruct user:
|
||||||
|
```
|
||||||
|
To delete this component, remove these files from your filesystem:
|
||||||
|
• ./fluxwing/components/[name].uxm
|
||||||
|
• ./fluxwing/components/[name].md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### No Component Name Provided
|
||||||
|
|
||||||
|
```
|
||||||
|
Which component would you like to view?
|
||||||
|
|
||||||
|
Example:
|
||||||
|
"Show me primary-button"
|
||||||
|
"View details for user-card"
|
||||||
|
|
||||||
|
View all available components: Ask me to "show me all components"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component Not Found
|
||||||
|
|
||||||
|
```
|
||||||
|
✗ Component '[name]' not found.
|
||||||
|
|
||||||
|
Searched in:
|
||||||
|
- ./fluxwing/components/
|
||||||
|
- ./fluxwing/library/
|
||||||
|
- Bundled templates
|
||||||
|
|
||||||
|
Available components:
|
||||||
|
[List first 10 components found]
|
||||||
|
|
||||||
|
Did you mean one of these?
|
||||||
|
- primary-button
|
||||||
|
- submit-button
|
||||||
|
- user-card
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fuzzy search**: If component name is close to an existing component, suggest it:
|
||||||
|
```
|
||||||
|
✗ Component 'primry-button' not found.
|
||||||
|
|
||||||
|
Did you mean 'primary-button'?
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Read Errors
|
||||||
|
|
||||||
|
```
|
||||||
|
✗ Found component but couldn't read files.
|
||||||
|
|
||||||
|
Location: ./fluxwing/components/[name].uxm
|
||||||
|
Error: [specific error message]
|
||||||
|
|
||||||
|
Please check file permissions and try again.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Interactions
|
||||||
|
|
||||||
|
### Example 1: View Bundled Template
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Show me the primary-button
|
||||||
|
|
||||||
|
Skill: 📄 PRIMARY-BUTTON
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
📦 Source: Bundled Template
|
||||||
|
📍 Location: Component Creator Templates
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Standard clickable button with hover, focus, and disabled states
|
||||||
|
|
||||||
|
Component Details:
|
||||||
|
• Type: button
|
||||||
|
• Props: text (string), variant (string), disabled (boolean)
|
||||||
|
• States: default, hover, focus, disabled
|
||||||
|
• Accessibility: ✓ Full support
|
||||||
|
|
||||||
|
[ASCII preview shown...]
|
||||||
|
|
||||||
|
What would you like to do?
|
||||||
|
1️⃣ Copy to project library
|
||||||
|
2️⃣ View full template
|
||||||
|
3️⃣ View metadata
|
||||||
|
4️⃣ Browse all components
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: View User Component
|
||||||
|
|
||||||
|
```
|
||||||
|
User: What's in my submit-button component?
|
||||||
|
|
||||||
|
Skill: 📄 SUBMIT-BUTTON
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
🎨 Source: Your Component
|
||||||
|
📍 Location: ./fluxwing/components/submit-button.uxm
|
||||||
|
⏱️ Modified: 2024-10-15 14:23:00
|
||||||
|
|
||||||
|
Description:
|
||||||
|
Custom submit button for forms
|
||||||
|
|
||||||
|
Component Details:
|
||||||
|
• Type: button
|
||||||
|
• Props: text (string)
|
||||||
|
• States: default
|
||||||
|
• Accessibility: ✓ Basic support
|
||||||
|
|
||||||
|
[ASCII preview shown...]
|
||||||
|
|
||||||
|
💡 Tip: Add more states with "expand submit-button with hover and disabled"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- **Core Concepts**: See `{SKILL_ROOT}/docs/02-core-concepts.md` for component fundamentals
|
||||||
|
|
||||||
|
You're helping users understand their uxscii components in detail!
|
||||||
292
skills/fluxwing-component-viewer/docs/02-core-concepts.md
Normal file
292
skills/fluxwing-component-viewer/docs/02-core-concepts.md
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
# Core Concepts - Understanding uxscii
|
||||||
|
|
||||||
|
The fundamental concepts you need to understand the uxscii standard.
|
||||||
|
|
||||||
|
## The Philosophy
|
||||||
|
|
||||||
|
**uxscii** is an AI-native design markup language that combines:
|
||||||
|
- **Human-readable** ASCII art (visual representation)
|
||||||
|
- **Machine-parseable** JSON metadata (component definition)
|
||||||
|
- **Version-control friendly** text files (meaningful diffs)
|
||||||
|
|
||||||
|
## The Two-File System
|
||||||
|
|
||||||
|
Every component consists of exactly TWO files that work together:
|
||||||
|
|
||||||
|
### File 1: `.uxm` (Component Metadata)
|
||||||
|
|
||||||
|
JSON file containing:
|
||||||
|
- **Identity**: ID, type, version
|
||||||
|
- **Metadata**: Name, description, author, timestamps
|
||||||
|
- **Properties**: Configurable props with defaults
|
||||||
|
- **Behavior**: States, interactions, accessibility
|
||||||
|
- **ASCII Reference**: Template file, dimensions, variables
|
||||||
|
|
||||||
|
**Purpose**: Tells agents WHAT the component is and HOW it behaves.
|
||||||
|
|
||||||
|
### File 2: `.md` (Visual Template)
|
||||||
|
|
||||||
|
Markdown file containing:
|
||||||
|
- **ASCII Art**: Visual representation of the component
|
||||||
|
- **Variables**: `{{variableName}}` placeholders for dynamic content
|
||||||
|
- **States**: Visual variants (default, hover, focus, disabled, etc.)
|
||||||
|
- **Documentation**: Usage examples and variable descriptions
|
||||||
|
|
||||||
|
**Purpose**: Shows agents WHAT the component looks like.
|
||||||
|
|
||||||
|
## How They Connect
|
||||||
|
|
||||||
|
```
|
||||||
|
submit-button.uxm says:
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "submit-button.md", ← Points to template
|
||||||
|
"variables": [
|
||||||
|
{"name": "text", "type": "string"} ← Defines variables
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
submit-button.md contains:
|
||||||
|
╭──────────────────╮
|
||||||
|
│ {{text}} │ ← Uses the variable
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
The `.uxm` **defines** variables, the `.md` **uses** them.
|
||||||
|
|
||||||
|
## Component Anatomy
|
||||||
|
|
||||||
|
### Minimal Required Fields (.uxm)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "kebab-case-name", // Required: Unique identifier
|
||||||
|
"type": "button|input|card|...", // Required: Component type
|
||||||
|
"version": "1.0.0", // Required: Semantic version
|
||||||
|
"metadata": {
|
||||||
|
"name": "Human Name", // Required: Display name
|
||||||
|
"created": "ISO-timestamp", // Required: Creation date
|
||||||
|
"modified": "ISO-timestamp" // Required: Last modified
|
||||||
|
},
|
||||||
|
"props": {}, // Required: Can be empty object
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "name.md", // Required: Template filename
|
||||||
|
"width": 20, // Required: Character width
|
||||||
|
"height": 3 // Required: Character height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional But Recommended Fields
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"description": "What this component does",
|
||||||
|
"author": "Your name",
|
||||||
|
"tags": ["searchable", "keywords"],
|
||||||
|
"category": "input|display|layout|..."
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [/* multiple states */],
|
||||||
|
"interactions": [/* user interactions */],
|
||||||
|
"accessibility": {/* ARIA attributes */}
|
||||||
|
},
|
||||||
|
"extends": "base-component-id" // Component inheritance
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variable Substitution
|
||||||
|
|
||||||
|
### In .uxm (Definition)
|
||||||
|
```json
|
||||||
|
"props": {
|
||||||
|
"text": "Click me", // Default value
|
||||||
|
"disabled": false
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "text",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"description": "Button label"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### In .md (Usage)
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ {{text}} │ ← Replaced with prop value
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Result
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ Click me │ ← Actual rendered output
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## States and Behaviors
|
||||||
|
|
||||||
|
Components can have multiple visual states:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {"border": "solid"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {"border": "highlighted"},
|
||||||
|
"triggers": ["mouseenter"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disabled",
|
||||||
|
"properties": {"border": "dashed", "opacity": 0.5}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each state can have a corresponding ASCII representation in the `.md` file.
|
||||||
|
|
||||||
|
## Two-Phase Component Creation
|
||||||
|
|
||||||
|
Fluxwing optimizes for fast MVP prototyping with a two-phase approach:
|
||||||
|
|
||||||
|
### Phase 1: Create with Default State
|
||||||
|
|
||||||
|
When you create a component, it starts with **only the default state**:
|
||||||
|
- Fast creation for quick prototyping
|
||||||
|
- MVP-ready for early discussions
|
||||||
|
- Minimal file size and complexity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/fluxwing-create submit-button
|
||||||
|
# Creates component with default state only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Expand with Interactive States
|
||||||
|
|
||||||
|
After MVP validation, add interaction states on demand:
|
||||||
|
- Adds hover, focus, disabled states
|
||||||
|
- Smart defaults based on component type
|
||||||
|
- Preserves all existing data
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/fluxwing-expand-component submit-button
|
||||||
|
# Adds hover, active, disabled states
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why Two Phases?
|
||||||
|
|
||||||
|
**Benefits**:
|
||||||
|
- **60-80% faster** component creation
|
||||||
|
- Focus on core functionality first
|
||||||
|
- Add polish when needed, not upfront
|
||||||
|
- Cleaner diffs in version control
|
||||||
|
|
||||||
|
**Workflow**:
|
||||||
|
```
|
||||||
|
1. Create → Default state only (fast)
|
||||||
|
2. Discuss → Review with stakeholders
|
||||||
|
3. Validate → Confirm design works
|
||||||
|
4. Expand → Add interactive states (polish)
|
||||||
|
```
|
||||||
|
|
||||||
|
This approach aligns with agile development: ship fast, iterate based on feedback.
|
||||||
|
|
||||||
|
## Component Types
|
||||||
|
|
||||||
|
Standard types:
|
||||||
|
- **button**: Clickable action triggers
|
||||||
|
- **input**: Text entry fields
|
||||||
|
- **card**: Content containers
|
||||||
|
- **navigation**: Menu and navigation
|
||||||
|
- **form**: Multi-field forms
|
||||||
|
- **list**: Data displays
|
||||||
|
- **modal**: Overlay dialogs
|
||||||
|
- **table**: Tabular data
|
||||||
|
- **badge**: Small indicators
|
||||||
|
- **alert**: Notifications
|
||||||
|
- **container**: Layout wrappers
|
||||||
|
- **text**: Text displays
|
||||||
|
- **image**: Image placeholders
|
||||||
|
- **divider**: Visual separators
|
||||||
|
- **custom**: Anything else
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
### Component IDs (kebab-case)
|
||||||
|
- ✓ `submit-button`
|
||||||
|
- ✓ `email-input`
|
||||||
|
- ✓ `user-profile-card`
|
||||||
|
- ✗ `submitButton` (camelCase)
|
||||||
|
- ✗ `Submit Button` (spaces)
|
||||||
|
|
||||||
|
### Variables (camelCase)
|
||||||
|
- ✓ `userName`
|
||||||
|
- ✓ `isDisabled`
|
||||||
|
- ✓ `maxLength`
|
||||||
|
- ✗ `user-name` (kebab-case)
|
||||||
|
- ✗ `user_name` (snake_case)
|
||||||
|
|
||||||
|
### Files (kebab-case + extension)
|
||||||
|
- ✓ `submit-button.uxm`
|
||||||
|
- ✓ `submit-button.md`
|
||||||
|
- ✗ `submitButton.uxm`
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
Every interactive component should include accessibility attributes:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"behavior": {
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button", // ARIA role
|
||||||
|
"ariaLabel": "Submit form", // Screen reader label
|
||||||
|
"focusable": true, // Can receive focus
|
||||||
|
"keyboardSupport": ["Enter", "Space"] // Key bindings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Composition
|
||||||
|
|
||||||
|
Components can reference other components to create screens:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "login-screen",
|
||||||
|
"type": "container",
|
||||||
|
"props": {
|
||||||
|
"components": [
|
||||||
|
"email-input", // References another component
|
||||||
|
"password-input",
|
||||||
|
"submit-button"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Takeaways
|
||||||
|
|
||||||
|
1. **Two files, one component**: `.uxm` for logic, `.md` for visuals
|
||||||
|
2. **Variables bridge them**: Defined in `.uxm`, used in `.md`
|
||||||
|
3. **States create interactivity**: Multiple visual representations
|
||||||
|
4. **Types organize components**: Standard categories for consistency
|
||||||
|
5. **Naming matters**: Conventions ensure compatibility
|
||||||
|
6. **Accessibility is essential**: ARIA roles for all interactive elements
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- **See it in action**: Check `../examples/primary-button.{uxm,md}`
|
||||||
|
- **Create your own**: Follow `03-component-creation.md`
|
||||||
|
- **Learn patterns**: Browse `06-ascii-patterns.md`
|
||||||
|
|
||||||
|
You now understand the foundation of uxscii!
|
||||||
291
skills/fluxwing-enhancer/SKILL.md
Normal file
291
skills/fluxwing-enhancer/SKILL.md
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
---
|
||||||
|
name: Fluxwing Enhancer
|
||||||
|
description: Enhance uxscii components from sketch to production fidelity. Use when working with .uxm files marked as "fidelity: sketch" or when user wants to add detail and polish to components.
|
||||||
|
version: 0.0.1
|
||||||
|
author: Trabian
|
||||||
|
allowed-tools: Read, Write, Edit, Glob, Grep, Task, TodoWrite
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fluxwing Enhancer
|
||||||
|
|
||||||
|
Progressively enhance uxscii components from sketch to production quality.
|
||||||
|
|
||||||
|
## Data Location Rules
|
||||||
|
|
||||||
|
**READ from:**
|
||||||
|
- `./fluxwing/components/` - User components to enhance
|
||||||
|
- `{SKILL_ROOT}/../fluxwing-component-creator/templates/state-additions/` - State templates
|
||||||
|
- `{SKILL_ROOT}/docs/` - Enhancement documentation
|
||||||
|
|
||||||
|
**LOAD for copy-on-update logic:**
|
||||||
|
- `{SKILL_ROOT}/../shared/docs/copy-versioning.md` - Versioning pattern for fidelity enhancements
|
||||||
|
|
||||||
|
**WRITE to:**
|
||||||
|
- `./fluxwing/components/` - New versioned components (copy-on-update: {id}-v{N+1})
|
||||||
|
- `./fluxwing/screens/` - Updated screen .rendered.md (if applicable)
|
||||||
|
|
||||||
|
**IMPORTANT**: Enhancer creates NEW versions instead of overwriting originals. Each fidelity enhancement creates a new `-v{N}` copy.
|
||||||
|
|
||||||
|
## Fidelity Levels
|
||||||
|
|
||||||
|
### sketch (Fast Scaffold Output)
|
||||||
|
- Basic .uxm with minimal metadata
|
||||||
|
- May lack .md file or have simple .md
|
||||||
|
- Single "default" state
|
||||||
|
- Creation time: ~10 seconds
|
||||||
|
|
||||||
|
**Characteristics:**
|
||||||
|
- Minimal description
|
||||||
|
- Generic tags
|
||||||
|
- No examples
|
||||||
|
- Basic props only
|
||||||
|
|
||||||
|
### basic (First Enhancement)
|
||||||
|
- Enriched metadata (better description, tags)
|
||||||
|
- Simple .md with clean ASCII
|
||||||
|
- Default state only
|
||||||
|
- Enhancement time: ~30 seconds
|
||||||
|
|
||||||
|
**Improvements over sketch:**
|
||||||
|
- Detailed description
|
||||||
|
- Specific tags
|
||||||
|
- Clean ASCII art
|
||||||
|
- Documented variables
|
||||||
|
|
||||||
|
### detailed (Second Enhancement)
|
||||||
|
- Rich metadata with usage examples
|
||||||
|
- Polished .md with careful ASCII art
|
||||||
|
- 2-3 interaction states (hover, focus)
|
||||||
|
- Enhancement time: ~90 seconds
|
||||||
|
|
||||||
|
**Improvements over basic:**
|
||||||
|
- Usage examples in metadata
|
||||||
|
- Hover + focus states
|
||||||
|
- Polished ASCII
|
||||||
|
- Props.examples array
|
||||||
|
|
||||||
|
### production (Final Polish)
|
||||||
|
- Complete metadata with accessibility details
|
||||||
|
- Publication-quality ASCII
|
||||||
|
- All relevant states (hover, focus, disabled, active, error)
|
||||||
|
- Full documentation
|
||||||
|
- Enhancement time: ~180 seconds
|
||||||
|
|
||||||
|
**Improvements over detailed:**
|
||||||
|
- All applicable states
|
||||||
|
- Complete accessibility metadata
|
||||||
|
- Keyboard shortcuts
|
||||||
|
- Publication-quality ASCII
|
||||||
|
- Edge case documentation
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
Help users enhance components from current fidelity to target fidelity.
|
||||||
|
|
||||||
|
### Workflow
|
||||||
|
|
||||||
|
#### Step 1: Inventory Components
|
||||||
|
|
||||||
|
Check what components exist and their current fidelity:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls ./fluxwing/components/*.uxm
|
||||||
|
```
|
||||||
|
|
||||||
|
For each component, read fidelity level:
|
||||||
|
```bash
|
||||||
|
python3 -c "import json; print(json.load(open('./fluxwing/components/button.uxm'))['metadata'].get('fidelity', 'detailed'))"
|
||||||
|
```
|
||||||
|
|
||||||
|
List components by fidelity:
|
||||||
|
- sketch: [component-ids]
|
||||||
|
- basic: [component-ids]
|
||||||
|
- detailed: [component-ids]
|
||||||
|
- production: [component-ids]
|
||||||
|
|
||||||
|
#### Step 2: Determine Target Fidelity
|
||||||
|
|
||||||
|
Ask user or use defaults:
|
||||||
|
|
||||||
|
**User specifies:**
|
||||||
|
- "Enhance to basic"
|
||||||
|
- "Upgrade to detailed"
|
||||||
|
- "Make production-ready"
|
||||||
|
|
||||||
|
**Auto-select (if not specified):**
|
||||||
|
- sketch → detailed (most common, best balance)
|
||||||
|
- basic → detailed
|
||||||
|
- detailed → production
|
||||||
|
|
||||||
|
#### Step 3: Spawn Enhancement Agents
|
||||||
|
|
||||||
|
**If enhancing multiple components (3+):**
|
||||||
|
- Spawn agents in parallel (one per component)
|
||||||
|
- Each agent enhances independently
|
||||||
|
- Wait for all to complete
|
||||||
|
|
||||||
|
**If enhancing single component:**
|
||||||
|
- Spawn single agent
|
||||||
|
- Focus on quality
|
||||||
|
|
||||||
|
**Enhancement Agent Prompt:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
model: "sonnet", // Quality matters for enhancement
|
||||||
|
description: "Enhance ${componentId} to ${targetFidelity} (copy)",
|
||||||
|
prompt: `Enhance uxscii component from ${currentFidelity} to ${targetFidelity} using copy-on-update pattern.
|
||||||
|
|
||||||
|
Component: ${componentId}
|
||||||
|
Current fidelity: ${currentFidelity}
|
||||||
|
Target fidelity: ${targetFidelity}
|
||||||
|
|
||||||
|
COPY-ON-UPDATE MODE:
|
||||||
|
- Read existing component
|
||||||
|
- Find highest version (check ${componentId}-v2, -v3, etc.)
|
||||||
|
- Create NEW versioned copy: ${componentId}-v{N+1}
|
||||||
|
- DO NOT overwrite original
|
||||||
|
- Preserve metadata.created, update metadata.modified
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Read ./fluxwing/components/${componentId}.uxm
|
||||||
|
2. Read ./fluxwing/components/${componentId}.md (if exists)
|
||||||
|
3. Load copy-versioning docs: {SKILL_ROOT}/../shared/docs/copy-versioning.md
|
||||||
|
4. Find highest version of ${componentId} (original is v1)
|
||||||
|
5. Calculate next version number: v{N+1}
|
||||||
|
6. Load enhancement patterns: {SKILL_ROOT}/docs/enhancement-patterns.md
|
||||||
|
7. Load state templates: {SKILL_ROOT}/../fluxwing-component-creator/templates/state-additions/
|
||||||
|
|
||||||
|
8. Create versioned copy with enhancements:
|
||||||
|
|
||||||
|
**Update version metadata:**
|
||||||
|
- id: "${componentId}-v{N+1}" (add -v{N+1} suffix)
|
||||||
|
- version: Increment minor (e.g., 1.0.0 → 1.1.0)
|
||||||
|
- metadata.created: PRESERVE from source
|
||||||
|
- metadata.modified: SET to current timestamp
|
||||||
|
- metadata.fidelity: UPDATE to "${targetFidelity}"
|
||||||
|
|
||||||
|
${targetFidelity === 'basic' ? `
|
||||||
|
For "basic" fidelity:
|
||||||
|
- Improve metadata.description (1-2 sentences, specific)
|
||||||
|
- Add specific tags (not just generic type)
|
||||||
|
- Create/improve .md with clean ASCII art
|
||||||
|
- Keep single default state
|
||||||
|
- Update fidelity field to "basic"
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${targetFidelity === 'detailed' ? `
|
||||||
|
For "detailed" fidelity:
|
||||||
|
- All "basic" enhancements (if not already done)
|
||||||
|
- Add hover state (use {SKILL_ROOT}/../fluxwing-component-creator/templates/state-additions/hover.json)
|
||||||
|
- Add focus state (use {SKILL_ROOT}/../fluxwing-component-creator/templates/state-additions/focus.json)
|
||||||
|
- Polish ASCII art (smooth alignment, consistent spacing)
|
||||||
|
- Add props.examples array (2-3 usage examples)
|
||||||
|
- Update fidelity field to "detailed"
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${targetFidelity === 'production' ? `
|
||||||
|
For "production" fidelity:
|
||||||
|
- All "detailed" enhancements (if not already done)
|
||||||
|
- Add disabled state (if applicable for component type)
|
||||||
|
- Add error state (if applicable for component type)
|
||||||
|
- Complete accessibility metadata:
|
||||||
|
- aria-label templates
|
||||||
|
- keyboard shortcuts
|
||||||
|
- screen reader descriptions
|
||||||
|
- Add behavior.keyboardShortcuts (if interactive)
|
||||||
|
- Publication-quality ASCII (pixel-perfect alignment)
|
||||||
|
- Document edge cases in description
|
||||||
|
- Update fidelity field to "production"
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
9. Save NEW versioned files:
|
||||||
|
- ./fluxwing/components/${componentId}-v{N+1}.uxm
|
||||||
|
- ./fluxwing/components/${componentId}-v{N+1}.md
|
||||||
|
- DO NOT overwrite original ${componentId}.uxm
|
||||||
|
|
||||||
|
10. Verify JSON is valid
|
||||||
|
11. Return enhancement summary with version info
|
||||||
|
|
||||||
|
Target time: ${targetTime[targetFidelity]} seconds
|
||||||
|
|
||||||
|
Return format:
|
||||||
|
"Enhanced ${componentId} → ${componentId}-v{N+1}: ${currentFidelity} → ${targetFidelity}
|
||||||
|
- Version: {old version} → {new version}
|
||||||
|
- Added X states
|
||||||
|
- Improved metadata
|
||||||
|
- Polished ASCII
|
||||||
|
- Original preserved at ${componentId}.uxm"
|
||||||
|
`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 4: Regenerate Screen (If Applicable)
|
||||||
|
|
||||||
|
If components belong to a screen, regenerate .rendered.md:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find which screen uses this component
|
||||||
|
grep -l "${componentId}" ./fluxwing/screens/*.uxm
|
||||||
|
```
|
||||||
|
|
||||||
|
If found, respawn composer to regenerate .rendered.md with enhanced components.
|
||||||
|
|
||||||
|
#### Step 5: Report Results
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Enhancement Complete ✓
|
||||||
|
|
||||||
|
## Components Enhanced
|
||||||
|
|
||||||
|
${enhancedComponents.map(c => `
|
||||||
|
### ${c.id}
|
||||||
|
- Before: ${c.oldFidelity}
|
||||||
|
- After: ${c.newFidelity}
|
||||||
|
- Time: ${c.time}s
|
||||||
|
- Changes:
|
||||||
|
- ${c.changes.join('\n - ')}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Total Time: ${totalTime}s
|
||||||
|
|
||||||
|
## Screens Updated
|
||||||
|
${updatedScreens.length > 0 ? updatedScreens.map(s => `- ${s}.rendered.md regenerated`).join('\n') : 'None'}
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
1. Review enhanced components: \`ls ./fluxwing/components/\`
|
||||||
|
2. View updated screen: \`cat ./fluxwing/screens/${screenName}.rendered.md\`
|
||||||
|
3. Customize as needed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
**User:** "Enhance all components in banking-chat to detailed"
|
||||||
|
|
||||||
|
**Enhancer:**
|
||||||
|
1. Finds 6 sketch components
|
||||||
|
2. Spawns 6 enhancement agents (parallel)
|
||||||
|
3. Each agent: sketch → detailed (~90s)
|
||||||
|
4. Regenerates banking-chat.rendered.md
|
||||||
|
5. Total time: ~120 seconds
|
||||||
|
|
||||||
|
**User:** "Make submit-button production-ready"
|
||||||
|
|
||||||
|
**Enhancer:**
|
||||||
|
1. Finds submit-button (detailed fidelity)
|
||||||
|
2. Spawns 1 enhancement agent
|
||||||
|
3. Agent: detailed → production (~180s)
|
||||||
|
4. Updates screen if applicable
|
||||||
|
5. Total time: ~180 seconds
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- ✓ Components upgraded to target fidelity
|
||||||
|
- ✓ All states added as specified
|
||||||
|
- ✓ ASCII art improved
|
||||||
|
- ✓ Metadata enriched
|
||||||
|
- ✓ Screen .rendered.md regenerated (if applicable)
|
||||||
|
- ✓ All files validate against schema
|
||||||
|
|
||||||
|
You are building a progressive enhancement system for maximum flexibility!
|
||||||
355
skills/fluxwing-enhancer/docs/enhancement-patterns.md
Normal file
355
skills/fluxwing-enhancer/docs/enhancement-patterns.md
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
# Enhancement Patterns
|
||||||
|
|
||||||
|
How to enhance components from sketch to production fidelity.
|
||||||
|
|
||||||
|
## Fidelity Progression Matrix
|
||||||
|
|
||||||
|
| Aspect | sketch | basic | detailed | production |
|
||||||
|
|--------|--------|-------|----------|------------|
|
||||||
|
| **Description** | Generic | Specific (1-2 sent) | Detailed (3-4 sent) | Complete with edge cases |
|
||||||
|
| **Tags** | Type only | Type + context | Type + context + features | All applicable tags |
|
||||||
|
| **ASCII Art** | Missing or minimal | Clean and aligned | Polished with spacing | Pixel-perfect, publication-quality |
|
||||||
|
| **States** | default only | default only | default + hover + focus | All applicable states |
|
||||||
|
| **Props** | Minimal | Standard | + examples array | + validation rules |
|
||||||
|
| **Accessibility** | Basic role | + focusable | + aria-label | + keyboard shortcuts + full a11y |
|
||||||
|
| **Time** | 10s | 30s | 90s | 180s |
|
||||||
|
|
||||||
|
## Enhancement Checklist by Fidelity
|
||||||
|
|
||||||
|
### sketch → basic (30s)
|
||||||
|
|
||||||
|
**Metadata:**
|
||||||
|
- [ ] Improve description from generic to specific
|
||||||
|
- [ ] Add relevant tags beyond just type
|
||||||
|
- [ ] Keep created/modified timestamps
|
||||||
|
|
||||||
|
**ASCII:**
|
||||||
|
- [ ] Create .md if missing
|
||||||
|
- [ ] Add clean ASCII art using box-drawing characters
|
||||||
|
- [ ] Match dimensions from .uxm (ascii.width, ascii.height)
|
||||||
|
- [ ] Basic alignment (don't need perfect)
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
- [ ] Document all variables in .md
|
||||||
|
- [ ] No need to add examples yet
|
||||||
|
|
||||||
|
**States:**
|
||||||
|
- [ ] Keep default state only
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
Before (sketch):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"description": "Button component",
|
||||||
|
"tags": ["button"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After (basic):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"description": "Submit button for form submission with primary action styling",
|
||||||
|
"tags": ["button", "submit", "form", "primary-action"],
|
||||||
|
"fidelity": "basic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### basic → detailed (90s)
|
||||||
|
|
||||||
|
**Metadata:**
|
||||||
|
- [ ] Expand description with usage context
|
||||||
|
- [ ] Add props.examples array (2-3 examples)
|
||||||
|
|
||||||
|
**ASCII:**
|
||||||
|
- [ ] Polish alignment (consistent spacing)
|
||||||
|
- [ ] Smooth corners and borders
|
||||||
|
- [ ] Add visual details
|
||||||
|
|
||||||
|
**States:**
|
||||||
|
- [ ] Add hover state (use hover.json template)
|
||||||
|
- [ ] Add focus state (use focus.json template)
|
||||||
|
- [ ] Document state behavior in .md
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
- [ ] Add examples showing different prop combinations
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
Before (basic):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"props": {
|
||||||
|
"label": "Submit"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After (detailed):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"props": {
|
||||||
|
"label": "Submit",
|
||||||
|
"examples": [
|
||||||
|
{"label": "Submit", "context": "Form submission"},
|
||||||
|
{"label": "Sign In", "context": "Login form"},
|
||||||
|
{"label": "Continue", "context": "Multi-step process"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}},
|
||||||
|
{"name": "hover", "properties": {
|
||||||
|
"backgroundColor": "#e0e0e0",
|
||||||
|
"cursor": "pointer"
|
||||||
|
}},
|
||||||
|
{"name": "focus", "properties": {
|
||||||
|
"outline": "2px solid #0066cc",
|
||||||
|
"backgroundColor": "#f0f8ff"
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"fidelity": "detailed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### detailed → production (180s)
|
||||||
|
|
||||||
|
**Metadata:**
|
||||||
|
- [ ] Add edge case documentation
|
||||||
|
- [ ] Complete all optional fields
|
||||||
|
- [ ] Add keywords for searchability
|
||||||
|
|
||||||
|
**ASCII:**
|
||||||
|
- [ ] Pixel-perfect alignment
|
||||||
|
- [ ] Publication-quality rendering
|
||||||
|
- [ ] Consider printing/export appearance
|
||||||
|
|
||||||
|
**States:**
|
||||||
|
- [ ] Add disabled state (if applicable)
|
||||||
|
- [ ] Add error state (if applicable for forms)
|
||||||
|
- [ ] Add active/pressed state (for buttons)
|
||||||
|
- [ ] Add loading state (if async action)
|
||||||
|
|
||||||
|
**Accessibility:**
|
||||||
|
- [ ] Add keyboard shortcuts
|
||||||
|
- [ ] Add screen reader descriptions
|
||||||
|
- [ ] Add ARIA label templates
|
||||||
|
- [ ] Document keyboard navigation
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
- [ ] Add validation rules
|
||||||
|
- [ ] Document prop constraints
|
||||||
|
- [ ] Add deprecation notices if needed
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
Before (detailed):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}},
|
||||||
|
{"name": "hover", "properties": {...}},
|
||||||
|
{"name": "focus", "properties": {...}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"focusable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After (production):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "default", "properties": {}},
|
||||||
|
{"name": "hover", "properties": {...}},
|
||||||
|
{"name": "focus", "properties": {...}},
|
||||||
|
{"name": "disabled", "properties": {
|
||||||
|
"opacity": 0.5,
|
||||||
|
"cursor": "not-allowed",
|
||||||
|
"interactive": false
|
||||||
|
}},
|
||||||
|
{"name": "active", "properties": {
|
||||||
|
"backgroundColor": "#c0c0c0",
|
||||||
|
"transform": "translateY(1px)"
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"focusable": true,
|
||||||
|
"ariaLabel": "{{label}} - {{context}}",
|
||||||
|
"keyboardShortcuts": [
|
||||||
|
{"key": "Enter", "action": "activate"},
|
||||||
|
{"key": "Space", "action": "activate"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"fidelity": "production",
|
||||||
|
"description": "Submit button for form submission with primary action styling. Supports keyboard navigation (Enter/Space), shows visual feedback on hover and focus, and provides disabled state for form validation. Use for primary actions like 'Submit', 'Sign In', 'Continue'."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## State Addition Guide
|
||||||
|
|
||||||
|
### When to Add Each State
|
||||||
|
|
||||||
|
**hover:**
|
||||||
|
- All interactive components (button, input, link)
|
||||||
|
- Cards if clickable
|
||||||
|
- Navigation items
|
||||||
|
|
||||||
|
**focus:**
|
||||||
|
- All focusable components
|
||||||
|
- Required for keyboard navigation
|
||||||
|
- WCAG AA compliance
|
||||||
|
|
||||||
|
**disabled:**
|
||||||
|
- Form controls (button, input, checkbox, select)
|
||||||
|
- Actions that may be unavailable
|
||||||
|
- Not needed for display-only components
|
||||||
|
|
||||||
|
**error:**
|
||||||
|
- Form inputs (text, email, password)
|
||||||
|
- Validation feedback required
|
||||||
|
- Not needed for buttons or display components
|
||||||
|
|
||||||
|
**active/pressed:**
|
||||||
|
- Buttons
|
||||||
|
- Links
|
||||||
|
- Interactive cards
|
||||||
|
- Shows immediate feedback
|
||||||
|
|
||||||
|
**loading:**
|
||||||
|
- Buttons triggering async actions
|
||||||
|
- Forms submitting data
|
||||||
|
- Components fetching data
|
||||||
|
|
||||||
|
## ASCII Art Enhancement
|
||||||
|
|
||||||
|
### sketch → basic
|
||||||
|
|
||||||
|
Focus: Functionality over beauty
|
||||||
|
- Use basic box-drawing characters (─│┌┐└┘)
|
||||||
|
- Align text roughly
|
||||||
|
- Match dimensions
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
┌──────────────────┐
|
||||||
|
│ Submit │
|
||||||
|
└──────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### basic → detailed
|
||||||
|
|
||||||
|
Focus: Polish and consistency
|
||||||
|
- Use rounded corners (╭╮╰╯) for friendly feel
|
||||||
|
- Careful spacing (center-align text)
|
||||||
|
- Smooth visual flow
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ Submit │
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### detailed → production
|
||||||
|
|
||||||
|
Focus: Pixel-perfect publication quality
|
||||||
|
- Perfect alignment (character-level precision)
|
||||||
|
- Consider all states (how hover/focus changes appearance)
|
||||||
|
- Professional appearance for screenshots
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ Submit │
|
||||||
|
╰──────────────────╯
|
||||||
|
|
||||||
|
Hover:
|
||||||
|
╔══════════════════╗
|
||||||
|
║ Submit ║
|
||||||
|
╚══════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Button Enhancement
|
||||||
|
|
||||||
|
sketch → basic:
|
||||||
|
- Add clean box ASCII
|
||||||
|
- Specific description
|
||||||
|
|
||||||
|
basic → detailed:
|
||||||
|
- Add hover + focus states
|
||||||
|
- Polish ASCII with rounded corners
|
||||||
|
- Add usage examples
|
||||||
|
|
||||||
|
detailed → production:
|
||||||
|
- Add disabled + active states
|
||||||
|
- Add keyboard shortcuts
|
||||||
|
- Pixel-perfect ASCII
|
||||||
|
|
||||||
|
### Input Enhancement
|
||||||
|
|
||||||
|
sketch → basic:
|
||||||
|
- Add input box ASCII with placeholder
|
||||||
|
- Specific description
|
||||||
|
|
||||||
|
basic → detailed:
|
||||||
|
- Add focus state
|
||||||
|
- Add label positioning
|
||||||
|
- Add usage examples
|
||||||
|
|
||||||
|
detailed → production:
|
||||||
|
- Add error state
|
||||||
|
- Add disabled state
|
||||||
|
- Add validation rules
|
||||||
|
- Full accessibility
|
||||||
|
|
||||||
|
### Card Enhancement
|
||||||
|
|
||||||
|
sketch → basic:
|
||||||
|
- Add card box with title/content areas
|
||||||
|
- Specific description
|
||||||
|
|
||||||
|
basic → detailed:
|
||||||
|
- Polish layout and spacing
|
||||||
|
- Add examples (different content)
|
||||||
|
|
||||||
|
detailed → production:
|
||||||
|
- Add hover state (if interactive)
|
||||||
|
- Perfect alignment
|
||||||
|
- Complete metadata
|
||||||
|
|
||||||
|
## Performance Targets
|
||||||
|
|
||||||
|
| Enhancement | Components | Time Target |
|
||||||
|
|-------------|-----------|-------------|
|
||||||
|
| 1 component to basic | 1 | 30s |
|
||||||
|
| 1 component to detailed | 1 | 90s |
|
||||||
|
| 1 component to production | 1 | 180s |
|
||||||
|
| 6 components to basic (parallel) | 6 | 30s |
|
||||||
|
| 6 components to detailed (parallel) | 6 | 90s |
|
||||||
|
| 6 components to production (parallel) | 6 | 180s |
|
||||||
|
|
||||||
|
Parallel execution: Multiple agents working simultaneously, so total time ≈ single component time!
|
||||||
347
skills/fluxwing-library-browser/SKILL.md
Normal file
347
skills/fluxwing-library-browser/SKILL.md
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
---
|
||||||
|
name: Fluxwing Library Browser
|
||||||
|
description: Browse and view all available uxscii components including bundled templates, user components, and screens. Use when working with .uxm files, when user wants to see, list, browse, or search .uxm components or screens.
|
||||||
|
version: 0.0.1
|
||||||
|
author: Trabian
|
||||||
|
allowed-tools: Read, Glob, Grep
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fluxwing Library Browser
|
||||||
|
|
||||||
|
Browse all available uxscii components: bundled templates, user-created components, and complete screens.
|
||||||
|
|
||||||
|
## Data Location Rules
|
||||||
|
|
||||||
|
**READ from (bundled templates - reference only):**
|
||||||
|
- `{SKILL_ROOT}/../uxscii-component-creator/templates/` - 11 component templates
|
||||||
|
- `{SKILL_ROOT}/../uxscii-screen-scaffolder/templates/` - 2 screen examples (if available)
|
||||||
|
- `{SKILL_ROOT}/docs/` - Documentation
|
||||||
|
|
||||||
|
**READ from (project workspace):**
|
||||||
|
- `./fluxwing/components/` - Your created components
|
||||||
|
- `./fluxwing/screens/` - Your created screens
|
||||||
|
- `./fluxwing/library/` - Customized template copies
|
||||||
|
|
||||||
|
**NEVER write to skill directories - they are read-only!**
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
Show the user what uxscii components are available across **four sources**:
|
||||||
|
1. **Bundled Templates** - 11 curated examples from skill templates (read-only reference)
|
||||||
|
2. **Project Components** - User/agent-created reusable components in `./fluxwing/components/` (editable)
|
||||||
|
3. **Project Library** - Customized template copies in `./fluxwing/library/` (editable)
|
||||||
|
4. **Project Screens** - Complete screen compositions in `./fluxwing/screens/` (editable)
|
||||||
|
|
||||||
|
**Key Distinction**: Bundled templates are READ-ONLY reference materials. To customize them, copy to your project workspace first.
|
||||||
|
|
||||||
|
## Fast Browsing with Pre-Built Index
|
||||||
|
|
||||||
|
**IMPORTANT**: Use the pre-built template index for instant browsing (10x faster than globbing):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Load the pre-built index (1 file read = instant results!)
|
||||||
|
const index = JSON.parse(read('{SKILL_ROOT}/data/template-index.json'));
|
||||||
|
|
||||||
|
// Browse by type
|
||||||
|
const buttons = index.by_type.button; // ["primary-button", "secondary-button"]
|
||||||
|
const inputs = index.by_type.input; // ["email-input"]
|
||||||
|
|
||||||
|
// Search by tag
|
||||||
|
const formComponents = index.by_tag.form; // All form-related components
|
||||||
|
const interactiveComponents = index.by_tag.interactive; // All interactive components
|
||||||
|
|
||||||
|
// Get component info instantly (no file reads needed!)
|
||||||
|
const buttonInfo = index.bundled_templates.find(t => t.id === "primary-button");
|
||||||
|
console.log(buttonInfo.name); // "Primary Button"
|
||||||
|
console.log(buttonInfo.description); // Full description
|
||||||
|
console.log(buttonInfo.preview); // ASCII preview already extracted!
|
||||||
|
console.log(buttonInfo.states); // ["default", "hover", "active", "disabled"]
|
||||||
|
console.log(buttonInfo.props); // ["text", "variant", "size"]
|
||||||
|
console.log(buttonInfo.tags); // ["button", "primary", "action", "interactive"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Performance Benefits:**
|
||||||
|
- ✅ **1 file read** vs **11+ file reads** (10x faster!)
|
||||||
|
- ✅ **Instant type/tag filtering** (no parsing needed)
|
||||||
|
- ✅ **Pre-extracted ASCII previews** (show immediately)
|
||||||
|
- ✅ **Metadata summary** (no JSON parsing per component)
|
||||||
|
|
||||||
|
**Index Structure:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"generated": "2025-10-18T12:00:00Z",
|
||||||
|
"template_count": 11,
|
||||||
|
"bundled_templates": [ /* array of component metadata */ ],
|
||||||
|
"by_type": { /* components grouped by type */ },
|
||||||
|
"by_tag": { /* components grouped by tags */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use full file reads:**
|
||||||
|
- User requests detailed view of a specific component
|
||||||
|
- User wants to copy a template (need full .uxm and .md content)
|
||||||
|
- User searches for a very specific property not in the index
|
||||||
|
|
||||||
|
## Display Format
|
||||||
|
|
||||||
|
Present in a clear, hierarchical structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
🎁 BUNDLED TEMPLATES
|
||||||
|
📁 Component Creator Templates
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
These are starter templates you can copy and customize.
|
||||||
|
|
||||||
|
Buttons (2 variants)
|
||||||
|
├─ primary-button.uxm
|
||||||
|
│ └─ Standard clickable button with hover, focus, and disabled states
|
||||||
|
│ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
│ ▓ Click Me ▓
|
||||||
|
│ ▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
│
|
||||||
|
└─ icon-button.uxm
|
||||||
|
└─ Button with icon support for visual emphasis
|
||||||
|
[🔍 Search]
|
||||||
|
|
||||||
|
Inputs (2 variants)
|
||||||
|
├─ text-input.uxm
|
||||||
|
│ └─ Basic text input with validation states
|
||||||
|
│ [________________]
|
||||||
|
│
|
||||||
|
└─ email-input.uxm
|
||||||
|
└─ Email-specific input with format validation
|
||||||
|
[user@example.com ]
|
||||||
|
|
||||||
|
Cards (1 variant)
|
||||||
|
└─ card.uxm
|
||||||
|
└─ Container for grouping related content
|
||||||
|
╭─────────────╮
|
||||||
|
│ Card Title │
|
||||||
|
├─────────────┤
|
||||||
|
│ Content... │
|
||||||
|
╰─────────────╯
|
||||||
|
|
||||||
|
Modals (1 variant)
|
||||||
|
└─ modal.uxm
|
||||||
|
└─ Overlay dialog for focused interactions
|
||||||
|
╔═══════════════╗
|
||||||
|
║ Modal Title ║
|
||||||
|
╠═══════════════╣
|
||||||
|
║ Content... ║
|
||||||
|
╚═══════════════╝
|
||||||
|
|
||||||
|
Navigation (1 variant)
|
||||||
|
└─ navigation.uxm
|
||||||
|
└─ Primary navigation menu
|
||||||
|
• Home • About • Contact
|
||||||
|
|
||||||
|
Feedback (2 variants)
|
||||||
|
├─ alert.uxm
|
||||||
|
│ └─ User notification with severity levels
|
||||||
|
│ ⚠️ Warning: Action required
|
||||||
|
│
|
||||||
|
└─ badge.uxm
|
||||||
|
└─ Small status indicator or label
|
||||||
|
● New
|
||||||
|
|
||||||
|
Lists (1 variant)
|
||||||
|
└─ list.uxm
|
||||||
|
└─ Vertical list for displaying data
|
||||||
|
• Item 1
|
||||||
|
• Item 2
|
||||||
|
• Item 3
|
||||||
|
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
🎨 YOUR COMPONENTS
|
||||||
|
📁 ./fluxwing/components/
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
Components you've created for your project.
|
||||||
|
|
||||||
|
✓ submit-button.uxm
|
||||||
|
└─ Custom submit button for forms
|
||||||
|
Modified: 2024-10-11 14:23:00
|
||||||
|
[ Submit Form ]
|
||||||
|
|
||||||
|
✓ password-input.uxm
|
||||||
|
└─ Password input with show/hide toggle
|
||||||
|
Modified: 2024-10-11 14:25:00
|
||||||
|
[••••••••] 👁️
|
||||||
|
|
||||||
|
✓ user-card.uxm
|
||||||
|
└─ Card displaying user profile information
|
||||||
|
Modified: 2024-10-11 15:10:00
|
||||||
|
╭──────────────────╮
|
||||||
|
│ John Doe │
|
||||||
|
│ @johndoe │
|
||||||
|
╰──────────────────╯
|
||||||
|
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
🖥️ YOUR SCREENS
|
||||||
|
📁 ./fluxwing/screens/
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
Complete screen compositions.
|
||||||
|
|
||||||
|
✓ login-screen.uxm
|
||||||
|
└─ User authentication screen
|
||||||
|
Components used: email-input, password-input, submit-button, error-alert
|
||||||
|
Modified: 2024-10-11 15:45:00
|
||||||
|
|
||||||
|
✓ dashboard.uxm
|
||||||
|
└─ Main application dashboard
|
||||||
|
Components used: navigation, metric-card, data-table, sidebar
|
||||||
|
Modified: 2024-10-11 16:20:00
|
||||||
|
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
Total: 10 templates, 3 components, 2 screens
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interactive Options
|
||||||
|
|
||||||
|
After displaying the library, offer these actions:
|
||||||
|
|
||||||
|
```
|
||||||
|
What would you like to do?
|
||||||
|
|
||||||
|
1️⃣ View component details (ask me to "show me [name]")
|
||||||
|
2️⃣ Copy a template to your project
|
||||||
|
3️⃣ Create a new component (ask me to "create a [type]")
|
||||||
|
4️⃣ Scaffold a new screen (ask me to "build a [screen type] screen")
|
||||||
|
5️⃣ Search for a specific pattern (e.g., "button", "input", "card")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detailed View
|
||||||
|
|
||||||
|
If user wants to see details of a specific component:
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Show me primary-button
|
||||||
|
|
||||||
|
You: 📄 PRIMARY-BUTTON.UXM
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
ID: primary-button
|
||||||
|
Type: button
|
||||||
|
Version: 1.0.0
|
||||||
|
Description: Standard clickable button with hover, focus, and disabled states
|
||||||
|
|
||||||
|
Props:
|
||||||
|
- text: "Click me"
|
||||||
|
- variant: "primary"
|
||||||
|
- disabled: false
|
||||||
|
|
||||||
|
States:
|
||||||
|
- default (solid border, white background)
|
||||||
|
- hover (highlighted background)
|
||||||
|
- focus (outline indicator)
|
||||||
|
- disabled (grayed out)
|
||||||
|
|
||||||
|
Accessibility:
|
||||||
|
- Role: button
|
||||||
|
- Focusable: true
|
||||||
|
- Keyboard: Space, Enter
|
||||||
|
|
||||||
|
ASCII Preview:
|
||||||
|
|
||||||
|
Default State:
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
▓ Click Me ▓
|
||||||
|
▓▓▓▓▓▓▓▓▓▓▓▓
|
||||||
|
|
||||||
|
Hover State:
|
||||||
|
█████████████
|
||||||
|
█ Click Me █
|
||||||
|
█████████████
|
||||||
|
|
||||||
|
Location: {SKILL_ROOT}/../uxscii-component-creator/templates/primary-button.uxm
|
||||||
|
|
||||||
|
To customize: Copy to ./fluxwing/library/ for editing
|
||||||
|
```
|
||||||
|
|
||||||
|
## Copy Template to Project
|
||||||
|
|
||||||
|
If user wants to customize a bundled template:
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Copy primary-button to my project
|
||||||
|
|
||||||
|
You: I'll copy primary-button to your library for customization.
|
||||||
|
|
||||||
|
[Copies .uxm and .md files]
|
||||||
|
|
||||||
|
✓ Copied to ./fluxwing/library/
|
||||||
|
- primary-button.uxm
|
||||||
|
- primary-button.md
|
||||||
|
|
||||||
|
You can now edit these files safely. Changes won't affect the original template.
|
||||||
|
|
||||||
|
Next steps:
|
||||||
|
- Edit: Modify ./fluxwing/library/primary-button.uxm
|
||||||
|
- Expand: Ask me to "add hover state to primary-button"
|
||||||
|
- Use: Reference it in screens or other components
|
||||||
|
```
|
||||||
|
|
||||||
|
## Search Functionality
|
||||||
|
|
||||||
|
Support component search:
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Find all button components
|
||||||
|
|
||||||
|
You: Found 3 button components:
|
||||||
|
|
||||||
|
🎁 Bundled Templates:
|
||||||
|
- primary-button.uxm (standard clickable button)
|
||||||
|
- icon-button.uxm (button with icon support)
|
||||||
|
|
||||||
|
🎨 Your Components:
|
||||||
|
- submit-button.uxm (custom submit button for forms)
|
||||||
|
|
||||||
|
Would you like details on any of these?
|
||||||
|
```
|
||||||
|
|
||||||
|
## Empty Library Handling
|
||||||
|
|
||||||
|
If user has no components yet:
|
||||||
|
|
||||||
|
```
|
||||||
|
🎁 BUNDLED TEMPLATES
|
||||||
|
📁 Component Creator Templates
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
11 starter templates available
|
||||||
|
|
||||||
|
🎨 YOUR COMPONENTS
|
||||||
|
📁 ./fluxwing/components/
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
No components yet. Create your first component!
|
||||||
|
|
||||||
|
Try: "Create a submit button" or "Create an email input"
|
||||||
|
|
||||||
|
🖥️ YOUR SCREENS
|
||||||
|
📁 ./fluxwing/screens/
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
No screens yet. Scaffold your first screen!
|
||||||
|
|
||||||
|
Try: "Build a login screen" or "Create a dashboard"
|
||||||
|
|
||||||
|
─────────────────────────────────────────────────────
|
||||||
|
Total: 11 templates, 0 components, 0 screens
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- **Examples Guide**: See `{SKILL_ROOT}/docs/07-examples-guide.md` for detailed template documentation
|
||||||
|
- **Component Creator**: Use when you want to create new components
|
||||||
|
- **Screen Scaffolder**: Use when you want to build complete screens
|
||||||
|
- **Component Viewer**: Use for detailed component information
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
1. **Read-only templates**: Bundled templates cannot be modified directly
|
||||||
|
2. **Copy before customize**: Copy templates to `./fluxwing/library/` to customize
|
||||||
|
3. **Search**: Use Glob and Grep to find components by name or pattern
|
||||||
|
4. **Organization**: Keep components in `./fluxwing/components/`, customized templates in `./fluxwing/library/`
|
||||||
|
5. **Screens**: Screen files include `.uxm`, `.md`, and `.rendered.md` (three files)
|
||||||
|
|
||||||
|
You're helping users discover and navigate their uxscii component library!
|
||||||
509
skills/fluxwing-library-browser/data/template-index.json
Normal file
509
skills/fluxwing-library-browser/data/template-index.json
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"generated": "2025-10-18T13:02:14.626427Z",
|
||||||
|
"template_count": 11,
|
||||||
|
"bundled_templates": [
|
||||||
|
{
|
||||||
|
"id": "alert",
|
||||||
|
"type": "alert",
|
||||||
|
"name": "Alert/Notification Component",
|
||||||
|
"description": "Alert and notification messages for user feedback, system status, and important information display",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/alert.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/alert.md",
|
||||||
|
"states": [
|
||||||
|
"visible",
|
||||||
|
"entering",
|
||||||
|
"exiting",
|
||||||
|
"hidden"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"title",
|
||||||
|
"message",
|
||||||
|
"variant",
|
||||||
|
"icon",
|
||||||
|
"dismissible",
|
||||||
|
"persistent",
|
||||||
|
"position",
|
||||||
|
"duration",
|
||||||
|
"actions",
|
||||||
|
"showProgress",
|
||||||
|
"bordered",
|
||||||
|
"compact"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"message",
|
||||||
|
"notification",
|
||||||
|
"alert",
|
||||||
|
"feedback"
|
||||||
|
],
|
||||||
|
"preview": "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 \u2713 {{title}} \u2502\u2715\u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u2502\n\u2502 {{message}} \u2502",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "badge",
|
||||||
|
"type": "badge",
|
||||||
|
"name": "Badge/Tag Component",
|
||||||
|
"description": "Status indicators, labels, and informational tags with various styles and interactive capabilities",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/badge.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/badge.md",
|
||||||
|
"states": [
|
||||||
|
"default",
|
||||||
|
"hover",
|
||||||
|
"active",
|
||||||
|
"disabled"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"text",
|
||||||
|
"variant",
|
||||||
|
"size",
|
||||||
|
"removable",
|
||||||
|
"clickable",
|
||||||
|
"icon",
|
||||||
|
"color",
|
||||||
|
"outlined",
|
||||||
|
"pill",
|
||||||
|
"count",
|
||||||
|
"maxCount",
|
||||||
|
"dot"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"badge",
|
||||||
|
"tag",
|
||||||
|
"label",
|
||||||
|
"indicator",
|
||||||
|
"status"
|
||||||
|
],
|
||||||
|
"preview": "\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593 \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\n\u2593 {{text}} \u2593 \u2591 Secondary \u2591 \u2593 Primary \u2593 \u2588 Success \u2588\n\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593 \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "card",
|
||||||
|
"type": "card",
|
||||||
|
"name": "Card Component",
|
||||||
|
"description": "A flexible container for grouping related content with optional header, body, and footer sections",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/card.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/card.md",
|
||||||
|
"states": [
|
||||||
|
"default",
|
||||||
|
"hover",
|
||||||
|
"focus"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"title",
|
||||||
|
"subtitle",
|
||||||
|
"content",
|
||||||
|
"footer",
|
||||||
|
"elevated",
|
||||||
|
"padding",
|
||||||
|
"borderRadius",
|
||||||
|
"maxWidth",
|
||||||
|
"hasHeader",
|
||||||
|
"hasFooter"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"layout",
|
||||||
|
"container",
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"preview": "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 {{title}} \u2502\n\u2502 {{subtitle}} \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u2502",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom-widget",
|
||||||
|
"type": "custom",
|
||||||
|
"name": "Custom Widget",
|
||||||
|
"description": "A specialized widget component that extends the basic card with custom properties",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/custom-widget.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/custom-widget.md",
|
||||||
|
"states": [
|
||||||
|
"loading",
|
||||||
|
"error"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"widgetType",
|
||||||
|
"data",
|
||||||
|
"refreshable",
|
||||||
|
"autoRefresh",
|
||||||
|
"compact"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"custom",
|
||||||
|
"widget",
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"preview": "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 {{data.icon}} {{data.title}} \u2502\u27f3\u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u2502\n\u2502 {{data.value}} \u2502",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "email-input",
|
||||||
|
"type": "input",
|
||||||
|
"name": "Email Input Field",
|
||||||
|
"description": "A specialized input field for email addresses with built-in validation",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/email-input.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/email-input.md",
|
||||||
|
"states": [
|
||||||
|
"default",
|
||||||
|
"focus",
|
||||||
|
"valid",
|
||||||
|
"error",
|
||||||
|
"disabled"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"label",
|
||||||
|
"placeholder",
|
||||||
|
"value",
|
||||||
|
"disabled",
|
||||||
|
"required",
|
||||||
|
"autocomplete",
|
||||||
|
"ariaLabel",
|
||||||
|
"errorMessage",
|
||||||
|
"helpText"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"input",
|
||||||
|
"validation",
|
||||||
|
"email",
|
||||||
|
"form"
|
||||||
|
],
|
||||||
|
"preview": "{{label}} *\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 {{value || placeholder}} \u2502 @\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n{{helpText}}",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "form",
|
||||||
|
"type": "form",
|
||||||
|
"name": "Form Container",
|
||||||
|
"description": "A form container with field grouping, validation state management, and submission handling",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/form.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/form.md",
|
||||||
|
"states": [
|
||||||
|
"default",
|
||||||
|
"valid",
|
||||||
|
"invalid",
|
||||||
|
"submitting"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"title",
|
||||||
|
"method",
|
||||||
|
"action",
|
||||||
|
"fields",
|
||||||
|
"submitText",
|
||||||
|
"resetText",
|
||||||
|
"showReset",
|
||||||
|
"layout",
|
||||||
|
"spacing"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"container",
|
||||||
|
"validation",
|
||||||
|
"form"
|
||||||
|
],
|
||||||
|
"preview": "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 {{title}} \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u2502\n\u2502 {{fields[0].label}} {{fields[0].required ? '*' : ''}} \u2502",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "list",
|
||||||
|
"type": "list",
|
||||||
|
"name": "List Component",
|
||||||
|
"description": "Ordered and unordered list component with various display patterns and interaction support",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/list.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/list.md",
|
||||||
|
"states": [
|
||||||
|
"default",
|
||||||
|
"selected",
|
||||||
|
"hover",
|
||||||
|
"disabled"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"items",
|
||||||
|
"type",
|
||||||
|
"selectable",
|
||||||
|
"multiSelect",
|
||||||
|
"variant",
|
||||||
|
"marker",
|
||||||
|
"numbering",
|
||||||
|
"compact",
|
||||||
|
"bordered",
|
||||||
|
"striped"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"menu",
|
||||||
|
"list",
|
||||||
|
"navigation",
|
||||||
|
"display"
|
||||||
|
],
|
||||||
|
"preview": "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 {{marker}} {{items[0].text}} \u2502\n\u2502 {{marker}} {{items[1].text}} \u2502\n\u2502 {{marker}} {{items[2].text}} \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "modal",
|
||||||
|
"type": "modal",
|
||||||
|
"name": "Modal Dialog",
|
||||||
|
"description": "Modal dialog overlay with focus management, backdrop, and configurable content areas",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/modal.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/modal.md",
|
||||||
|
"states": [
|
||||||
|
"closed",
|
||||||
|
"opening",
|
||||||
|
"open",
|
||||||
|
"closing"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"title",
|
||||||
|
"content",
|
||||||
|
"size",
|
||||||
|
"showCloseButton",
|
||||||
|
"closable",
|
||||||
|
"backdrop",
|
||||||
|
"backdropClosable",
|
||||||
|
"centered",
|
||||||
|
"buttons",
|
||||||
|
"icon",
|
||||||
|
"variant"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"overlay",
|
||||||
|
"popup",
|
||||||
|
"modal",
|
||||||
|
"dialog",
|
||||||
|
"container"
|
||||||
|
],
|
||||||
|
"preview": "\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\n\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\n\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\n\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2502 {{title}} \u2502\u2715\u2502\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\n\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "navigation",
|
||||||
|
"type": "navigation",
|
||||||
|
"name": "Navigation Menu",
|
||||||
|
"description": "A horizontal or vertical navigation component with active state management",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/navigation.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/navigation.md",
|
||||||
|
"states": [
|
||||||
|
"default",
|
||||||
|
"active",
|
||||||
|
"hover"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"items",
|
||||||
|
"orientation",
|
||||||
|
"variant",
|
||||||
|
"separator",
|
||||||
|
"activeIndicator"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"menu",
|
||||||
|
"nav",
|
||||||
|
"navigation"
|
||||||
|
],
|
||||||
|
"preview": "\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 [*] {{items[0].label}} {{separator}} {{items[1].label}} {{separator}} {{items[2].label}} \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "primary-button",
|
||||||
|
"type": "button",
|
||||||
|
"name": "Primary Button",
|
||||||
|
"description": "A primary action button with emphasis styling for main user actions",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/primary-button.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/primary-button.md",
|
||||||
|
"states": [
|
||||||
|
"default",
|
||||||
|
"hover",
|
||||||
|
"active",
|
||||||
|
"disabled"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"text",
|
||||||
|
"disabled",
|
||||||
|
"size",
|
||||||
|
"fullWidth",
|
||||||
|
"ariaLabel"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"button",
|
||||||
|
"action",
|
||||||
|
"primary",
|
||||||
|
"form"
|
||||||
|
],
|
||||||
|
"preview": "\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\n\u2593 {{text}} \u2593\n\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593\u2593",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "secondary-button",
|
||||||
|
"type": "button",
|
||||||
|
"name": "Secondary Button",
|
||||||
|
"description": "A secondary action button with subtle styling for less prominent actions",
|
||||||
|
"file": "{SKILL_ROOT}/../uxscii-component-creator/templates/secondary-button.uxm",
|
||||||
|
"md_file": "{SKILL_ROOT}/../uxscii-component-creator/templates/secondary-button.md",
|
||||||
|
"states": [
|
||||||
|
"default",
|
||||||
|
"hover",
|
||||||
|
"active",
|
||||||
|
"disabled"
|
||||||
|
],
|
||||||
|
"props": [
|
||||||
|
"text",
|
||||||
|
"disabled",
|
||||||
|
"size",
|
||||||
|
"fullWidth",
|
||||||
|
"ariaLabel"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"secondary",
|
||||||
|
"button",
|
||||||
|
"action",
|
||||||
|
"form"
|
||||||
|
],
|
||||||
|
"preview": "\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\n\u2591 {{text}} \u2591\n\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591",
|
||||||
|
"interactive": false,
|
||||||
|
"has_accessibility": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"by_type": {
|
||||||
|
"alert": [
|
||||||
|
"alert"
|
||||||
|
],
|
||||||
|
"badge": [
|
||||||
|
"badge"
|
||||||
|
],
|
||||||
|
"card": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"custom": [
|
||||||
|
"custom-widget"
|
||||||
|
],
|
||||||
|
"input": [
|
||||||
|
"email-input"
|
||||||
|
],
|
||||||
|
"form": [
|
||||||
|
"form"
|
||||||
|
],
|
||||||
|
"list": [
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"modal": [
|
||||||
|
"modal"
|
||||||
|
],
|
||||||
|
"navigation": [
|
||||||
|
"navigation"
|
||||||
|
],
|
||||||
|
"button": [
|
||||||
|
"primary-button",
|
||||||
|
"secondary-button"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"by_tag": {
|
||||||
|
"message": [
|
||||||
|
"alert"
|
||||||
|
],
|
||||||
|
"notification": [
|
||||||
|
"alert"
|
||||||
|
],
|
||||||
|
"alert": [
|
||||||
|
"alert"
|
||||||
|
],
|
||||||
|
"feedback": [
|
||||||
|
"alert"
|
||||||
|
],
|
||||||
|
"badge": [
|
||||||
|
"badge"
|
||||||
|
],
|
||||||
|
"tag": [
|
||||||
|
"badge"
|
||||||
|
],
|
||||||
|
"label": [
|
||||||
|
"badge"
|
||||||
|
],
|
||||||
|
"indicator": [
|
||||||
|
"badge"
|
||||||
|
],
|
||||||
|
"status": [
|
||||||
|
"badge"
|
||||||
|
],
|
||||||
|
"layout": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"container": [
|
||||||
|
"card",
|
||||||
|
"form",
|
||||||
|
"modal"
|
||||||
|
],
|
||||||
|
"card": [
|
||||||
|
"card",
|
||||||
|
"custom-widget"
|
||||||
|
],
|
||||||
|
"custom": [
|
||||||
|
"custom-widget"
|
||||||
|
],
|
||||||
|
"widget": [
|
||||||
|
"custom-widget"
|
||||||
|
],
|
||||||
|
"input": [
|
||||||
|
"email-input"
|
||||||
|
],
|
||||||
|
"validation": [
|
||||||
|
"email-input",
|
||||||
|
"form"
|
||||||
|
],
|
||||||
|
"email": [
|
||||||
|
"email-input"
|
||||||
|
],
|
||||||
|
"form": [
|
||||||
|
"email-input",
|
||||||
|
"form",
|
||||||
|
"primary-button",
|
||||||
|
"secondary-button"
|
||||||
|
],
|
||||||
|
"menu": [
|
||||||
|
"list",
|
||||||
|
"navigation"
|
||||||
|
],
|
||||||
|
"list": [
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"navigation": [
|
||||||
|
"list",
|
||||||
|
"navigation"
|
||||||
|
],
|
||||||
|
"display": [
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"overlay": [
|
||||||
|
"modal"
|
||||||
|
],
|
||||||
|
"popup": [
|
||||||
|
"modal"
|
||||||
|
],
|
||||||
|
"modal": [
|
||||||
|
"modal"
|
||||||
|
],
|
||||||
|
"dialog": [
|
||||||
|
"modal"
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
"navigation"
|
||||||
|
],
|
||||||
|
"button": [
|
||||||
|
"primary-button",
|
||||||
|
"secondary-button"
|
||||||
|
],
|
||||||
|
"action": [
|
||||||
|
"primary-button",
|
||||||
|
"secondary-button"
|
||||||
|
],
|
||||||
|
"primary": [
|
||||||
|
"primary-button"
|
||||||
|
],
|
||||||
|
"secondary": [
|
||||||
|
"secondary-button"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
77
skills/fluxwing-library-browser/docs/07-examples-guide.md
Normal file
77
skills/fluxwing-library-browser/docs/07-examples-guide.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Component Examples
|
||||||
|
|
||||||
|
This directory contains **fully expanded** component examples showing complete interaction states.
|
||||||
|
|
||||||
|
## Important Note
|
||||||
|
|
||||||
|
**These examples show expanded components** - they include all interactive states (hover, focus, disabled, error, etc.).
|
||||||
|
|
||||||
|
When you create new components using `/fluxwing-create`, they will have **only the default state** for fast MVP prototyping. This is by design to optimize for speed and agile development.
|
||||||
|
|
||||||
|
## Two-Phase Creation Workflow
|
||||||
|
|
||||||
|
### Phase 1: Fast MVP Creation
|
||||||
|
```bash
|
||||||
|
/fluxwing-create my-button
|
||||||
|
# Creates component with default state only
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result**: Minimal, MVP-ready component for quick prototyping and stakeholder discussions.
|
||||||
|
|
||||||
|
### Phase 2: Add Interactive Polish
|
||||||
|
```bash
|
||||||
|
/fluxwing-expand-component my-button
|
||||||
|
# Adds hover, active, disabled states automatically
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result**: Fully interactive component like the examples in this directory.
|
||||||
|
|
||||||
|
## What's in This Directory
|
||||||
|
|
||||||
|
All examples are **production-ready templates** showing:
|
||||||
|
- Complete state coverage (default, hover, focus, disabled, etc.)
|
||||||
|
- Full metadata and accessibility attributes
|
||||||
|
- Proper variable definitions
|
||||||
|
- ASCII art for all states
|
||||||
|
|
||||||
|
These serve as:
|
||||||
|
1. **Reference patterns** for component structure
|
||||||
|
2. **Visual examples** of what expanded components look like
|
||||||
|
3. **Templates** you can copy and customize
|
||||||
|
|
||||||
|
## Using These Examples
|
||||||
|
|
||||||
|
### View an Example
|
||||||
|
```bash
|
||||||
|
/fluxwing-get primary-button
|
||||||
|
```
|
||||||
|
|
||||||
|
### Copy to Your Project
|
||||||
|
```bash
|
||||||
|
/fluxwing-library
|
||||||
|
# Browse and copy bundled templates
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create from Scratch
|
||||||
|
```bash
|
||||||
|
/fluxwing-create my-component
|
||||||
|
# Then expand when needed:
|
||||||
|
/fluxwing-expand-component my-component
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Examples
|
||||||
|
|
||||||
|
- **Buttons**: primary-button, secondary-button
|
||||||
|
- **Inputs**: email-input, password-input, text-input
|
||||||
|
- **Cards**: card, pricing-card
|
||||||
|
- **Forms**: form components
|
||||||
|
- **Navigation**: navigation components
|
||||||
|
- **Feedback**: alert, badge components
|
||||||
|
|
||||||
|
All examples follow uxscii standards and best practices.
|
||||||
|
|
||||||
|
## Need More?
|
||||||
|
|
||||||
|
- **Component creation guide**: See `../docs/03-component-creation.md`
|
||||||
|
- **ASCII patterns**: See `../docs/06-ascii-patterns.md`
|
||||||
|
- **Quick start**: See `../docs/01-quick-start.md`
|
||||||
172
skills/fluxwing-library-browser/scripts/build_index.py
Normal file
172
skills/fluxwing-library-browser/scripts/build_index.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Build searchable index of all component templates.
|
||||||
|
Run this when templates are added/updated.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python build_index.py [templates_dir] [output_file]
|
||||||
|
|
||||||
|
Defaults:
|
||||||
|
templates_dir: ../uxscii-component-creator/templates/
|
||||||
|
output_file: ../data/template-index.json
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def extract_preview(md_file: Path, max_lines: int = 5) -> str:
|
||||||
|
"""Extract ASCII preview from .md file."""
|
||||||
|
if not md_file.exists():
|
||||||
|
return ""
|
||||||
|
|
||||||
|
content = md_file.read_text()
|
||||||
|
|
||||||
|
# Find first code block after "## Default State"
|
||||||
|
match = re.search(r'## Default State.*?```(.*?)```', content, re.DOTALL)
|
||||||
|
if match:
|
||||||
|
lines = match.group(1).strip().split('\n')
|
||||||
|
return '\n'.join(lines[:max_lines])
|
||||||
|
|
||||||
|
# Fallback: find any code block
|
||||||
|
match = re.search(r'```(.*?)```', content, re.DOTALL)
|
||||||
|
if match:
|
||||||
|
lines = match.group(1).strip().split('\n')
|
||||||
|
return '\n'.join(lines[:max_lines])
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def infer_tags(component: dict) -> list:
|
||||||
|
"""Infer searchable tags from component."""
|
||||||
|
tags = [component["type"]]
|
||||||
|
|
||||||
|
# Add interactivity tags
|
||||||
|
if component.get("behavior", {}).get("interactive"):
|
||||||
|
tags.append("interactive")
|
||||||
|
|
||||||
|
# Add form-related tags
|
||||||
|
if component["type"] in ["input", "button", "form", "checkbox", "radio", "select"]:
|
||||||
|
tags.append("form")
|
||||||
|
|
||||||
|
# Add navigation tags
|
||||||
|
if component["type"] in ["navigation", "breadcrumb", "pagination", "tabs"]:
|
||||||
|
tags.append("navigation")
|
||||||
|
|
||||||
|
# Add container tags
|
||||||
|
if component["type"] in ["card", "modal", "panel", "container"]:
|
||||||
|
tags.append("container")
|
||||||
|
|
||||||
|
# Add from component metadata tags
|
||||||
|
if "tags" in component.get("metadata", {}):
|
||||||
|
tags.extend(component["metadata"]["tags"])
|
||||||
|
|
||||||
|
return list(set(tags)) # Remove duplicates
|
||||||
|
|
||||||
|
|
||||||
|
def build_index(templates_dir: Path, output_file: Path):
|
||||||
|
"""Build index from all .uxm files in directory."""
|
||||||
|
index = {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"generated": datetime.utcnow().isoformat() + "Z",
|
||||||
|
"template_count": 0,
|
||||||
|
"bundled_templates": [],
|
||||||
|
"by_type": {},
|
||||||
|
"by_tag": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
uxm_files = sorted(templates_dir.glob("*.uxm"))
|
||||||
|
|
||||||
|
if not uxm_files:
|
||||||
|
print(f"⚠ Warning: No .uxm files found in {templates_dir}")
|
||||||
|
return
|
||||||
|
|
||||||
|
for uxm_file in uxm_files:
|
||||||
|
try:
|
||||||
|
with open(uxm_file) as f:
|
||||||
|
component = json.load(f)
|
||||||
|
|
||||||
|
md_file = uxm_file.with_suffix('.md')
|
||||||
|
preview = extract_preview(md_file)
|
||||||
|
|
||||||
|
# Get states
|
||||||
|
states = component.get("behavior", {}).get("states", [])
|
||||||
|
state_names = [s["name"] if isinstance(s, dict) else s for s in states]
|
||||||
|
|
||||||
|
# Extract metadata
|
||||||
|
template_info = {
|
||||||
|
"id": component["id"],
|
||||||
|
"type": component["type"],
|
||||||
|
"name": component["metadata"]["name"],
|
||||||
|
"description": component["metadata"].get("description", ""),
|
||||||
|
"file": f"{{SKILL_ROOT}}/../uxscii-component-creator/templates/{uxm_file.name}",
|
||||||
|
"md_file": f"{{SKILL_ROOT}}/../uxscii-component-creator/templates/{md_file.name}",
|
||||||
|
"states": state_names,
|
||||||
|
"props": list(component.get("props", {}).keys()),
|
||||||
|
"tags": infer_tags(component),
|
||||||
|
"preview": preview,
|
||||||
|
"interactive": component.get("behavior", {}).get("interactive", False),
|
||||||
|
"has_accessibility": bool(component.get("accessibility"))
|
||||||
|
}
|
||||||
|
|
||||||
|
index["bundled_templates"].append(template_info)
|
||||||
|
|
||||||
|
# Index by type
|
||||||
|
comp_type = component["type"]
|
||||||
|
if comp_type not in index["by_type"]:
|
||||||
|
index["by_type"][comp_type] = []
|
||||||
|
index["by_type"][comp_type].append(component["id"])
|
||||||
|
|
||||||
|
# Index by tags
|
||||||
|
for tag in template_info["tags"]:
|
||||||
|
if tag not in index["by_tag"]:
|
||||||
|
index["by_tag"][tag] = []
|
||||||
|
index["by_tag"][tag].append(component["id"])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Error processing {uxm_file.name}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
index["template_count"] = len(index["bundled_templates"])
|
||||||
|
|
||||||
|
# Write index
|
||||||
|
output_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
|
json.dump(index, f, indent=2)
|
||||||
|
|
||||||
|
print(f"✓ Built index with {index['template_count']} templates")
|
||||||
|
print(f" Types: {len(index['by_type'])} ({', '.join(sorted(index['by_type'].keys()))})")
|
||||||
|
print(f" Tags: {len(index['by_tag'])} ({', '.join(sorted(index['by_tag'].keys()))})")
|
||||||
|
print(f" Output: {output_file}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Determine paths
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
|
||||||
|
if len(sys.argv) >= 2:
|
||||||
|
templates_dir = Path(sys.argv[1])
|
||||||
|
else:
|
||||||
|
templates_dir = script_dir.parent / "../uxscii-component-creator/templates"
|
||||||
|
|
||||||
|
if len(sys.argv) >= 3:
|
||||||
|
output_file = Path(sys.argv[2])
|
||||||
|
else:
|
||||||
|
output_file = script_dir.parent / "data/template-index.json"
|
||||||
|
|
||||||
|
templates_dir = templates_dir.resolve()
|
||||||
|
output_file = output_file.resolve()
|
||||||
|
|
||||||
|
if not templates_dir.exists():
|
||||||
|
print(f"✗ Error: Templates directory not found: {templates_dir}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"Building index from: {templates_dir}")
|
||||||
|
build_index(templates_dir, output_file)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
882
skills/fluxwing-screen-scaffolder/SKILL.md
Normal file
882
skills/fluxwing-screen-scaffolder/SKILL.md
Normal file
@@ -0,0 +1,882 @@
|
|||||||
|
---
|
||||||
|
name: Fluxwing Screen Scaffolder
|
||||||
|
description: Build complete UI screens by composing multiple uxscii components. Use when working with .uxm files, when user wants to create, scaffold, or build .uxm screens like login, dashboard, profile, settings, or checkout pages.
|
||||||
|
version: 0.0.1
|
||||||
|
author: Trabian
|
||||||
|
allowed-tools: Read, Write, Glob, Grep, Task, TodoWrite
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fluxwing Screen Scaffolder
|
||||||
|
|
||||||
|
Create complete screen designs using the **uxscii standard** by orchestrating specialized agents.
|
||||||
|
|
||||||
|
## Data Location Rules
|
||||||
|
|
||||||
|
**READ from (bundled templates - reference only):**
|
||||||
|
- `{SKILL_ROOT}/../uxscii-component-creator/templates/` - 11 component templates
|
||||||
|
- `{SKILL_ROOT}/templates/` - 2 screen examples
|
||||||
|
- `{SKILL_ROOT}/docs/` - Documentation
|
||||||
|
|
||||||
|
**INVENTORY sources (check all for available components):**
|
||||||
|
- `./fluxwing/components/` - User-created components (FIRST PRIORITY)
|
||||||
|
- `./fluxwing/library/` - Customized template copies
|
||||||
|
- `{SKILL_ROOT}/../uxscii-component-creator/templates/` - Bundled templates (READ-ONLY)
|
||||||
|
|
||||||
|
**WRITE to (project workspace):**
|
||||||
|
- `./fluxwing/screens/` - Your created screens (via composer agent)
|
||||||
|
|
||||||
|
**NEVER write to skill directories - they are read-only!**
|
||||||
|
|
||||||
|
**LOAD for copy-on-update logic:**
|
||||||
|
- `{SKILL_ROOT}/../shared/docs/copy-versioning.md` - Versioning pattern for existing screens
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
**⚠️ YOU ARE AN ORCHESTRATOR - DO NOT DO THE WORK YOURSELF! ⚠️**
|
||||||
|
|
||||||
|
Your role is to **spawn agents** using the Task tool. You coordinate agents, you don't create components directly.
|
||||||
|
|
||||||
|
Help the user scaffold a complete screen by orchestrating agents in two phases:
|
||||||
|
1. **Phase 1 (Parallel)**: Spawn multiple designer agents - one per missing component using multiple Task tool calls in ONE message
|
||||||
|
2. **Phase 2 (Sequential)**: After all components exist, spawn composer agent
|
||||||
|
|
||||||
|
**Concurrency Model**:
|
||||||
|
- If 6 components are missing → spawn 6 agents in parallel using 6 Task tool calls in ONE message (~6x faster)
|
||||||
|
- Then wait for all to complete before composing screen
|
||||||
|
|
||||||
|
**CRITICAL**: Use the Task tool to spawn agents. Do NOT use TodoWrite and work through tasks yourself. Do NOT create files yourself.
|
||||||
|
|
||||||
|
**MULTI-SCREEN SCENARIOS:**
|
||||||
|
|
||||||
|
If user requests N screens (N > 1):
|
||||||
|
- ⚠️ **DO NOT** create TodoWrite list with N screen tasks and work through them
|
||||||
|
- ⚠️ **DO NOT** use Write/Edit tools to create screen files yourself
|
||||||
|
- ⚠️ **DO NOT** "help" composer agents by pre-creating any files
|
||||||
|
- ✅ **DO** spawn N composer agents in parallel (one per screen)
|
||||||
|
- ✅ **DO** send ONE message with ALL Task calls (parallel execution)
|
||||||
|
- ✅ **DO** let each composer agent independently create all 3 files (.uxm, .md, .rendered.md)
|
||||||
|
|
||||||
|
**Example:** 6 screens = ONE message with 6 Task tool calls → 18 files created by agents
|
||||||
|
|
||||||
|
You are an ORCHESTRATOR. Your job is to spawn agents, not do their work.
|
||||||
|
|
||||||
|
## Quality Presets
|
||||||
|
|
||||||
|
Scaffolder supports different quality levels for speed vs fidelity tradeoffs:
|
||||||
|
|
||||||
|
### fast (60-90s)
|
||||||
|
- Create components (sketch fidelity)
|
||||||
|
- Compose screen
|
||||||
|
- Skip enhancement
|
||||||
|
- Use when: Rapid prototyping, early iteration
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Components: sketch fidelity (.uxm only initially, .md by composer)
|
||||||
|
- Screen: .rendered.md with basic quality
|
||||||
|
- Time: ~60-90s for 6 components
|
||||||
|
|
||||||
|
### balanced (90-150s)
|
||||||
|
- Create components (sketch fidelity)
|
||||||
|
- Compose screen
|
||||||
|
- Enhance to basic fidelity
|
||||||
|
- Use when: Quick iteration with decent quality
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Components: basic fidelity (.uxm + improved .md)
|
||||||
|
- Screen: .rendered.md with good quality
|
||||||
|
- Time: ~90-150s for 6 components
|
||||||
|
|
||||||
|
### detailed (180-240s) **[DEFAULT]**
|
||||||
|
- Create components (sketch fidelity)
|
||||||
|
- Compose screen
|
||||||
|
- Enhance to detailed fidelity (hover + focus states)
|
||||||
|
- Use when: Production-ready screens, demos
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Components: detailed fidelity (.uxm + polished .md + states)
|
||||||
|
- Screen: .rendered.md with high quality
|
||||||
|
- Time: ~180-240s for 6 components
|
||||||
|
|
||||||
|
### production (300-400s)
|
||||||
|
- Create components (sketch fidelity)
|
||||||
|
- Compose screen
|
||||||
|
- Enhance to production fidelity (all states + full a11y)
|
||||||
|
- Use when: Final polish, publication
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Components: production fidelity (complete)
|
||||||
|
- Screen: .rendered.md with publication quality
|
||||||
|
- Time: ~300-400s for 6 components
|
||||||
|
|
||||||
|
**Default: detailed** (best balance of quality and speed)
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### Step 1: Understand the Screen
|
||||||
|
|
||||||
|
Ask about the screen they want to create:
|
||||||
|
|
||||||
|
**Screen details:**
|
||||||
|
- **Screen name and purpose**: login, dashboard, profile, settings, checkout, etc.
|
||||||
|
- **Required components**: forms, navigation, cards, modals, lists, etc.
|
||||||
|
- **Layout structure**: vertical, horizontal, grid, sidebar+main, etc.
|
||||||
|
|
||||||
|
**Quality preset** (optional):
|
||||||
|
- **fast** - Quick prototype (60-90s)
|
||||||
|
- **balanced** - Good quality (90-150s)
|
||||||
|
- **detailed** - High quality (180-240s) [DEFAULT]
|
||||||
|
- **production** - Publication ready (300-400s)
|
||||||
|
|
||||||
|
If user doesn't specify, use **detailed** preset.
|
||||||
|
|
||||||
|
**Detect multi-screen requests:**
|
||||||
|
|
||||||
|
If the user's request indicates multiple screens, detect by:
|
||||||
|
- **Plural language**: "screens", "pages", "all of them"
|
||||||
|
- **Multiple file references**: Glob results showing multiple screenshots or files
|
||||||
|
- **Explicit numbers**: "5 screens", "all these", "create pages for..."
|
||||||
|
|
||||||
|
**When multiple screens detected**, confirm with the user:
|
||||||
|
|
||||||
|
```
|
||||||
|
I see you want to create [N] screens:
|
||||||
|
- [screen-name-1]
|
||||||
|
- [screen-name-2]
|
||||||
|
- [screen-name-3]
|
||||||
|
- ...
|
||||||
|
|
||||||
|
I'll create these in parallel using one composer agent per screen.
|
||||||
|
Each screen will get all 3 files (.uxm, .md, .rendered.md).
|
||||||
|
|
||||||
|
Quality preset: [preset] (default: detailed)
|
||||||
|
Estimated time: ~[X-Y] seconds
|
||||||
|
|
||||||
|
Proceed? [Yes/No]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Purpose:**
|
||||||
|
- Sets clear expectations upfront
|
||||||
|
- Emphasizes .rendered.md is included for ALL screens
|
||||||
|
- Provides time estimate
|
||||||
|
- Prevents surprises
|
||||||
|
|
||||||
|
After confirmation, use **Path B workflow** (see below).
|
||||||
|
|
||||||
|
### Step 2: Component Inventory
|
||||||
|
|
||||||
|
Check what components are available **in this order**:
|
||||||
|
|
||||||
|
1. **User-created**: `./fluxwing/components/` (FIRST PRIORITY)
|
||||||
|
2. **Library**: `./fluxwing/library/` (customized templates)
|
||||||
|
3. **Bundled examples**: `{SKILL_ROOT}/../uxscii-component-creator/templates/` (READ-ONLY)
|
||||||
|
|
||||||
|
List what exists vs what needs to be created.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example inventory check
|
||||||
|
const userComponents = glob('./fluxwing/components/*.uxm');
|
||||||
|
const libraryComponents = glob('./fluxwing/library/*.uxm');
|
||||||
|
const bundledComponents = glob('{SKILL_ROOT}/../uxscii-component-creator/templates/*.uxm');
|
||||||
|
|
||||||
|
const available = [...userComponents, ...libraryComponents, ...bundledComponents];
|
||||||
|
const missing = requiredComponents.filter(c => !available.includes(c));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2.5: Pre-Scaffolding Validation (Check for Existing Screen)
|
||||||
|
|
||||||
|
**IMPORTANT**: Before creating any screen, check if it already exists to prevent data loss.
|
||||||
|
|
||||||
|
1. **Convert screen name to kebab-case ID**:
|
||||||
|
```
|
||||||
|
"Login Screen" → "login-screen"
|
||||||
|
"User Dashboard" → "user-dashboard"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check if screen exists**:
|
||||||
|
```bash
|
||||||
|
# Check for existing screen
|
||||||
|
test -f ./fluxwing/screens/{screen-id}.uxm
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **If screen EXISTS**:
|
||||||
|
|
||||||
|
**Inform user and offer choices**:
|
||||||
|
```
|
||||||
|
Screen '{screen-id}' already exists (version {current-version}).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
(a) Create new version (copy-on-update: {screen-id}-v{N+1})
|
||||||
|
(b) Create with different name
|
||||||
|
(c) Cancel operation
|
||||||
|
|
||||||
|
What would you like to do?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Handle user response**:
|
||||||
|
|
||||||
|
- **Choice (a) - Create new version**:
|
||||||
|
1. Load copy-versioning logic from `{SKILL_ROOT}/../shared/docs/copy-versioning.md`
|
||||||
|
2. Read existing `{screen-id}.uxm`
|
||||||
|
3. Find highest version (check for `{screen-id}-v2`, `-v3`, etc.)
|
||||||
|
4. Calculate next version: `v{N+1}`
|
||||||
|
5. Pass to composer agent with versioning parameters:
|
||||||
|
- `baseScreenId`: Original ID (e.g., "login-screen")
|
||||||
|
- `newScreenId`: Versioned ID (e.g., "login-screen-v2")
|
||||||
|
- `baseOnExisting`: true
|
||||||
|
- `sourceVersion`: Highest existing version
|
||||||
|
6. Composer creates THREE files:
|
||||||
|
- `{screen-id}-v{N+1}.uxm`
|
||||||
|
- `{screen-id}-v{N+1}.md`
|
||||||
|
- `{screen-id}-v{N+1}.rendered.md`
|
||||||
|
7. Metadata: Increment minor version (1.0.0 → 1.1.0), update modified, preserve created
|
||||||
|
|
||||||
|
- **Choice (b) - Different name**:
|
||||||
|
1. Ask: "What would you like to name this screen?"
|
||||||
|
2. Wait for user response
|
||||||
|
3. Use new name for screen ID
|
||||||
|
4. Proceed with normal scaffolding
|
||||||
|
|
||||||
|
- **Choice (c) - Cancel**:
|
||||||
|
1. Do not create any files
|
||||||
|
2. Inform user: "Operation cancelled. No files were created."
|
||||||
|
3. Exit workflow
|
||||||
|
|
||||||
|
4. **If screen DOES NOT exist**:
|
||||||
|
- Proceed with normal scaffolding workflow (no versioning needed)
|
||||||
|
- Screen will be created as `{screen-id}.uxm` (no version suffix)
|
||||||
|
- Initial version: 1.0.0
|
||||||
|
|
||||||
|
### Workflow Branching: Single vs Multi-Screen
|
||||||
|
|
||||||
|
After completing the component inventory, choose the appropriate workflow path:
|
||||||
|
|
||||||
|
**Path A: Single Screen** (1 screen requested)
|
||||||
|
- Step 3: Create missing components (parallel)
|
||||||
|
- Step 4: Spawn ONE composer agent
|
||||||
|
- Step 5: Enhancement
|
||||||
|
|
||||||
|
**Path B: Multiple Screens** (2+ screens requested)
|
||||||
|
- Step 3: Create missing components across ALL screens (deduplicated, parallel)
|
||||||
|
- Step 4: Spawn ONE composer agent PER screen (parallel)
|
||||||
|
- Step 5: Enhancement
|
||||||
|
|
||||||
|
**Key distinction:** Path B spawns N composer agents in parallel (one per screen) instead of one.
|
||||||
|
|
||||||
|
**Example scenarios:**
|
||||||
|
- "Create a login screen" → Path A (1 screen, 1 composer)
|
||||||
|
- "Create pages for these 6 screenshots" → Path B (6 screens, 6 composers in parallel)
|
||||||
|
- "Build dashboard and settings screens" → Path B (2 screens, 2 composers in parallel)
|
||||||
|
|
||||||
|
Follow the appropriate path based on screen count. Instructions below apply to **Path A** (single screen). For **Path B** modifications, see notes in Steps 3 and 4.
|
||||||
|
|
||||||
|
### Step 3: Create Missing Components (Fast Mode)
|
||||||
|
|
||||||
|
**If missing components exist**, spawn designer agents in FAST MODE - one agent per component:
|
||||||
|
|
||||||
|
**CRITICAL ORCHESTRATION RULES**:
|
||||||
|
1. ⚠️ **DO NOT** create TodoWrite list and work through it yourself
|
||||||
|
2. ⚠️ **DO NOT** create components yourself using Write/Edit tools
|
||||||
|
3. ✅ **DO** use Task tool to spawn one agent per missing component
|
||||||
|
4. ✅ **DO** send ONE message with ALL Task calls (parallel execution)
|
||||||
|
5. ✅ **DO** use FAST MODE (creates .uxm only, <10s per component)
|
||||||
|
|
||||||
|
**Path B (Multi-screen): Component Deduplication**
|
||||||
|
|
||||||
|
When creating components for multiple screens:
|
||||||
|
|
||||||
|
1. **Inventory ALL screens first**: Collect component requirements from all N screens
|
||||||
|
2. **Deduplicate**: Create a unique list of missing components
|
||||||
|
- Example: 6 screens might need `sidebar-nav`, but create it only once
|
||||||
|
- Shared components across screens = created once, reused everywhere
|
||||||
|
3. **Spawn one agent per UNIQUE component**: Not one per screen × component
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
- Screen 1 needs: [sidebar-nav, button-primary, input-email]
|
||||||
|
- Screen 2 needs: [sidebar-nav, button-primary, input-password]
|
||||||
|
- Screen 3 needs: [sidebar-nav, card-metric]
|
||||||
|
|
||||||
|
**Don't spawn:** 3 × 3 = 9 agents (wasteful duplicates)
|
||||||
|
**Do spawn:** 5 unique agents (sidebar-nav, button-primary, input-email, input-password, card-metric)
|
||||||
|
|
||||||
|
**Fast Mode Creates:**
|
||||||
|
- Component .uxm files (sketch fidelity)
|
||||||
|
- NO .md files (composer will create these)
|
||||||
|
|
||||||
|
**Example: 6 missing components for banking-chat**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
model: "haiku", // Fast model
|
||||||
|
description: "Create message-bubble (fast)",
|
||||||
|
prompt: "Create sketch-fidelity uxscii component from template.
|
||||||
|
|
||||||
|
Component: message-bubble
|
||||||
|
Type: container
|
||||||
|
Screen context: banking-chat
|
||||||
|
|
||||||
|
FAST MODE - Speed is critical! <10 seconds target.
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load minimal template: {SKILL_ROOT}/../fluxwing-component-creator/templates/minimal/container.uxm.template
|
||||||
|
2. Replace variables:
|
||||||
|
- {{id}} = 'message-bubble'
|
||||||
|
- {{name}} = 'Message Bubble'
|
||||||
|
- {{description}} = 'Chat message container for banking-chat'
|
||||||
|
- {{timestamp}} = '${new Date().toISOString()}'
|
||||||
|
- {{screenContext}} = 'banking-chat'
|
||||||
|
|
||||||
|
3. CRITICAL: Set metadata.fidelity = "sketch"
|
||||||
|
|
||||||
|
**REQUIRED FIELD**: The fidelity field is MANDATORY in the schema and tracks progressive enhancement.
|
||||||
|
Fast mode MUST set fidelity to "sketch" to indicate initial scaffolding quality.
|
||||||
|
|
||||||
|
4. Verify JSON is well-formed
|
||||||
|
5. Save to ./fluxwing/components/message-bubble.uxm
|
||||||
|
6. DO NOT create .md file
|
||||||
|
7. DO NOT load documentation
|
||||||
|
|
||||||
|
VERIFICATION:
|
||||||
|
- [ ] metadata.fidelity = "sketch"
|
||||||
|
- [ ] All required fields present (name, description, created, modified, tags, category, fidelity)
|
||||||
|
|
||||||
|
Return: 'Created message-bubble.uxm (sketch fidelity)'
|
||||||
|
|
||||||
|
Target: <10 seconds"
|
||||||
|
})
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
model: "haiku",
|
||||||
|
description: "Create message-input (fast)",
|
||||||
|
prompt: "Create sketch-fidelity uxscii component from template.
|
||||||
|
|
||||||
|
Component: message-input
|
||||||
|
Type: input
|
||||||
|
Screen context: banking-chat
|
||||||
|
|
||||||
|
FAST MODE - Speed is critical! <10 seconds target.
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load minimal template: {SKILL_ROOT}/../fluxwing-component-creator/templates/minimal/input.uxm.template
|
||||||
|
2. Replace variables:
|
||||||
|
- {{id}} = 'message-input'
|
||||||
|
- {{name}} = 'Message Input'
|
||||||
|
- {{description}} = 'Chat input field for banking-chat'
|
||||||
|
- {{timestamp}} = '${new Date().toISOString()}'
|
||||||
|
- {{placeholder}} = 'Type a message...'
|
||||||
|
- {{label}} = 'Message'
|
||||||
|
- {{screenContext}} = 'banking-chat'
|
||||||
|
|
||||||
|
3. CRITICAL: Set metadata.fidelity = "sketch"
|
||||||
|
|
||||||
|
**REQUIRED FIELD**: The fidelity field is MANDATORY in the schema and tracks progressive enhancement.
|
||||||
|
Fast mode MUST set fidelity to "sketch" to indicate initial scaffolding quality.
|
||||||
|
|
||||||
|
4. Verify JSON is well-formed
|
||||||
|
5. Save to ./fluxwing/components/message-input.uxm
|
||||||
|
6. DO NOT create .md file
|
||||||
|
7. DO NOT load documentation
|
||||||
|
|
||||||
|
VERIFICATION:
|
||||||
|
- [ ] metadata.fidelity = "sketch"
|
||||||
|
- [ ] All required fields present (name, description, created, modified, tags, category, fidelity)
|
||||||
|
|
||||||
|
Return: 'Created message-input.uxm (sketch fidelity)'
|
||||||
|
|
||||||
|
Target: <10 seconds"
|
||||||
|
})
|
||||||
|
|
||||||
|
// ... spawn ONE Task per missing component in SAME message ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Performance:** 6 components × 10s = ~60 seconds (was 120-180s)
|
||||||
|
|
||||||
|
**Wait for ALL component agents to complete before Step 4.**
|
||||||
|
|
||||||
|
### Step 4: Compose Screen with Component .md Generation
|
||||||
|
|
||||||
|
**Once all components exist**, spawn the composer agent:
|
||||||
|
|
||||||
|
**Path A (Single Screen):** Spawn ONE composer agent
|
||||||
|
**Path B (Multi-screen):** Spawn ONE composer agent PER screen (parallel)
|
||||||
|
|
||||||
|
**Example Path B:**
|
||||||
|
- 6 screens requested → spawn 6 composer agents in ONE message
|
||||||
|
- Each agent creates 3 files for its assigned screen (.uxm, .md, .rendered.md)
|
||||||
|
- All 6 agents run in parallel (~90s total, not 540s sequential)
|
||||||
|
- Result: 18 files (6 screens × 3 files)
|
||||||
|
|
||||||
|
**CRITICAL: Composer generates .md files for ALL components!**
|
||||||
|
|
||||||
|
This is the "smart composer" - it:
|
||||||
|
1. Generates missing component .md files
|
||||||
|
2. Loads documentation ONCE (not per component)
|
||||||
|
3. Creates screen .uxm + .md + .rendered.md
|
||||||
|
4. Focuses quality on .rendered.md (main deliverable)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
model: "sonnet", // Smart model for quality composition
|
||||||
|
description: "Compose ${screenName} with components",
|
||||||
|
prompt: `You are a uxscii screen composer creating production-ready screens.
|
||||||
|
|
||||||
|
Screen: ${screenName}
|
||||||
|
Components: ${componentList}
|
||||||
|
Layout: ${layoutStructure}
|
||||||
|
${baseOnExisting ? `
|
||||||
|
VERSIONING MODE:
|
||||||
|
- Base on existing: ${baseScreenId}
|
||||||
|
- New screen ID: ${newScreenId}
|
||||||
|
- Source version: ${sourceVersion}
|
||||||
|
- Copy-on-update: Increment minor version, preserve created timestamp
|
||||||
|
- Create THREE files with -v{N} suffix
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
YOUR PRIMARY DELIVERABLE: ${screenName}.rendered.md
|
||||||
|
This is what the user will see. Everything else supports this.
|
||||||
|
|
||||||
|
MANDATORY OUTPUTS (all 3 required):
|
||||||
|
1. ${screenName}.uxm (metadata)
|
||||||
|
2. ${screenName}.md (template with {{placeholders}})
|
||||||
|
3. ${screenName}.rendered.md (REAL data, ACTUAL ASCII) ⚠️ CRITICAL
|
||||||
|
|
||||||
|
If you complete without creating .rendered.md, you have FAILED.
|
||||||
|
|
||||||
|
Your task has TWO parts:
|
||||||
|
|
||||||
|
═══════════════════════════════════════
|
||||||
|
PART 1: Generate Component .md Files
|
||||||
|
═══════════════════════════════════════
|
||||||
|
|
||||||
|
For each component in [${componentList}]:
|
||||||
|
|
||||||
|
1. Read component .uxm from ./fluxwing/components/
|
||||||
|
2. Check if .md exists:
|
||||||
|
- If EXISTS: Skip to next component
|
||||||
|
- If MISSING: Generate .md file (steps 3-5)
|
||||||
|
|
||||||
|
3. Load ASCII patterns (ONCE for all components):
|
||||||
|
{SKILL_ROOT}/../fluxwing-component-creator/docs/06-ascii-patterns.md
|
||||||
|
|
||||||
|
4. Generate simple ASCII art based on component type:
|
||||||
|
- button: Rounded box with label
|
||||||
|
- input: Rectangular box with placeholder
|
||||||
|
- card: Box with title and content area
|
||||||
|
- container: Large box for children
|
||||||
|
- Use dimensions from .uxm (ascii.width, ascii.height)
|
||||||
|
- Keep it simple (sketch quality, not detailed)
|
||||||
|
|
||||||
|
5. Create .md file with:
|
||||||
|
- Component description
|
||||||
|
- ASCII art in code block
|
||||||
|
- Variables from .uxm props
|
||||||
|
- Save to ./fluxwing/components/\${componentId}.md
|
||||||
|
|
||||||
|
Example .md for button:
|
||||||
|
\`\`\`markdown
|
||||||
|
# \${componentName}
|
||||||
|
|
||||||
|
\${description}
|
||||||
|
|
||||||
|
## ASCII
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
╭──────────────────╮
|
||||||
|
│ {{label}} │
|
||||||
|
╰──────────────────╯
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
- label (string): Button text
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
═══════════════════════════════════════
|
||||||
|
PART 2: Compose Screen
|
||||||
|
═══════════════════════════════════════
|
||||||
|
|
||||||
|
1. Load schema: {SKILL_ROOT}/../fluxwing-component-creator/schemas/uxm-component.schema.json
|
||||||
|
2. Load screen docs: {SKILL_ROOT}/docs/04-screen-composition.md
|
||||||
|
|
||||||
|
3. Create screen .uxm:
|
||||||
|
- ${baseOnExisting ? `Load existing: ./fluxwing/screens/${sourceVersion}.uxm` : `type: "container"`}
|
||||||
|
- ${baseOnExisting ? `Base structure on source version` : `props.components: [${componentList}]`}
|
||||||
|
- layout: ${layoutStructure}
|
||||||
|
- ${baseOnExisting ? `id: "${newScreenId}" (with -v{N} suffix)` : `id: "${screenId}"`}
|
||||||
|
- ${baseOnExisting ? `version: Increment minor from source (e.g., 1.0.0 → 1.1.0)` : `version: "1.0.0"`}
|
||||||
|
- ${baseOnExisting ? `metadata.created: PRESERVE from ${sourceVersion}` : `metadata.created: current timestamp`}
|
||||||
|
- ${baseOnExisting ? `metadata.modified: SET to current timestamp` : `metadata.modified: current timestamp`}
|
||||||
|
|
||||||
|
CRITICAL: Set metadata.fidelity = "detailed"
|
||||||
|
|
||||||
|
**REQUIRED FIELD**: Screen fidelity tracks the screen's completion level.
|
||||||
|
Even if components are sketch fidelity, the screen itself is detailed (it's a composition).
|
||||||
|
This enables tracking progressive fidelity at both component and screen levels.
|
||||||
|
|
||||||
|
4. Create screen .md (template):
|
||||||
|
- Use {{component:id}} syntax for component references
|
||||||
|
- Show layout structure with placeholders
|
||||||
|
- ${baseOnExisting ? `Filename: ${newScreenId}.md (versioned)` : `Filename: ${screenId}.md`}
|
||||||
|
|
||||||
|
5. Create screen .rendered.md (MAIN DELIVERABLE):
|
||||||
|
- Embed ACTUAL component ASCII (read from .md files you just created)
|
||||||
|
- Use REAL example data (names: "Sarah Johnson", emails: "sarah@example.com", etc.)
|
||||||
|
- Show all screen states (idle, loading, error if applicable)
|
||||||
|
- High visual quality - this is what user will see!
|
||||||
|
- Make it publication-ready
|
||||||
|
- ${baseOnExisting ? `Filename: ${newScreenId}.rendered.md (versioned)` : `Filename: ${screenId}.rendered.md`}
|
||||||
|
|
||||||
|
6. Save all 3 files to ./fluxwing/screens/
|
||||||
|
- ${baseOnExisting ? `${newScreenId}.uxm, ${newScreenId}.md, ${newScreenId}.rendered.md` : `${screenId}.uxm, ${screenId}.md, ${screenId}.rendered.md`}
|
||||||
|
|
||||||
|
═══════════════════════════════════════
|
||||||
|
Performance Notes:
|
||||||
|
═══════════════════════════════════════
|
||||||
|
|
||||||
|
- Load ASCII patterns ONCE, reuse for all components
|
||||||
|
- Keep component .md simple (detailed version comes with enhancement)
|
||||||
|
- Focus quality effort on screen .rendered.md
|
||||||
|
- Target: 60-90 seconds total
|
||||||
|
|
||||||
|
═══════════════════════════════════════
|
||||||
|
VERIFICATION CHECKLIST (before returning):
|
||||||
|
═══════════════════════════════════════
|
||||||
|
|
||||||
|
- [ ] .uxm file created and saved to ./fluxwing/screens/
|
||||||
|
- [ ] .md template created and saved to ./fluxwing/screens/
|
||||||
|
- [ ] .rendered.md created with REAL data and ACTUAL component ASCII
|
||||||
|
- [ ] All 3 files are in ./fluxwing/screens/
|
||||||
|
- [ ] .rendered.md uses realistic example data (not placeholders)
|
||||||
|
- [ ] .rendered.md embeds actual ASCII from component .md files
|
||||||
|
|
||||||
|
Return: "Created ${screenName}: .uxm ✓, .md ✓, .rendered.md ✓"
|
||||||
|
`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Auto-Enhancement (Based on Quality Preset)
|
||||||
|
|
||||||
|
After screen composition, optionally enhance components based on quality preset.
|
||||||
|
|
||||||
|
**Determine enhancement level:**
|
||||||
|
- fast → Skip (no enhancement)
|
||||||
|
- balanced → Enhance to "basic"
|
||||||
|
- detailed → Enhance to "detailed" [DEFAULT]
|
||||||
|
- production → Enhance to "production"
|
||||||
|
|
||||||
|
**If enhancement needed:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
model: "sonnet", // Quality model for enhancement
|
||||||
|
description: "Enhance screen components to ${targetFidelity}",
|
||||||
|
prompt: `Enhance all components in ${screenName} to ${targetFidelity} fidelity.
|
||||||
|
|
||||||
|
Screen: ${screenName}
|
||||||
|
Components: ${componentList}
|
||||||
|
Current fidelity: sketch
|
||||||
|
Target fidelity: ${targetFidelity}
|
||||||
|
|
||||||
|
Use fluxwing-enhancer patterns:
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load enhancement patterns: {SKILL_ROOT}/../fluxwing-enhancer/docs/enhancement-patterns.md
|
||||||
|
2. Load state templates: {SKILL_ROOT}/../fluxwing-component-creator/templates/state-additions/
|
||||||
|
|
||||||
|
3. For EACH component (process in parallel if possible):
|
||||||
|
|
||||||
|
Read: ./fluxwing/components/\${componentId}.uxm
|
||||||
|
Read: ./fluxwing/components/\${componentId}.md
|
||||||
|
|
||||||
|
Enhance based on target:
|
||||||
|
|
||||||
|
${targetFidelity === 'basic' ? `
|
||||||
|
basic fidelity:
|
||||||
|
- Improve metadata.description (specific, 1-2 sentences)
|
||||||
|
- Add relevant tags
|
||||||
|
- Polish .md ASCII (clean, aligned)
|
||||||
|
- Keep default state only
|
||||||
|
- CRITICAL: Update metadata.fidelity = "basic" (REQUIRED FIELD)
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${targetFidelity === 'detailed' ? `
|
||||||
|
detailed fidelity:
|
||||||
|
- All "basic" enhancements
|
||||||
|
- Add hover state (use templates/state-additions/hover.json)
|
||||||
|
- Add focus state (use templates/state-additions/focus.json)
|
||||||
|
- Polish ASCII art (rounded corners, smooth)
|
||||||
|
- Add props.examples array (2-3 examples)
|
||||||
|
- CRITICAL: Update metadata.fidelity = "detailed" (REQUIRED FIELD)
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${targetFidelity === 'production' ? `
|
||||||
|
production fidelity:
|
||||||
|
- All "detailed" enhancements
|
||||||
|
- Add disabled state (if applicable)
|
||||||
|
- Add error state (if form component)
|
||||||
|
- Complete accessibility metadata
|
||||||
|
- Add keyboard shortcuts
|
||||||
|
- Pixel-perfect ASCII
|
||||||
|
- CRITICAL: Update metadata.fidelity = "production" (REQUIRED FIELD)
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
Save: Updated .uxm and .md to ./fluxwing/components/
|
||||||
|
|
||||||
|
4. After ALL components enhanced:
|
||||||
|
|
||||||
|
Regenerate screen .rendered.md:
|
||||||
|
- Read enhanced component .md files
|
||||||
|
- Embed improved ASCII in .rendered.md
|
||||||
|
- Maintain real example data
|
||||||
|
- Save to ./fluxwing/screens/
|
||||||
|
|
||||||
|
5. Return summary:
|
||||||
|
- Components enhanced: [list with changes]
|
||||||
|
- Total time: Xs
|
||||||
|
- Screen .rendered.md regenerated
|
||||||
|
|
||||||
|
Target time:
|
||||||
|
- basic: 30s (parallel for all components)
|
||||||
|
- detailed: 90s (parallel for all components)
|
||||||
|
- production: 180s (parallel for all components)
|
||||||
|
|
||||||
|
Note: Process components in parallel when possible for maximum speed!
|
||||||
|
`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Enhancement is parallel:** All 6 components enhanced simultaneously, so time ≈ single component time!
|
||||||
|
|
||||||
|
### Step 5.5: Validate Screen
|
||||||
|
|
||||||
|
**REQUIRED**: Validate the screen file against the schema after composition.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Validate the screen
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-screen.js \
|
||||||
|
./fluxwing/screens/${screenId}.uxm \
|
||||||
|
{SKILL_ROOT}/../fluxwing-component-creator/schemas/uxm-component.schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected behavior**:
|
||||||
|
- ✅ **If validation passes**: Continue to Step 6 (Report Results)
|
||||||
|
- ❌ **If validation fails**: Report errors clearly and stop execution
|
||||||
|
|
||||||
|
**Screen-specific checks**:
|
||||||
|
- Validates against uxscii schema (same as components)
|
||||||
|
- Checks for .rendered.md file (warning if missing)
|
||||||
|
- Checks if composed components exist (warnings for missing components)
|
||||||
|
|
||||||
|
**Example output** (success):
|
||||||
|
```
|
||||||
|
✓ Valid: login-screen
|
||||||
|
Type: container
|
||||||
|
Version: 1.0.0
|
||||||
|
States: 3
|
||||||
|
Props: 3
|
||||||
|
|
||||||
|
⚠ Warnings:
|
||||||
|
Warning 1: Referenced component not found: custom-input
|
||||||
|
Location: composed
|
||||||
|
```
|
||||||
|
|
||||||
|
**If validation fails**, show errors to user:
|
||||||
|
```
|
||||||
|
✗ Validation Failed
|
||||||
|
|
||||||
|
Error 1: must have required property 'fidelity'
|
||||||
|
Location: metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Report Results
|
||||||
|
|
||||||
|
Create comprehensive summary including enhancement details:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Screen Scaffolding Complete ✓
|
||||||
|
|
||||||
|
## Screen: ${screenName}
|
||||||
|
## Quality Preset: ${qualityPreset}
|
||||||
|
|
||||||
|
### Phase 1: Fast Component Creation ⚡
|
||||||
|
${missingComponents.map(c => `✓ ${c}.uxm (sketch fidelity, ~10s)`).join('\n')}
|
||||||
|
|
||||||
|
Time: ~${missingComponents.length * 10}s
|
||||||
|
|
||||||
|
### Phase 2: Smart Composition 🎨
|
||||||
|
Component .md files generated:
|
||||||
|
${componentList.map(c => `✓ ${c}.md (by composer)`).join('\n')}
|
||||||
|
|
||||||
|
Screen files created:
|
||||||
|
✓ ${screenName}.uxm
|
||||||
|
✓ ${screenName}.md (template)
|
||||||
|
✓ ${screenName}.rendered.md
|
||||||
|
|
||||||
|
Time: ~60-90s
|
||||||
|
|
||||||
|
${qualityPreset !== 'fast' ? `
|
||||||
|
### Phase 3: Auto-Enhancement ✨
|
||||||
|
Components enhanced to ${targetFidelity} fidelity:
|
||||||
|
${componentList.map(c => `✓ ${c}: sketch → ${targetFidelity}`).join('\n')}
|
||||||
|
|
||||||
|
Enhancements applied:
|
||||||
|
${targetFidelity === 'basic' ? '- Improved descriptions\n- Added specific tags\n- Polished ASCII' : ''}
|
||||||
|
${targetFidelity === 'detailed' ? '- Improved descriptions\n- Added hover + focus states\n- Polished ASCII\n- Added usage examples' : ''}
|
||||||
|
${targetFidelity === 'production' ? '- All detailed enhancements\n- Added disabled + error states\n- Complete accessibility\n- Pixel-perfect ASCII' : ''}
|
||||||
|
|
||||||
|
Screen .rendered.md regenerated with enhanced components
|
||||||
|
|
||||||
|
Time: ~${enhancementTime[targetFidelity]}s
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
### Total Time: ~${totalTime}s (${Math.round(totalTime/60)} minutes)
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Target: ${targetTime[qualityPreset]}
|
||||||
|
- Actual: ${totalTime}s
|
||||||
|
- Status: ${totalTime <= targetTime[qualityPreset] ? '✓ ON TARGET' : '⚠ OVER TARGET'}
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
|
||||||
|
**Components** (./fluxwing/components/):
|
||||||
|
- ${componentList.length} .uxm files (${finalFidelity} fidelity)
|
||||||
|
- ${componentList.length} .md files
|
||||||
|
|
||||||
|
**Screen** (./fluxwing/screens/):
|
||||||
|
- ${screenName}.uxm
|
||||||
|
- ${screenName}.md
|
||||||
|
- ${screenName}.rendered.md
|
||||||
|
|
||||||
|
**Total: ${componentList.length * 2 + 3} files**
|
||||||
|
|
||||||
|
## Quality Assessment
|
||||||
|
|
||||||
|
- Component fidelity: ${finalFidelity}
|
||||||
|
- Screen .rendered.md: ${qualityPreset === 'production' ? 'Publication-ready' : qualityPreset === 'detailed' ? 'High quality' : qualityPreset === 'balanced' ? 'Good quality' : 'Basic quality'}
|
||||||
|
- States included: ${statesCount} states per component
|
||||||
|
- Accessibility: ${qualityPreset === 'production' ? 'Complete' : qualityPreset === 'detailed' ? 'Good' : 'Basic'}
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Review rendered screen:**
|
||||||
|
\`cat ./fluxwing/screens/${screenName}.rendered.md\`
|
||||||
|
|
||||||
|
2. **Further enhancement (optional):**
|
||||||
|
${qualityPreset !== 'production' ? `Use /fluxwing-enhance to upgrade to ${nextFidelity} fidelity` : 'Already at maximum fidelity'}
|
||||||
|
|
||||||
|
3. **Customize components:**
|
||||||
|
Edit ./fluxwing/components/ files as needed
|
||||||
|
|
||||||
|
4. **View components:**
|
||||||
|
Use /fluxwing-view to inspect individual components
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parallel Execution Strategy
|
||||||
|
|
||||||
|
**Two-phase concurrency model:**
|
||||||
|
|
||||||
|
**Phase 1 - Component Creation (Parallel)**:
|
||||||
|
- If N components are missing → spawn N agents in parallel (one per component)
|
||||||
|
- All component agents run simultaneously (~Nx faster than sequential)
|
||||||
|
- Each agent independently creates its component files and tracks progress
|
||||||
|
|
||||||
|
**Phase 2 - Screen Composition (Sequential)**:
|
||||||
|
- After ALL component agents complete → spawn composer agent
|
||||||
|
- Composer reads all created components and assembles screen
|
||||||
|
- Cannot run in parallel with Phase 1 (needs components to exist first)
|
||||||
|
|
||||||
|
**Performance Example**:
|
||||||
|
- 6 missing components + screen composition
|
||||||
|
- Old way (sequential): ~180 seconds (6 × 30s)
|
||||||
|
- New way (parallel Phase 1): ~30 seconds (6 agents at once) + ~30s composer = ~60 seconds total
|
||||||
|
- **3x faster!**
|
||||||
|
|
||||||
|
## Example Interaction
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Create a login screen
|
||||||
|
|
||||||
|
Skill: I'll help you create a login screen! Let me check what components we have...
|
||||||
|
|
||||||
|
[Checks ./fluxwing/components/, ./fluxwing/library/, bundled templates]
|
||||||
|
|
||||||
|
I found:
|
||||||
|
✓ email-input (exists in ./fluxwing/components/)
|
||||||
|
✗ password-input (needs to be created)
|
||||||
|
✗ submit-button (needs to be created)
|
||||||
|
|
||||||
|
I'll use a two-phase approach:
|
||||||
|
- Phase 1: Spawn 2 agents in parallel (one per missing component)
|
||||||
|
- Phase 2: After components complete, spawn composer agent
|
||||||
|
|
||||||
|
[Spawns 2 component agents in parallel]
|
||||||
|
|
||||||
|
✓ password-input agent completed
|
||||||
|
✓ submit-button agent completed
|
||||||
|
|
||||||
|
[Now spawns composer agent]
|
||||||
|
|
||||||
|
✓ Composer agent created login screen
|
||||||
|
|
||||||
|
Total: 7 files created in ./fluxwing/
|
||||||
|
Performance: ~2x faster with parallel component creation!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**If designer agent fails:**
|
||||||
|
- Report which components failed
|
||||||
|
- User can create manually or retry
|
||||||
|
- Composer agent cannot proceed without components
|
||||||
|
|
||||||
|
**If composer agent fails:**
|
||||||
|
- Components are still created and usable
|
||||||
|
- User can manually compose screen
|
||||||
|
- Provide specific error context
|
||||||
|
|
||||||
|
## Common Rationalizations That Mean You're Failing
|
||||||
|
|
||||||
|
If you catch yourself thinking ANY of these thoughts, STOP. You are rationalizing. Use the Task tool to spawn agents instead.
|
||||||
|
|
||||||
|
**About creating files directly:**
|
||||||
|
- "I'll just create the files directly, it's faster" → WRONG. Spawn agents.
|
||||||
|
- "Write is simpler than Task for this" → WRONG. You're an orchestrator, not a worker.
|
||||||
|
- "I'll create .uxm and .md myself, agents can do .rendered later" → WRONG. Agents do ALL 3 files.
|
||||||
|
- "Let me help by pre-creating some files first" → WRONG. Agents are self-sufficient.
|
||||||
|
- "The user wants it fast, no time for agents" → WRONG. Parallel agents ARE faster.
|
||||||
|
|
||||||
|
**About multi-screen scenarios:**
|
||||||
|
- "Orchestration is overkill for this batch" → WRONG. Batches are exactly when you orchestrate.
|
||||||
|
- "I'll do the first screen to show progress" → WRONG. Spawn all agents in parallel.
|
||||||
|
- "Let me create a todo list and work through screens" → WRONG. TodoWrite + sequential work = slowest path.
|
||||||
|
- "11 screens is too many for parallel agents" → WRONG. That's the perfect use case.
|
||||||
|
|
||||||
|
**About the .rendered.md file:**
|
||||||
|
- "I'll skip .rendered.md for now" → WRONG. It's the PRIMARY deliverable.
|
||||||
|
- "Template (.md) is enough" → WRONG. User needs to SEE the actual layout.
|
||||||
|
- ".rendered.md can be added later" → WRONG. Composer creates it NOW.
|
||||||
|
|
||||||
|
**Why these rationalizations happen:**
|
||||||
|
- Write/Edit tools feel more direct than Task tool
|
||||||
|
- Creating files yourself feels productive
|
||||||
|
- Spawning agents feels abstract
|
||||||
|
- But: You're an ORCHESTRATOR. Your job is coordination, not execution.
|
||||||
|
|
||||||
|
**What to do instead:**
|
||||||
|
1. Detect how many screens are needed
|
||||||
|
2. Spawn that many composer agents in ONE message
|
||||||
|
3. Wait for results
|
||||||
|
4. Report completion
|
||||||
|
|
||||||
|
STOP. Use the Task tool. You are an orchestrator, not a worker.
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- ✓ All required components exist (created or found)
|
||||||
|
- ✓ Screen has 3 files (.uxm + .md + .rendered.md)
|
||||||
|
- ✓ Agents ran efficiently (parallel when possible)
|
||||||
|
- ✓ User can immediately use the screen design
|
||||||
|
|
||||||
|
You are building complete, production-ready screen designs with maximum agent concurrency!
|
||||||
319
skills/fluxwing-screen-scaffolder/docs/04-screen-composition.md
Normal file
319
skills/fluxwing-screen-scaffolder/docs/04-screen-composition.md
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
# Screen Composition - Building Complete UIs
|
||||||
|
|
||||||
|
How to compose complete screens from existing components.
|
||||||
|
|
||||||
|
## What is a Screen?
|
||||||
|
|
||||||
|
A **screen** is a complete page or view composed of multiple components. Unlike individual components, screens:
|
||||||
|
- Reference existing components by ID
|
||||||
|
- Define layout and positioning
|
||||||
|
- Show complete user flows
|
||||||
|
- Include rendered examples with real data
|
||||||
|
|
||||||
|
## The Three-File System for Screens
|
||||||
|
|
||||||
|
Every screen needs THREE files:
|
||||||
|
|
||||||
|
### 1. `screen-name.uxm` (Metadata)
|
||||||
|
Defines which components are used and how they're arranged
|
||||||
|
|
||||||
|
### 2. `screen-name.md` (Template)
|
||||||
|
Shows the layout with {{variables}} for dynamic content
|
||||||
|
|
||||||
|
### 3. `screen-name.rendered.md` (Example)
|
||||||
|
**Critical**: Shows the screen with REAL data (not variables)
|
||||||
|
|
||||||
|
## Creating a Screen
|
||||||
|
|
||||||
|
### Step 1: Inventory Components
|
||||||
|
|
||||||
|
Before composing, list what's available:
|
||||||
|
- Check `./fluxwing/components/` for user-created components
|
||||||
|
- Check `./fluxwing/library/` for customized templates
|
||||||
|
- Check `../examples/` for bundled components
|
||||||
|
|
||||||
|
**If missing components**, create them first with `/fluxwing-create`
|
||||||
|
|
||||||
|
### Step 2: Design the Layout
|
||||||
|
|
||||||
|
Choose a layout pattern (see patterns below) and plan:
|
||||||
|
- Component placement
|
||||||
|
- Visual hierarchy
|
||||||
|
- Spacing and alignment
|
||||||
|
- User flow
|
||||||
|
|
||||||
|
### Step 3: Create screen-name.uxm
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "login-screen",
|
||||||
|
"type": "container",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Login Screen",
|
||||||
|
"description": "User authentication screen",
|
||||||
|
"created": "2024-10-11T12:00:00Z",
|
||||||
|
"modified": "2024-10-11T12:00:00Z",
|
||||||
|
"tags": ["auth", "login", "screen"],
|
||||||
|
"category": "display"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "Welcome Back",
|
||||||
|
"components": [
|
||||||
|
"email-input",
|
||||||
|
"password-input",
|
||||||
|
"submit-button",
|
||||||
|
"error-alert"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "flex",
|
||||||
|
"positioning": "relative",
|
||||||
|
"spacing": {
|
||||||
|
"padding": 16
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{"name": "idle", "properties": {}},
|
||||||
|
{"name": "loading", "properties": {"showSpinner": true}},
|
||||||
|
{"name": "error", "properties": {"showError": true}}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "main",
|
||||||
|
"ariaLabel": "Login form"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "login-screen.md",
|
||||||
|
"width": 40,
|
||||||
|
"height": 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Create screen-name.md (Template)
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
# Login Screen
|
||||||
|
|
||||||
|
User authentication screen with email/password.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
╭────────────────────────────────────────╮
|
||||||
|
│ {{title}} │
|
||||||
|
├────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Email │
|
||||||
|
│ {{emailInput}} │
|
||||||
|
│ │
|
||||||
|
│ Password │
|
||||||
|
│ {{passwordInput}} │
|
||||||
|
│ │
|
||||||
|
│ {{error}} │
|
||||||
|
│ │
|
||||||
|
│ {{submitButton}} │
|
||||||
|
│ │
|
||||||
|
╰────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components Used
|
||||||
|
|
||||||
|
- `email-input`: Email address field
|
||||||
|
- `password-input`: Password field with show/hide
|
||||||
|
- `submit-button`: Form submission
|
||||||
|
- `error-alert`: Error messages
|
||||||
|
|
||||||
|
## States
|
||||||
|
|
||||||
|
### Idle (default)
|
||||||
|
User has not yet interacted
|
||||||
|
|
||||||
|
### Loading
|
||||||
|
While authenticating
|
||||||
|
|
||||||
|
### Error
|
||||||
|
When authentication fails
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `title` (string): Screen heading
|
||||||
|
- `emailInput` (component): Email input component
|
||||||
|
- `passwordInput` (component): Password input component
|
||||||
|
- `submitButton` (component): Submit button
|
||||||
|
- `error` (component): Error alert (hidden by default)
|
||||||
|
````
|
||||||
|
|
||||||
|
### Step 5: Create screen-name.rendered.md (REAL DATA)
|
||||||
|
|
||||||
|
**This is critical** - show actual rendered output:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Login Screen - Rendered Example
|
||||||
|
|
||||||
|
This shows the actual screen with example data.
|
||||||
|
|
||||||
|
╭────────────────────────────────────────╮
|
||||||
|
│ Welcome Back │
|
||||||
|
├────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Email │
|
||||||
|
│ ┌──────────────────────────────────┐ │
|
||||||
|
│ │ john@example.com │ │
|
||||||
|
│ └──────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Password │
|
||||||
|
│ ┌──────────────────────────────────┐ │
|
||||||
|
│ │ •••••••• │👁│
|
||||||
|
│ └──────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ╭──────────────────────────────────╮ │
|
||||||
|
│ │ Sign In │ │
|
||||||
|
│ ╰──────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
╰────────────────────────────────────────╯
|
||||||
|
|
||||||
|
## Data Context
|
||||||
|
|
||||||
|
- Email: john@example.com (valid email)
|
||||||
|
- Password: ******** (8 characters, masked)
|
||||||
|
- State: Idle (ready for submission)
|
||||||
|
|
||||||
|
## User Flow
|
||||||
|
|
||||||
|
1. User enters email address
|
||||||
|
2. User enters password
|
||||||
|
3. Optional: Toggle password visibility with 👁 icon
|
||||||
|
4. Click "Sign In" button
|
||||||
|
5. If error: Alert shows above button
|
||||||
|
6. If success: Navigate to dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why this matters**: Other agents can see the actual intended output, not just templates with {{variables}}.
|
||||||
|
|
||||||
|
## Common Layout Patterns
|
||||||
|
|
||||||
|
### Pattern 1: Vertical Stack (Login/Signup)
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│ [Logo/Header] │
|
||||||
|
├──────────────────┤
|
||||||
|
│ [Form Fields] │
|
||||||
|
│ [Input 1] │
|
||||||
|
│ [Input 2] │
|
||||||
|
│ [Input 3] │
|
||||||
|
├──────────────────┤
|
||||||
|
│ [Submit Button] │
|
||||||
|
├──────────────────┤
|
||||||
|
│ [Footer Links] │
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 2: Sidebar + Main (Dashboard)
|
||||||
|
```
|
||||||
|
╭──────╮╭──────────────────────────╮
|
||||||
|
│ ││ │
|
||||||
|
│ Nav ││ Main Content │
|
||||||
|
│ ││ │
|
||||||
|
│ Menu ││ [Components Grid] │
|
||||||
|
│ ││ │
|
||||||
|
│ Items││ [Data/Charts/Cards] │
|
||||||
|
│ ││ │
|
||||||
|
╰──────╯╰──────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 3: Tabbed Content (Settings)
|
||||||
|
```
|
||||||
|
╭────────────────────────────────────╮
|
||||||
|
│ Settings [Save] │
|
||||||
|
├────────────────────────────────────┤
|
||||||
|
│ [Tab1] [Tab2] [Tab3] [Tab4] │
|
||||||
|
├────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ [Tab Content - Form Fields] │
|
||||||
|
│ │
|
||||||
|
╰────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 4: Grid Layout (Gallery/Cards)
|
||||||
|
```
|
||||||
|
╭───────────╮ ╭───────────╮ ╭───────────╮
|
||||||
|
│ Card 1 │ │ Card 2 │ │ Card 3 │
|
||||||
|
╰───────────╯ ╰───────────╯ ╰───────────╯
|
||||||
|
╭───────────╮ ╭───────────╮ ╭───────────╮
|
||||||
|
│ Card 4 │ │ Card 5 │ │ Card 6 │
|
||||||
|
╰───────────╯ ╰───────────╯ ╰───────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 5: List/Table (Data View)
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────╮
|
||||||
|
│ [Search] [Filters] [Actions] │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ Header1 │ Header2 │ Header3 │ Action │
|
||||||
|
├─────────┼─────────┼─────────┼────────┤
|
||||||
|
│ Data │ Data │ Data │ [•] │
|
||||||
|
│ Data │ Data │ Data │ [•] │
|
||||||
|
│ Data │ Data │ Data │ [•] │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ [Pagination] │
|
||||||
|
╰──────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Responsive Considerations
|
||||||
|
|
||||||
|
Show mobile and desktop layouts when needed:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
## Desktop Layout (> 768px)
|
||||||
|
|
||||||
|
```
|
||||||
|
╭────╮╭────────────────────────────╮
|
||||||
|
│Nav ││ Main Content │
|
||||||
|
╰────╯╰────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mobile Layout (<= 768px)
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────╮
|
||||||
|
│ [☰] Header │
|
||||||
|
├──────────────┤
|
||||||
|
│ Main Content │
|
||||||
|
╰──────────────╯
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
## Tips for Great Screens
|
||||||
|
|
||||||
|
1. **Start with wireframe**: Sketch layout before coding
|
||||||
|
2. **Reuse components**: Don't recreate what exists
|
||||||
|
3. **Consistent spacing**: Use multiples of 4 characters
|
||||||
|
4. **Visual hierarchy**: Important items prominent
|
||||||
|
5. **Real examples**: Always create .rendered.md with actual data
|
||||||
|
6. **Document flows**: Explain what users can do
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**"Component not found"**
|
||||||
|
- Check component ID spelling (case-sensitive)
|
||||||
|
- Ensure component file exists in ./fluxwing/components/
|
||||||
|
|
||||||
|
**"Layout looks misaligned"**
|
||||||
|
- Use monospace font
|
||||||
|
- Check for consistent box-drawing characters
|
||||||
|
- Ensure spacing is uniform
|
||||||
|
|
||||||
|
**"Too complex"**
|
||||||
|
- Break into smaller sections
|
||||||
|
- Consider nested layouts
|
||||||
|
- Use clear visual dividers
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- **View library**: Use `/fluxwing-library` to see all screens
|
||||||
|
- **Create more**: Build additional screens for your app
|
||||||
|
|
||||||
|
You can now compose complete, production-ready screen designs!
|
||||||
68
skills/fluxwing-screen-scaffolder/templates/dashboard.md
Normal file
68
skills/fluxwing-screen-scaffolder/templates/dashboard.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Dashboard
|
||||||
|
|
||||||
|
Main application dashboard with key metrics and recent activity.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ {{title}} {{userName}} ▼ │
|
||||||
|
├──────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{greeting}} │
|
||||||
|
│ │
|
||||||
|
│ ╭───────────────────╮ ╭───────────────────╮ ╭──────────────────────╮ │
|
||||||
|
│ │ {{metricCard1}} │ │ {{metricCard2}} │ │ {{metricCard3}} │ │
|
||||||
|
│ ╰───────────────────╯ ╰───────────────────╯ ╰──────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ {{recentActivityTitle}} │
|
||||||
|
│ ╭────────────────────────────────────────────────────────────────────────╮ │
|
||||||
|
│ │ {{activityList}} │ │
|
||||||
|
│ ╰────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components Used
|
||||||
|
|
||||||
|
- **navigation**: Top navigation bar with user menu
|
||||||
|
- **card**: Metric cards for key statistics (3x)
|
||||||
|
- **badge**: Status indicators and trend arrows
|
||||||
|
- **list**: Recent activity feed
|
||||||
|
|
||||||
|
## States
|
||||||
|
|
||||||
|
### Loaded State
|
||||||
|
Dashboard fully loaded with all data displayed.
|
||||||
|
|
||||||
|
### Loading State
|
||||||
|
While fetching dashboard data - shows spinner.
|
||||||
|
|
||||||
|
### Error State
|
||||||
|
When data fetch fails - shows error message.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `title` (string): Dashboard title
|
||||||
|
- `userName` (string): Current user's name
|
||||||
|
- `greeting` (string): Personalized greeting
|
||||||
|
- `metricCard1` (component): First metric card (e.g., Revenue)
|
||||||
|
- `metricCard2` (component): Second metric card (e.g., Users)
|
||||||
|
- `metricCard3` (component): Third metric card (e.g., Growth)
|
||||||
|
- `recentActivityTitle` (string): Activity section header
|
||||||
|
- `activityList` (component): List of recent activities
|
||||||
|
|
||||||
|
## User Flows
|
||||||
|
|
||||||
|
1. User logs in and lands on dashboard
|
||||||
|
2. Dashboard loads metrics and activity data
|
||||||
|
3. User can click metric cards for detailed views
|
||||||
|
4. User can interact with activity items
|
||||||
|
5. User can access navigation menu via dropdown
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: main
|
||||||
|
- **ARIA Label**: "Dashboard main content"
|
||||||
|
- **Keyboard**: Tab navigation through interactive elements
|
||||||
|
- **Screen Reader**: All metrics and activities properly labeled
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
# Dashboard - Rendered Example
|
||||||
|
|
||||||
|
This shows the actual dashboard with realistic metrics and activity data.
|
||||||
|
|
||||||
|
## Loaded State (Default)
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Dashboard Sarah Johnson ▼ │
|
||||||
|
├──────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Welcome back, Sarah! Here's what's happening today. │
|
||||||
|
│ │
|
||||||
|
│ ╭───────────────────╮ ╭───────────────────╮ ╭──────────────────────╮ │
|
||||||
|
│ │ Revenue │ │ Active Users │ │ Growth │ │
|
||||||
|
│ │ ───────────────── │ │ ───────────────── │ │ ─────────────────── │ │
|
||||||
|
│ │ $24,567 │ │ 1,234 │ │ +12.5% │ │
|
||||||
|
│ │ +8.3% ↗ │ │ +45 today │ │ MoM │ │
|
||||||
|
│ ╰───────────────────╯ ╰───────────────────╯ ╰──────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ Recent Activity │
|
||||||
|
│ ╭────────────────────────────────────────────────────────────────────────╮ │
|
||||||
|
│ │ • John Doe signed up 2 minutes ago │ │
|
||||||
|
│ │ • New order #1234 received 5 minutes ago │ │
|
||||||
|
│ │ • Sarah Johnson updated profile 8 minutes ago │ │
|
||||||
|
│ │ • Mike Smith completed onboarding 15 minutes ago │ │
|
||||||
|
│ │ • System backup completed successfully 22 minutes ago │ │
|
||||||
|
│ ╰────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading State
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Dashboard Sarah Johnson ▼ │
|
||||||
|
├──────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ⠋ Loading dashboard... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Context
|
||||||
|
|
||||||
|
This example demonstrates:
|
||||||
|
|
||||||
|
### User Information
|
||||||
|
- **User**: Sarah Johnson (logged in)
|
||||||
|
- **Time**: October 11, 2024, 3:45 PM
|
||||||
|
- **Greeting**: Personalized with name and time-appropriate message
|
||||||
|
|
||||||
|
### Metric Cards
|
||||||
|
|
||||||
|
**Card 1: Revenue**
|
||||||
|
- Value: $24,567 (monthly revenue)
|
||||||
|
- Trend: +8.3% ↗ (positive growth from last month)
|
||||||
|
- Visual: Green trend indicator
|
||||||
|
|
||||||
|
**Card 2: Active Users**
|
||||||
|
- Value: 1,234 (total active users)
|
||||||
|
- Change: +45 today (new users today)
|
||||||
|
- Visual: User count with daily change
|
||||||
|
|
||||||
|
**Card 3: Growth**
|
||||||
|
- Value: +12.5% (percentage growth)
|
||||||
|
- Period: MoM (month over month)
|
||||||
|
- Visual: Growth percentage
|
||||||
|
|
||||||
|
### Recent Activity
|
||||||
|
|
||||||
|
Real activity entries with timestamps:
|
||||||
|
1. **John Doe signed up** - 2 minutes ago
|
||||||
|
2. **New order #1234 received** - 5 minutes ago
|
||||||
|
3. **Sarah Johnson updated profile** - 8 minutes ago
|
||||||
|
4. **Mike Smith completed onboarding** - 15 minutes ago
|
||||||
|
5. **System backup completed successfully** - 22 minutes ago
|
||||||
|
|
||||||
|
## User Flow Demonstration
|
||||||
|
|
||||||
|
### Step 1: Login Complete
|
||||||
|
User successfully authenticates
|
||||||
|
→ Dashboard loads
|
||||||
|
|
||||||
|
### Step 2: Data Fetch
|
||||||
|
System retrieves:
|
||||||
|
- Latest metrics
|
||||||
|
- Recent activity
|
||||||
|
- User preferences
|
||||||
|
|
||||||
|
### Step 3: Display
|
||||||
|
Dashboard renders with:
|
||||||
|
- Personalized greeting
|
||||||
|
- Current metrics with trends
|
||||||
|
- Real-time activity feed
|
||||||
|
|
||||||
|
### Step 4: Interaction
|
||||||
|
User can:
|
||||||
|
- Click metric cards for details
|
||||||
|
- Click activity items for context
|
||||||
|
- Access user menu via dropdown
|
||||||
|
- Navigate to other sections
|
||||||
|
|
||||||
|
## Components Breakdown
|
||||||
|
|
||||||
|
This screen composition includes:
|
||||||
|
|
||||||
|
1. **Top Bar**
|
||||||
|
- Dashboard title (left)
|
||||||
|
- User dropdown menu (right): "Sarah Johnson ▼"
|
||||||
|
|
||||||
|
2. **Greeting Section**
|
||||||
|
- Personalized message: "Welcome back, Sarah!"
|
||||||
|
- Context: "Here's what's happening today."
|
||||||
|
|
||||||
|
3. **Metric Cards (3x)** (component: card)
|
||||||
|
- **Revenue Card**
|
||||||
|
- Title: "Revenue"
|
||||||
|
- Value: "$24,567"
|
||||||
|
- Trend: "+8.3% ↗" (badge with arrow)
|
||||||
|
|
||||||
|
- **Users Card**
|
||||||
|
- Title: "Active Users"
|
||||||
|
- Value: "1,234"
|
||||||
|
- Change: "+45 today"
|
||||||
|
|
||||||
|
- **Growth Card**
|
||||||
|
- Title: "Growth"
|
||||||
|
- Value: "+12.5%"
|
||||||
|
- Period: "MoM"
|
||||||
|
|
||||||
|
4. **Activity Section**
|
||||||
|
- Header: "Recent Activity"
|
||||||
|
- List (component: list)
|
||||||
|
- 5 recent items
|
||||||
|
- Each with description and timestamp
|
||||||
|
- Bullet formatting for clarity
|
||||||
|
|
||||||
|
## Design Notes
|
||||||
|
|
||||||
|
- **Information Hierarchy**: Most important metrics (revenue) comes first
|
||||||
|
- **Visual Grouping**: Cards use consistent styling and spacing
|
||||||
|
- **Real-time Feel**: Recent timestamps show system is live
|
||||||
|
- **Scanability**: Bullet points and clear labels make scanning easy
|
||||||
|
- **Trend Indicators**: Arrows (↗) and percentages show direction quickly
|
||||||
|
- **Whitespace**: Generous padding prevents cramped feeling
|
||||||
|
|
||||||
|
## Trend Indicators Explained
|
||||||
|
|
||||||
|
- **↗** (up-right arrow): Positive trend, growth
|
||||||
|
- **→** (right arrow): Flat, no change
|
||||||
|
- **↘** (down-right arrow): Negative trend, decline
|
||||||
|
- **+X%**: Positive percentage change (green context)
|
||||||
|
- **-X%**: Negative percentage change (red context)
|
||||||
|
|
||||||
|
## Time-based Data
|
||||||
|
|
||||||
|
All timestamps are relative:
|
||||||
|
- "2 minutes ago" - Very recent
|
||||||
|
- "5 minutes ago" - Recent
|
||||||
|
- "15 minutes ago" - Somewhat recent
|
||||||
|
- "22 minutes ago" - Less recent
|
||||||
|
|
||||||
|
This creates a sense of real-time activity.
|
||||||
|
|
||||||
|
## Accessibility Features
|
||||||
|
|
||||||
|
- Clear heading hierarchy (Dashboard → sections)
|
||||||
|
- Metric values announced with context
|
||||||
|
- Activity items in semantic list
|
||||||
|
- Keyboard navigation: Tab through cards and items
|
||||||
|
- Screen reader: Trends and timestamps properly announced
|
||||||
|
- High contrast for trend indicators
|
||||||
|
|
||||||
|
## State Transitions
|
||||||
|
|
||||||
|
1. **Initial Load** → Loading spinner shown
|
||||||
|
2. **Data Fetched** → Transition to loaded state
|
||||||
|
3. **Error Occurs** → Show error message, retry button
|
||||||
|
4. **User Clicks Card** → Navigate to detail view
|
||||||
|
5. **New Activity** → Update activity list (real-time)
|
||||||
|
|
||||||
|
This rendered example shows exactly how a production dashboard appears with real data!
|
||||||
66
skills/fluxwing-screen-scaffolder/templates/dashboard.uxm
Normal file
66
skills/fluxwing-screen-scaffolder/templates/dashboard.uxm
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"id": "dashboard",
|
||||||
|
"type": "container",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Dashboard",
|
||||||
|
"description": "Main application dashboard with metrics and activity feed",
|
||||||
|
"author": "Fluxwing Team",
|
||||||
|
"created": "2024-10-11T12:00:00Z",
|
||||||
|
"modified": "2024-10-11T12:00:00Z",
|
||||||
|
"tags": ["dashboard", "screen", "metrics", "analytics"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "Dashboard",
|
||||||
|
"userName": "User",
|
||||||
|
"components": [
|
||||||
|
"navigation",
|
||||||
|
"card",
|
||||||
|
"badge",
|
||||||
|
"list"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "grid",
|
||||||
|
"positioning": "relative",
|
||||||
|
"spacing": {
|
||||||
|
"padding": 16,
|
||||||
|
"margin": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "loaded",
|
||||||
|
"properties": {
|
||||||
|
"showContent": true,
|
||||||
|
"loading": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "loading",
|
||||||
|
"properties": {
|
||||||
|
"showSpinner": true,
|
||||||
|
"hideContent": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "error",
|
||||||
|
"properties": {
|
||||||
|
"showError": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "main",
|
||||||
|
"ariaLabel": "Dashboard main content"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "dashboard.md",
|
||||||
|
"width": 80,
|
||||||
|
"height": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
71
skills/fluxwing-screen-scaffolder/templates/login-screen.md
Normal file
71
skills/fluxwing-screen-scaffolder/templates/login-screen.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Login Screen
|
||||||
|
|
||||||
|
User authentication screen with email and password inputs.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ {{title}} │
|
||||||
|
│ {{subtitle}} │
|
||||||
|
│ │
|
||||||
|
│ Email │
|
||||||
|
│ {{emailInput}} │
|
||||||
|
│ │
|
||||||
|
│ Password │
|
||||||
|
│ {{passwordInput}} │
|
||||||
|
│ │
|
||||||
|
│ {{errorMessage}} │
|
||||||
|
│ │
|
||||||
|
│ {{submitButton}} │
|
||||||
|
│ │
|
||||||
|
│ {{forgotPassword}} │ {{signupLink}} │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components Used
|
||||||
|
|
||||||
|
- **email-input**: Email address field with validation
|
||||||
|
- **password-input**: Password field with show/hide toggle (Note: using email-input as placeholder)
|
||||||
|
- **submit-button**: Form submission button (using primary-button)
|
||||||
|
- **error-alert**: Error message display (optional)
|
||||||
|
|
||||||
|
## States
|
||||||
|
|
||||||
|
### Idle State
|
||||||
|
User has not yet interacted with the form.
|
||||||
|
|
||||||
|
### Loading State
|
||||||
|
While authenticating user credentials.
|
||||||
|
|
||||||
|
### Error State
|
||||||
|
When authentication fails - shows error message above submit button.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `title` (string): Screen heading text
|
||||||
|
- `subtitle` (string): Subheading text
|
||||||
|
- `emailInput` (component): Email input component
|
||||||
|
- `passwordInput` (component): Password input component
|
||||||
|
- `submitButton` (component): Submit button component
|
||||||
|
- `errorMessage` (component): Error alert component
|
||||||
|
- `forgotPassword` (link): Forgot password link
|
||||||
|
- `signupLink` (link): Sign up link
|
||||||
|
|
||||||
|
## User Flows
|
||||||
|
|
||||||
|
1. User enters email address
|
||||||
|
2. User enters password
|
||||||
|
3. User clicks "Sign In" button
|
||||||
|
4. System validates credentials
|
||||||
|
5. If valid: Navigate to dashboard
|
||||||
|
6. If invalid: Show error message, allow retry
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: main
|
||||||
|
- **ARIA Label**: "Login form"
|
||||||
|
- **Keyboard**: Tab navigation between fields, Enter to submit
|
||||||
|
- **Screen Reader**: All fields properly labeled
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
# Login Screen - Rendered Example
|
||||||
|
|
||||||
|
This shows the actual login screen with realistic example data.
|
||||||
|
|
||||||
|
## Default State (Idle)
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Welcome Back │
|
||||||
|
│ Sign in to continue │
|
||||||
|
│ │
|
||||||
|
│ Email │
|
||||||
|
│ ┌────────────────────────────────────┐ │
|
||||||
|
│ │ sarah.johnson@example.com │ │
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Password │
|
||||||
|
│ ┌────────────────────────────────────┐ │
|
||||||
|
│ │ •••••••• │👁│
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭──────────────────────────────────╮ │
|
||||||
|
│ │ Sign In │ │
|
||||||
|
│ ╰──────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ Forgot password? │ Create account │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading State
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Welcome Back │
|
||||||
|
│ Sign in to continue │
|
||||||
|
│ │
|
||||||
|
│ Email │
|
||||||
|
│ ┌────────────────────────────────────┐ │
|
||||||
|
│ │ sarah.johnson@example.com │ │
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Password │
|
||||||
|
│ ┌────────────────────────────────────┐ │
|
||||||
|
│ │ •••••••• │👁│
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭──────────────────────────────────╮ │
|
||||||
|
│ │ ⠋ Signing in... │ │
|
||||||
|
│ ╰──────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ Forgot password? │ Create account │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error State
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ Welcome Back │
|
||||||
|
│ Sign in to continue │
|
||||||
|
│ │
|
||||||
|
│ Email │
|
||||||
|
│ ┌────────────────────────────────────┐ │
|
||||||
|
│ │ sarah.johnson@example.com │ │
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Password │
|
||||||
|
│ ┌────────────────────────────────────┐ │
|
||||||
|
│ │ •••••••• │👁│
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ╭────────────────────────────────────╮ │
|
||||||
|
│ │ ❌ Invalid email or password │ │
|
||||||
|
│ ╰────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ ╭──────────────────────────────────╮ │
|
||||||
|
│ │ Sign In │ │
|
||||||
|
│ ╰──────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ Forgot password? │ Create account │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Context
|
||||||
|
|
||||||
|
This example demonstrates:
|
||||||
|
|
||||||
|
### User Input
|
||||||
|
- **Email**: sarah.johnson@example.com (valid format)
|
||||||
|
- **Password**: ******** (8 characters, masked for security)
|
||||||
|
|
||||||
|
### Screen Elements
|
||||||
|
- **Title**: "Welcome Back"
|
||||||
|
- **Subtitle**: "Sign in to continue"
|
||||||
|
- **Submit Button**: "Sign In" (primary action)
|
||||||
|
- **Password Toggle**: 👁 icon to show/hide password
|
||||||
|
- **Footer Links**: "Forgot password?" and "Create account"
|
||||||
|
|
||||||
|
### States Shown
|
||||||
|
1. **Idle**: Clean form ready for input
|
||||||
|
2. **Loading**: Spinner animation while authenticating
|
||||||
|
3. **Error**: Red error message showing failed authentication
|
||||||
|
|
||||||
|
## User Flow Demonstration
|
||||||
|
|
||||||
|
### Step 1: Initial State
|
||||||
|
User sees empty form with placeholder text
|
||||||
|
|
||||||
|
### Step 2: Input Data
|
||||||
|
User enters:
|
||||||
|
- Email: sarah.johnson@example.com
|
||||||
|
- Password: ••••••••
|
||||||
|
|
||||||
|
### Step 3: Submit
|
||||||
|
User clicks "Sign In" button
|
||||||
|
→ Form enters loading state
|
||||||
|
→ Shows spinner: "⠋ Signing in..."
|
||||||
|
|
||||||
|
### Step 4a: Success Path
|
||||||
|
✓ Credentials valid
|
||||||
|
→ Navigate to dashboard
|
||||||
|
|
||||||
|
### Step 4b: Error Path
|
||||||
|
✗ Credentials invalid
|
||||||
|
→ Show error: "❌ Invalid email or password"
|
||||||
|
→ Keep form populated for retry
|
||||||
|
→ Focus returns to password field
|
||||||
|
|
||||||
|
## Components Breakdown
|
||||||
|
|
||||||
|
This screen composition includes:
|
||||||
|
|
||||||
|
1. **Container** (outer box)
|
||||||
|
- Rounded corners for friendly feel
|
||||||
|
- Centered layout with padding
|
||||||
|
|
||||||
|
2. **Email Input** (component: email-input)
|
||||||
|
- Text field with email validation
|
||||||
|
- Shows user input: sarah.johnson@example.com
|
||||||
|
|
||||||
|
3. **Password Input** (adapted from email-input)
|
||||||
|
- Masked text field
|
||||||
|
- Show/hide toggle (👁 icon)
|
||||||
|
- Input: ••••••••
|
||||||
|
|
||||||
|
4. **Submit Button** (component: primary-button)
|
||||||
|
- Primary action styling
|
||||||
|
- Text: "Sign In"
|
||||||
|
- Loading state with spinner
|
||||||
|
|
||||||
|
5. **Error Alert** (conditional)
|
||||||
|
- Only visible in error state
|
||||||
|
- Red styling with ❌ icon
|
||||||
|
- Message: "Invalid email or password"
|
||||||
|
|
||||||
|
6. **Footer Links** (text elements)
|
||||||
|
- "Forgot password?" (clickable)
|
||||||
|
- "Create account" (clickable)
|
||||||
|
|
||||||
|
## Design Notes
|
||||||
|
|
||||||
|
- Clean, minimal design reduces cognitive load
|
||||||
|
- Clear visual hierarchy (title → inputs → action)
|
||||||
|
- Helpful error messaging guides user to fix issues
|
||||||
|
- Password masking with optional reveal improves security
|
||||||
|
- Footer links provide escape hatches for common scenarios
|
||||||
|
|
||||||
|
## Accessibility Features
|
||||||
|
|
||||||
|
- Proper heading hierarchy (h1 for title)
|
||||||
|
- Form labels associated with inputs
|
||||||
|
- Error messages announced to screen readers
|
||||||
|
- Keyboard navigation: Tab between fields, Enter to submit
|
||||||
|
- Focus indicators visible on all interactive elements
|
||||||
|
- High contrast text for readability
|
||||||
|
|
||||||
|
This rendered example shows exactly how the login screen appears and behaves in practice!
|
||||||
72
skills/fluxwing-screen-scaffolder/templates/login-screen.uxm
Normal file
72
skills/fluxwing-screen-scaffolder/templates/login-screen.uxm
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"id": "login-screen",
|
||||||
|
"type": "container",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Login Screen",
|
||||||
|
"description": "User authentication screen with email and password inputs",
|
||||||
|
"author": "Fluxwing Team",
|
||||||
|
"created": "2024-10-11T12:00:00Z",
|
||||||
|
"modified": "2024-10-11T12:00:00Z",
|
||||||
|
"tags": ["auth", "login", "screen", "form"],
|
||||||
|
"category": "display",
|
||||||
|
"fidelity": "detailed"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "Welcome Back",
|
||||||
|
"subtitle": "Sign in to continue",
|
||||||
|
"components": [
|
||||||
|
"email-input",
|
||||||
|
"primary-button"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "flex",
|
||||||
|
"positioning": "relative",
|
||||||
|
"spacing": {
|
||||||
|
"padding": 16,
|
||||||
|
"margin": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "idle",
|
||||||
|
"properties": {
|
||||||
|
"showError": false,
|
||||||
|
"loading": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "loading",
|
||||||
|
"properties": {
|
||||||
|
"showSpinner": true,
|
||||||
|
"disableInputs": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "error",
|
||||||
|
"properties": {
|
||||||
|
"showError": true,
|
||||||
|
"errorMessage": "Invalid credentials"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"trigger": "submit",
|
||||||
|
"action": "authenticate-user"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "main",
|
||||||
|
"ariaLabel": "Login form",
|
||||||
|
"focusable": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "login-screen.md",
|
||||||
|
"width": 44,
|
||||||
|
"height": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
461
skills/fluxwing-screenshot-importer/SKILL.md
Normal file
461
skills/fluxwing-screenshot-importer/SKILL.md
Normal file
@@ -0,0 +1,461 @@
|
|||||||
|
---
|
||||||
|
name: Fluxwing Screenshot Importer
|
||||||
|
description: Import UI screenshots and generate uxscii components automatically using vision analysis. Use when user wants to import, convert, or generate .uxm components from screenshots or images.
|
||||||
|
version: 0.0.1
|
||||||
|
author: Trabian
|
||||||
|
allowed-tools: Read, Write, Task, TodoWrite, Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fluxwing Screenshot Importer
|
||||||
|
|
||||||
|
Import UI screenshots and convert them to the **uxscii standard** by orchestrating specialized vision agents.
|
||||||
|
|
||||||
|
## Data Location Rules
|
||||||
|
|
||||||
|
**READ from (bundled templates - reference only):**
|
||||||
|
- `{SKILL_ROOT}/../uxscii-component-creator/templates/` - 11 component templates (for reference)
|
||||||
|
- `{SKILL_ROOT}/docs/` - Screenshot processing documentation
|
||||||
|
|
||||||
|
**WRITE to (project workspace):**
|
||||||
|
- `./fluxwing/components/` - Extracted components (.uxm + .md)
|
||||||
|
- `./fluxwing/screens/` - Screen composition (.uxm + .md + .rendered.md)
|
||||||
|
|
||||||
|
**NEVER write to skill directories - they are read-only!**
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
Import a screenshot of a UI design and automatically generate uxscii components and screens by **orchestrating specialized agents**:
|
||||||
|
|
||||||
|
1. **Vision Coordinator Agent** - Spawns 3 parallel vision agents (layout + components + properties)
|
||||||
|
2. **Component Generator Agents** - Generate component files in parallel (atomic + composite)
|
||||||
|
3. **Screen Scaffolder Skill** - Delegates to fluxwing-screen-scaffolder for screen composition
|
||||||
|
|
||||||
|
**⚠️ ORCHESTRATION RULES:**
|
||||||
|
|
||||||
|
**YOU CAN (as orchestrator):**
|
||||||
|
- ✅ Spawn vision analysis agents
|
||||||
|
- ✅ Spawn component generator agents
|
||||||
|
- ✅ Invoke fluxwing-screen-scaffolder skill
|
||||||
|
- ✅ Read screenshots
|
||||||
|
- ✅ Validate vision data
|
||||||
|
|
||||||
|
**YOU CANNOT (worker mode - forbidden):**
|
||||||
|
- ⚠️ Create screen files yourself using Write/Edit tools
|
||||||
|
- ⚠️ Generate .uxm, .md, or .rendered.md files for screens
|
||||||
|
- ⚠️ "Help" the scaffolder by pre-creating screen files
|
||||||
|
- ⚠️ Use TodoWrite to work through screen creation tasks yourself
|
||||||
|
|
||||||
|
**For screen composition:** ALWAYS delegate to fluxwing-screen-scaffolder skill. It will spawn composer agents that create all screen files (.uxm, .md, .rendered.md).
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### Phase 1: Get Screenshot Path
|
||||||
|
|
||||||
|
Ask the user for the screenshot path if not provided:
|
||||||
|
- "Which screenshot would you like to import?"
|
||||||
|
- Validate file exists and is a supported format (PNG, JPG, JPEG, WebP, GIF)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example
|
||||||
|
const screenshotPath = "/path/to/screenshot.png";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Spawn Vision Coordinator Agent
|
||||||
|
|
||||||
|
**CRITICAL**: Spawn the `screenshot-vision-coordinator` agent to orchestrate parallel vision analysis.
|
||||||
|
|
||||||
|
This agent will:
|
||||||
|
- Spawn 3 vision agents in parallel (layout discovery + component detection + visual properties)
|
||||||
|
- Wait for all agents to complete
|
||||||
|
- Merge results into unified component data structure
|
||||||
|
- Return JSON with screen metadata, components array, and composition
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Analyze screenshot with vision analysis",
|
||||||
|
prompt: `You are a UI screenshot analyzer extracting component structure for uxscii.
|
||||||
|
|
||||||
|
Screenshot path: ${screenshotPath}
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Read the screenshot image file
|
||||||
|
2. Analyze the UI layout structure (vertical, horizontal, grid, sidebar+main)
|
||||||
|
3. Detect all UI components (buttons, inputs, navigation, cards, etc.)
|
||||||
|
4. Extract visual properties (colors, spacing, borders, typography)
|
||||||
|
5. Identify component hierarchy (atomic vs composite)
|
||||||
|
6. Merge all findings into a unified data structure
|
||||||
|
7. Return valid JSON output
|
||||||
|
|
||||||
|
CRITICAL detection requirements:
|
||||||
|
- Do NOT miss navigation elements (check all edges - top, left, right, bottom)
|
||||||
|
- Do NOT miss small elements (icons, badges, close buttons, status indicators)
|
||||||
|
- Identify composite components (forms, cards with multiple elements)
|
||||||
|
- Note spatial relationships between components
|
||||||
|
|
||||||
|
Expected output format (valid JSON only, no markdown):
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"screen": {
|
||||||
|
"id": "screen-name",
|
||||||
|
"type": "dashboard|login|profile|settings",
|
||||||
|
"name": "Screen Name",
|
||||||
|
"description": "What this screen does",
|
||||||
|
"layout": "vertical|horizontal|grid|sidebar-main"
|
||||||
|
},
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"id": "component-id",
|
||||||
|
"type": "button|input|navigation|etc",
|
||||||
|
"name": "Component Name",
|
||||||
|
"description": "What it does",
|
||||||
|
"visualProperties": {...},
|
||||||
|
"isComposite": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"composition": {
|
||||||
|
"atomicComponents": ["id1", "id2"],
|
||||||
|
"compositeComponents": ["id3"],
|
||||||
|
"screenComponents": ["screen-id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Use your vision capabilities to analyze the screenshot carefully.`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wait for the vision coordinator to complete and return results.**
|
||||||
|
|
||||||
|
### Phase 3: Validate Vision Data
|
||||||
|
|
||||||
|
Check the returned data structure:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const visionData = visionCoordinatorResult;
|
||||||
|
|
||||||
|
// Required fields
|
||||||
|
if (!visionData.success) {
|
||||||
|
throw new Error(`Vision analysis failed: ${visionData.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visionData.components || visionData.components.length === 0) {
|
||||||
|
throw new Error("No components detected in screenshot");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation check (CRITICAL)
|
||||||
|
const hasNavigation = visionData.components.some(c =>
|
||||||
|
c.type === 'navigation' || c.id.includes('nav') || c.id.includes('header')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (visionData.screen.type === 'dashboard' && !hasNavigation) {
|
||||||
|
console.warn("⚠️ Dashboard detected but no navigation found - verify all nav elements were detected");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Spawn Component Generator Agents (Parallel)
|
||||||
|
|
||||||
|
**CRITICAL**: YOU MUST spawn ALL component generator agents in a SINGLE message with multiple Task tool calls. This is the ONLY way to achieve true parallel execution.
|
||||||
|
|
||||||
|
**DO THIS**: Send ONE message containing ALL Task calls for all components
|
||||||
|
**DON'T DO THIS**: Send separate messages for each component (this runs them sequentially)
|
||||||
|
|
||||||
|
For each atomic component, create a Task call in the SAME message:
|
||||||
|
|
||||||
|
```
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Generate email-input component",
|
||||||
|
prompt: "You are a uxscii component generator. Generate component files from vision data.
|
||||||
|
|
||||||
|
Component data: {id: 'email-input', type: 'input', visualProperties: {...}}
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load schema from {SKILL_ROOT}/../uxscii-component-creator/schemas/uxm-component.schema.json
|
||||||
|
2. Load docs from {SKILL_ROOT}/docs/screenshot-import-helpers.md
|
||||||
|
3. Generate .uxm file (valid JSON with default state only)
|
||||||
|
4. Generate .md file (ASCII template matching visual properties)
|
||||||
|
5. Save to ./fluxwing/components/
|
||||||
|
6. Return success with file paths
|
||||||
|
|
||||||
|
Follow uxscii standard strictly."
|
||||||
|
})
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Generate password-input component",
|
||||||
|
prompt: "You are a uxscii component generator. Generate component files from vision data.
|
||||||
|
|
||||||
|
Component data: {id: 'password-input', type: 'input', visualProperties: {...}}
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load schema from {SKILL_ROOT}/../uxscii-component-creator/schemas/uxm-component.schema.json
|
||||||
|
2. Load docs from {SKILL_ROOT}/docs/screenshot-import-helpers.md
|
||||||
|
3. Generate .uxm file (valid JSON with default state only)
|
||||||
|
4. Generate .md file (ASCII template matching visual properties)
|
||||||
|
5. Save to ./fluxwing/components/
|
||||||
|
6. Return success with file paths
|
||||||
|
|
||||||
|
Follow uxscii standard strictly."
|
||||||
|
})
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Generate submit-button component",
|
||||||
|
prompt: "You are a uxscii component generator. Generate component files from vision data.
|
||||||
|
|
||||||
|
Component data: {id: 'submit-button', type: 'button', visualProperties: {...}}
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load schema from {SKILL_ROOT}/../uxscii-component-creator/schemas/uxm-component.schema.json
|
||||||
|
2. Load docs from {SKILL_ROOT}/docs/screenshot-import-helpers.md
|
||||||
|
3. Generate .uxm file (valid JSON with default state only)
|
||||||
|
4. Generate .md file (ASCII template matching visual properties)
|
||||||
|
5. Save to ./fluxwing/components/
|
||||||
|
6. Return success with file paths
|
||||||
|
|
||||||
|
Follow uxscii standard strictly."
|
||||||
|
})
|
||||||
|
|
||||||
|
... repeat for ALL atomic components in the SAME message ...
|
||||||
|
|
||||||
|
... then for composite components in the SAME message:
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
description: "Generate login-form composite",
|
||||||
|
prompt: "You are a uxscii component generator. Generate composite component from vision data.
|
||||||
|
|
||||||
|
Component data: {id: 'login-form', type: 'form', components: [...], visualProperties: {...}}
|
||||||
|
|
||||||
|
IMPORTANT: Include component references in props.components array.
|
||||||
|
|
||||||
|
Your task:
|
||||||
|
1. Load schema from {SKILL_ROOT}/../uxscii-component-creator/schemas/uxm-component.schema.json
|
||||||
|
2. Generate .uxm with components array in props
|
||||||
|
3. Generate .md with {{component:id}} references
|
||||||
|
4. Save to ./fluxwing/components/
|
||||||
|
5. Return success
|
||||||
|
|
||||||
|
Follow uxscii standard strictly."
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Remember**: ALL Task calls must be in a SINGLE message for parallel execution!
|
||||||
|
|
||||||
|
### Phase 5: Delegate Screen Composition to Scaffolder
|
||||||
|
|
||||||
|
**CRITICAL**: YOU ARE AN ORCHESTRATOR - delegate screen creation to the screen-scaffolder skill!
|
||||||
|
|
||||||
|
**⚠️ DO NOT create screen files yourself using Write/Edit tools!**
|
||||||
|
|
||||||
|
After all components are created, use the fluxwing-screen-scaffolder to compose screens:
|
||||||
|
|
||||||
|
**Single Screen Import:**
|
||||||
|
```typescript
|
||||||
|
// Invoke screen-scaffolder skill
|
||||||
|
Skill({ command: "fluxwing-skills:fluxwing-screen-scaffolder" })
|
||||||
|
|
||||||
|
// The scaffolder will:
|
||||||
|
// 1. See all components already exist (you just created them)
|
||||||
|
// 2. Skip component creation (Step 3)
|
||||||
|
// 3. Spawn ONE composer agent (Step 4)
|
||||||
|
// 4. Composer creates .uxm + .md + .rendered.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**Multiple Screenshots Import (N > 1):**
|
||||||
|
```typescript
|
||||||
|
// After analyzing ALL screenshots and creating ALL components
|
||||||
|
// Invoke screen-scaffolder skill ONCE
|
||||||
|
Skill({ command: "fluxwing-skills:fluxwing-screen-scaffolder" })
|
||||||
|
|
||||||
|
// Tell it about the screens:
|
||||||
|
// "I've imported N screenshots and created X components.
|
||||||
|
// Please compose these N screens: [list screen names and component lists]"
|
||||||
|
|
||||||
|
// The scaffolder will:
|
||||||
|
// 1. Detect multi-screen scenario (N > 1)
|
||||||
|
// 2. Confirm with user
|
||||||
|
// 3. Skip component creation (all exist)
|
||||||
|
// 4. Spawn N composer agents in parallel (one per screen)
|
||||||
|
// 5. Each composer creates 3 files (.uxm, .md, .rendered.md)
|
||||||
|
// Result: 3N files created by scaffolder agents
|
||||||
|
```
|
||||||
|
|
||||||
|
**What you provide to scaffolder:**
|
||||||
|
- Screen name and type (from vision analysis)
|
||||||
|
- Component list (what components the screen needs)
|
||||||
|
- Layout structure (from vision data)
|
||||||
|
- Screenshot path (for .rendered.md generation)
|
||||||
|
|
||||||
|
### Phase 6: Report Results
|
||||||
|
|
||||||
|
Create comprehensive summary:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Screenshot Import Complete ✓
|
||||||
|
|
||||||
|
## Screenshot Analysis
|
||||||
|
- File: ${screenshotPath}
|
||||||
|
- Screen type: ${screenData.type}
|
||||||
|
- Layout: ${screenData.layout}
|
||||||
|
|
||||||
|
## Components Generated
|
||||||
|
|
||||||
|
### Atomic Components (${atomicCount})
|
||||||
|
${atomicComponents.map(c => `✓ ${c.id} (${c.type})`).join('\n')}
|
||||||
|
|
||||||
|
### Composite Components (${compositeCount})
|
||||||
|
${compositeComponents.map(c => `✓ ${c.id} (${c.type})`).join('\n')}
|
||||||
|
|
||||||
|
### Screen
|
||||||
|
✓ ${screenId}
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
|
||||||
|
**Components** (./fluxwing/components/):
|
||||||
|
- ${totalComponentFiles} files (.uxm + .md)
|
||||||
|
|
||||||
|
**Screen** (./fluxwing/screens/):
|
||||||
|
- ${screenId}.uxm
|
||||||
|
- ${screenId}.md
|
||||||
|
- ${screenId}.rendered.md
|
||||||
|
|
||||||
|
**Total: ${totalFiles} files created**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
- Vision analysis: Parallel (3 agents) ⚡
|
||||||
|
- Component generation: Parallel (${atomicCount + compositeCount} agents) ⚡
|
||||||
|
- Total time: ~${estimatedTime}s
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Review screen: `cat ./fluxwing/screens/${screenId}.rendered.md`
|
||||||
|
2. Add interaction states to components
|
||||||
|
3. Customize components as needed
|
||||||
|
4. View all components
|
||||||
|
```
|
||||||
|
|
||||||
|
## Vision Agents Used
|
||||||
|
|
||||||
|
This skill orchestrates 5 specialized vision agents:
|
||||||
|
|
||||||
|
1. **screenshot-vision-coordinator** - Orchestrates parallel analysis
|
||||||
|
2. **screenshot-component-detection** - Finds UI elements
|
||||||
|
3. **screenshot-layout-discovery** - Understands structure
|
||||||
|
4. **screenshot-visual-properties** - Extracts styling
|
||||||
|
5. **screenshot-component-generator** - Creates .uxm/.md files
|
||||||
|
|
||||||
|
## Example Interaction
|
||||||
|
|
||||||
|
```
|
||||||
|
User: Import this screenshot at /Users/me/Desktop/login.png
|
||||||
|
|
||||||
|
Skill: I'll import the UI screenshot and generate uxscii components!
|
||||||
|
|
||||||
|
[Validates screenshot exists]
|
||||||
|
|
||||||
|
Step 1: Analyzing screenshot with vision agents...
|
||||||
|
[Spawns vision coordinator]
|
||||||
|
|
||||||
|
✓ Vision analysis complete:
|
||||||
|
- Detected 5 components
|
||||||
|
- Screen type: login
|
||||||
|
- Layout: vertical-center
|
||||||
|
|
||||||
|
Step 2: Generating component files in parallel...
|
||||||
|
[Spawns 5 component generator agents in parallel]
|
||||||
|
|
||||||
|
✓ All components generated!
|
||||||
|
|
||||||
|
# Screenshot Import Complete ✓
|
||||||
|
|
||||||
|
## Components Generated
|
||||||
|
✓ email-input (input)
|
||||||
|
✓ password-input (input)
|
||||||
|
✓ submit-button (button)
|
||||||
|
✓ cancel-link (link)
|
||||||
|
✓ login-form (form)
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
- 10 component files
|
||||||
|
- 3 screen files
|
||||||
|
Total: 13 files
|
||||||
|
|
||||||
|
Performance: ~45s (5 agents in parallel) ⚡
|
||||||
|
|
||||||
|
Next steps:
|
||||||
|
- Review: cat ./fluxwing/screens/login-screen.rendered.md
|
||||||
|
- Add states to make components interactive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quality Standards
|
||||||
|
|
||||||
|
Ensure imported components include:
|
||||||
|
- ✓ Valid JSON schema compliance
|
||||||
|
- ✓ Complete metadata (name, description, tags)
|
||||||
|
- ✓ Proper component types
|
||||||
|
- ✓ ASCII art matches detected visual properties
|
||||||
|
- ✓ All detected components extracted
|
||||||
|
- ✓ Screen composition includes all components
|
||||||
|
- ✓ Rendered example with realistic data
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
- **Parallel execution is critical**: All agents must be spawned in a single message
|
||||||
|
- **Navigation elements**: Verify top nav, side nav, footer nav are detected
|
||||||
|
- **Small elements**: Don't miss icons, badges, close buttons, status indicators
|
||||||
|
- **Composite components**: Forms, cards with multiple elements
|
||||||
|
- **Screen files**: Always create 3 files (.uxm, .md, .rendered.md)
|
||||||
|
- **Validation**: Check vision data before generating components
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**If vision analysis fails:**
|
||||||
|
```
|
||||||
|
✗ Vision analysis failed: [error message]
|
||||||
|
|
||||||
|
Please check:
|
||||||
|
- Screenshot file exists and is readable
|
||||||
|
- File format is supported (PNG, JPG, JPEG, WebP, GIF)
|
||||||
|
- Screenshot contains visible UI elements
|
||||||
|
```
|
||||||
|
|
||||||
|
**If component generation fails:**
|
||||||
|
```
|
||||||
|
⚠️ Partial success: 3 of 5 components generated
|
||||||
|
|
||||||
|
Successful:
|
||||||
|
✓ email-input
|
||||||
|
✓ password-input
|
||||||
|
✓ submit-button
|
||||||
|
|
||||||
|
Failed:
|
||||||
|
✗ cancel-link: [error]
|
||||||
|
✗ login-form: [error]
|
||||||
|
|
||||||
|
You can retry failed components or create them manually.
|
||||||
|
```
|
||||||
|
|
||||||
|
**If no components detected:**
|
||||||
|
```
|
||||||
|
✗ No components detected in screenshot.
|
||||||
|
|
||||||
|
This could mean:
|
||||||
|
- Screenshot is blank or unclear
|
||||||
|
- UI elements are too small to detect
|
||||||
|
- Screenshot is not a UI design
|
||||||
|
|
||||||
|
Please try a different screenshot or create components manually.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
See `{SKILL_ROOT}/docs/` for detailed documentation on:
|
||||||
|
- screenshot-import-ascii.md - ASCII generation patterns
|
||||||
|
- screenshot-import-examples.md - Example imports
|
||||||
|
- screenshot-import-helpers.md - Helper functions
|
||||||
|
- screenshot-data-merging.md - Data structure merging
|
||||||
|
- screenshot-screen-generation.md - Screen file creation
|
||||||
|
- screenshot-validation-functions.md - Data validation
|
||||||
|
|
||||||
|
You're helping users rapidly convert visual designs into uxscii components!
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
# Screenshot Import Data Merging
|
||||||
|
|
||||||
|
This document provides helper functions for merging vision agent outputs into a unified data structure.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
After 3 vision agents complete (layout, components, visual properties), their outputs must be merged into a single enriched data structure suitable for component generation.
|
||||||
|
|
||||||
|
## Helper Functions
|
||||||
|
|
||||||
|
### 1. findSectionForComponent()
|
||||||
|
|
||||||
|
Determines which layout section contains a component:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function findSectionForComponent(compLocation, layoutStructure) {
|
||||||
|
// Match component location to layout sections
|
||||||
|
for (const section of layoutStructure.sections) {
|
||||||
|
if (compLocation.section === section.id) {
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to main section if no match
|
||||||
|
return layoutStructure.sections.find(s => s.type === 'main');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. categorizeComponents()
|
||||||
|
|
||||||
|
Splits components into atomic, composite, and screen categories:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function categorizeComponents(components, screenType) {
|
||||||
|
const atomicComponents = components
|
||||||
|
.filter(c => c.category === "atomic")
|
||||||
|
.map(c => c.id);
|
||||||
|
|
||||||
|
const compositeComponents = components
|
||||||
|
.filter(c => c.category === "composite")
|
||||||
|
.map(c => c.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
atomicComponents,
|
||||||
|
compositeComponents,
|
||||||
|
screenComponents: [`${screenType}-screen`]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. generateScreenName()
|
||||||
|
|
||||||
|
Converts screen type to human-readable name:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateScreenName(screenType) {
|
||||||
|
const names = {
|
||||||
|
"dashboard": "Dashboard",
|
||||||
|
"login": "Login Screen",
|
||||||
|
"form": "Form Screen",
|
||||||
|
"list": "List View",
|
||||||
|
"detail": "Detail View",
|
||||||
|
"settings": "Settings",
|
||||||
|
"profile": "Profile Page"
|
||||||
|
};
|
||||||
|
|
||||||
|
return names[screenType] || `${screenType.charAt(0).toUpperCase()}${screenType.slice(1)} Screen`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. generateScreenDescription()
|
||||||
|
|
||||||
|
Creates screen description from type and layout:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateScreenDescription(screenType, layoutStructure) {
|
||||||
|
const sectionNames = layoutStructure.sections.map(s => s.type).join(", ");
|
||||||
|
return `${generateScreenName(screenType)} with ${sectionNames} sections`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. enrichComponentWithVisualProps()
|
||||||
|
|
||||||
|
Merges component data with visual properties (with defaults):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function enrichComponentWithVisualProps(comp, visualResult) {
|
||||||
|
const visualProps = visualResult.visualProperties[comp.id] || {
|
||||||
|
dimensions: {
|
||||||
|
width: comp.location.position.width,
|
||||||
|
height: comp.location.position.height,
|
||||||
|
unit: "characters"
|
||||||
|
},
|
||||||
|
borderStyle: "light",
|
||||||
|
fillPattern: "transparent",
|
||||||
|
textAlignment: "left",
|
||||||
|
spacing: { padding: "normal", margin: "normal" }
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: visualProps.dimensions?.width || comp.location.position.width,
|
||||||
|
height: visualProps.dimensions?.height || comp.location.position.height,
|
||||||
|
borderStyle: visualProps.borderStyle || "light",
|
||||||
|
fillPattern: visualProps.fillPattern || "transparent",
|
||||||
|
textAlignment: visualProps.textAlignment || "left",
|
||||||
|
textContent: comp.textContent,
|
||||||
|
placeholder: comp.placeholder || ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Merging Workflow
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function mergeAgentResults(layoutResult, componentResult, visualResult) {
|
||||||
|
const mergedData = {
|
||||||
|
screen: {
|
||||||
|
type: layoutResult.screenType,
|
||||||
|
name: generateScreenName(layoutResult.screenType),
|
||||||
|
description: generateScreenDescription(layoutResult.screenType, layoutResult.layoutStructure),
|
||||||
|
layout: layoutResult.layoutStructure.type
|
||||||
|
},
|
||||||
|
components: componentResult.components.map(comp => {
|
||||||
|
// Find which section contains this component
|
||||||
|
const section = findSectionForComponent(comp.location, layoutResult.layoutStructure);
|
||||||
|
|
||||||
|
// Enrich with visual properties
|
||||||
|
const visualProperties = enrichComponentWithVisualProps(comp, visualResult);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: comp.id,
|
||||||
|
type: comp.type,
|
||||||
|
category: comp.category,
|
||||||
|
section: section?.id || "main",
|
||||||
|
visualProperties,
|
||||||
|
states: comp.states || ["default"],
|
||||||
|
accessibility: comp.accessibility,
|
||||||
|
location: comp.location // preserve original location data
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
composition: categorizeComponents(componentResult.components, layoutResult.screenType),
|
||||||
|
layoutHierarchy: layoutResult.hierarchy
|
||||||
|
};
|
||||||
|
|
||||||
|
return mergedData;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// After all 3 vision agents complete
|
||||||
|
const layoutResult = await layoutAgent();
|
||||||
|
const componentResult = await componentAgent();
|
||||||
|
const visualResult = await visualAgent();
|
||||||
|
|
||||||
|
// Merge their outputs
|
||||||
|
const mergedData = mergeAgentResults(layoutResult, componentResult, visualResult);
|
||||||
|
|
||||||
|
// mergedData is now ready for component generation
|
||||||
|
console.log(`Merged ${mergedData.components.length} components for ${mergedData.screen.type} screen`);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Structure
|
||||||
|
|
||||||
|
The merged data structure:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
screen: {
|
||||||
|
type: "dashboard",
|
||||||
|
name: "Dashboard",
|
||||||
|
description: "Dashboard with header, main sections",
|
||||||
|
layout: "fixed-header-sidebar"
|
||||||
|
},
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
id: "email-input",
|
||||||
|
type: "input",
|
||||||
|
category: "atomic",
|
||||||
|
section: "main",
|
||||||
|
visualProperties: {
|
||||||
|
width: 40,
|
||||||
|
height: 3,
|
||||||
|
borderStyle: "light",
|
||||||
|
fillPattern: "transparent",
|
||||||
|
textAlignment: "left",
|
||||||
|
textContent: "Email",
|
||||||
|
placeholder: "Enter your email"
|
||||||
|
},
|
||||||
|
states: ["default", "focus", "error"],
|
||||||
|
accessibility: {
|
||||||
|
role: "textbox",
|
||||||
|
label: "Email address"
|
||||||
|
},
|
||||||
|
location: { /* original location data */ }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
composition: {
|
||||||
|
atomicComponents: ["email-input", "password-input"],
|
||||||
|
compositeComponents: ["login-form"],
|
||||||
|
screenComponents: ["dashboard-screen"]
|
||||||
|
},
|
||||||
|
layoutHierarchy: {
|
||||||
|
root: "screen",
|
||||||
|
children: {
|
||||||
|
header: ["logo", "navigation"],
|
||||||
|
main: ["login-form"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,481 @@
|
|||||||
|
# Screenshot Import ASCII Generation
|
||||||
|
|
||||||
|
This document contains all ASCII art generation functions for screenshot-to-uxscii conversion. These functions transform visual component properties into uxscii-compliant ASCII representations.
|
||||||
|
|
||||||
|
## Core ASCII Functions
|
||||||
|
|
||||||
|
### selectBorderChars()
|
||||||
|
|
||||||
|
Selects box-drawing characters based on state and border style:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function selectBorderChars(state: string, baseStyle: string): string {
|
||||||
|
// Border character map: state → style → character sequence
|
||||||
|
// Format: "topLeft|top|topRight|side|bottomLeft|bottom|bottomRight"
|
||||||
|
const styleMap: Record<string, Record<string, string>> = {
|
||||||
|
'default': {
|
||||||
|
'light': '┌|─|┐|│|└|┘',
|
||||||
|
'rounded': '╭|─|╮|│|╰|╯',
|
||||||
|
'double': '╔|═|╗|║|╚|╝',
|
||||||
|
'heavy': '┏|━|┓|┃|┗|┛',
|
||||||
|
'none': ' | | | | | '
|
||||||
|
},
|
||||||
|
'hover': {
|
||||||
|
'light': '┏|━|┓|┃|┗|┛', // Upgrade to heavy
|
||||||
|
'rounded': '┏|━|┓|┃|┗|┛', // Upgrade to heavy
|
||||||
|
'double': '╔|═|╗|║|╚|╝', // Keep double
|
||||||
|
'heavy': '┏|━|┓|┃|┗|┛', // Keep heavy
|
||||||
|
'none': ' | | | | | '
|
||||||
|
},
|
||||||
|
'focus': {
|
||||||
|
'light': '┏|━|┓|┃|┗|┛', // Upgrade to heavy
|
||||||
|
'rounded': '┏|━|┓|┃|┗|┛', // Upgrade to heavy
|
||||||
|
'double': '╔|═|╗|║|╚|╝', // Keep double
|
||||||
|
'heavy': '┏|━|┓|┃|┗|┛', // Keep heavy
|
||||||
|
'none': ' | | | | | '
|
||||||
|
},
|
||||||
|
'active': {
|
||||||
|
'light': '┏|━|┓|┃|┗|┛',
|
||||||
|
'rounded': '┏|━|┓|┃|┗|┛',
|
||||||
|
'double': '╔|═|╗|║|╚|╝',
|
||||||
|
'heavy': '┏|━|┓|┃|┗|┛',
|
||||||
|
'none': ' | | | | | '
|
||||||
|
},
|
||||||
|
'disabled': {
|
||||||
|
'light': '┌| ─ |┐|│|└| ─ |┘', // Dashed pattern
|
||||||
|
'rounded': '╭| ─ |╮|│|╰| ─ |╯', // Dashed pattern
|
||||||
|
'double': '╔| ═ |╗|║|╚| ═ |╝', // Dashed pattern
|
||||||
|
'heavy': '┏| ━ |┓|┃|┗| ━ |┛', // Dashed pattern
|
||||||
|
'none': ' | | | | | '
|
||||||
|
},
|
||||||
|
'error': {
|
||||||
|
'light': '┏|━|┓|┃|┗|┛',
|
||||||
|
'rounded': '┏|━|┓|┃|┗|┛',
|
||||||
|
'double': '╔|═|╗|║|╚|╝',
|
||||||
|
'heavy': '┏|━|┓|┃|┗|┛',
|
||||||
|
'none': ' | | | | | '
|
||||||
|
},
|
||||||
|
'success': {
|
||||||
|
'light': '┏|━|┓|┃|┗|┛',
|
||||||
|
'rounded': '┏|━|┓|┃|┗|┛',
|
||||||
|
'double': '╔|═|╗|║|╚|╝',
|
||||||
|
'heavy': '┏|━|┓|┃|┗|┛',
|
||||||
|
'none': ' | | | | | '
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stateStyles = styleMap[state] || styleMap['default'];
|
||||||
|
return stateStyles[baseStyle] || styleMap['default']['light'];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**State Transformations:**
|
||||||
|
- **hover/focus/active**: Upgrade light/rounded to heavy
|
||||||
|
- **disabled**: Add spaces for dashed appearance
|
||||||
|
- **error/success**: Use heavy borders for attention
|
||||||
|
|
||||||
|
### selectFillPattern()
|
||||||
|
|
||||||
|
Determines interior fill character based on component type and state:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function selectFillPattern(state: string, componentType: string): string {
|
||||||
|
const typePatterns: Record<string, string> = {
|
||||||
|
'button': '▓', // Solid button fill
|
||||||
|
'input': ' ', // Empty space for text
|
||||||
|
'checkbox': ' ', // Filled by special generator
|
||||||
|
'radio': ' ', // Filled by special generator
|
||||||
|
'select': ' ', // Text area
|
||||||
|
'card': ' ', // Content area
|
||||||
|
'modal': ' ', // Content area
|
||||||
|
'panel': ' ', // Content area
|
||||||
|
'alert': ' ', // Message area
|
||||||
|
'badge': '▓', // Solid badge fill
|
||||||
|
'progress': ' ', // Filled by special generator
|
||||||
|
'spinner': ' ', // Filled by special generator
|
||||||
|
'toast': ' ', // Message area
|
||||||
|
'table': ' ', // Data cells
|
||||||
|
'list': ' ' // List items
|
||||||
|
};
|
||||||
|
|
||||||
|
const baseFill = typePatterns[componentType] || ' ';
|
||||||
|
|
||||||
|
// State-specific modifications
|
||||||
|
if (state === 'hover' && componentType === 'button') {
|
||||||
|
return '█'; // Darker fill on hover
|
||||||
|
}
|
||||||
|
if (state === 'disabled') {
|
||||||
|
return ' '; // Empty on disabled
|
||||||
|
}
|
||||||
|
if (state === 'active' && componentType === 'button') {
|
||||||
|
return '█'; // Full solid on active
|
||||||
|
}
|
||||||
|
if (state === 'focus' && componentType === 'input') {
|
||||||
|
return '│'; // Cursor indicator
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseFill;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern Types:**
|
||||||
|
- Solid `▓`: Buttons, badges
|
||||||
|
- Full `█`: Hover/active states
|
||||||
|
- Cursor `│`: Input focus
|
||||||
|
- Empty ` `: Inputs, containers
|
||||||
|
|
||||||
|
### buildASCIIBox()
|
||||||
|
|
||||||
|
Constructs ASCII box with text centering:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function buildASCIIBox(
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
text: string,
|
||||||
|
borderChars: string,
|
||||||
|
fillPattern: string
|
||||||
|
): string {
|
||||||
|
const [tl, t, tr, s, bl, b, br] = borderChars.split('|');
|
||||||
|
const lines: string[] = [];
|
||||||
|
const innerWidth = width - 2;
|
||||||
|
const innerHeight = height - 2;
|
||||||
|
|
||||||
|
// Top border
|
||||||
|
lines.push(tl + t.repeat(innerWidth) + tr);
|
||||||
|
|
||||||
|
// Calculate text position (vertical center)
|
||||||
|
const textLine = Math.floor(innerHeight / 2);
|
||||||
|
|
||||||
|
// Middle lines
|
||||||
|
for (let i = 0; i < innerHeight; i++) {
|
||||||
|
if (i === textLine && text) {
|
||||||
|
// Center text horizontally
|
||||||
|
const textLength = text.length;
|
||||||
|
const paddingLeft = Math.floor((innerWidth - textLength) / 2);
|
||||||
|
const paddingRight = innerWidth - textLength - paddingLeft;
|
||||||
|
|
||||||
|
const line = s +
|
||||||
|
' '.repeat(paddingLeft) +
|
||||||
|
text +
|
||||||
|
' '.repeat(paddingRight) +
|
||||||
|
s;
|
||||||
|
lines.push(line);
|
||||||
|
} else {
|
||||||
|
lines.push(s + fillPattern.repeat(innerWidth) + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom border
|
||||||
|
lines.push(bl + b.repeat(innerWidth) + br);
|
||||||
|
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### generateASCII()
|
||||||
|
|
||||||
|
Main ASCII generation function with optional minimal mode:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateASCII(
|
||||||
|
componentId: string,
|
||||||
|
state: string,
|
||||||
|
visualProperties: any,
|
||||||
|
componentType: string,
|
||||||
|
minimalMode: boolean = false // NEW: Enable single-state generation
|
||||||
|
): string {
|
||||||
|
// Special component handlers
|
||||||
|
if (componentType === 'checkbox') {
|
||||||
|
return generateCheckbox(state, visualProperties.textContent);
|
||||||
|
}
|
||||||
|
if (componentType === 'radio') {
|
||||||
|
return generateRadio(state, visualProperties.textContent);
|
||||||
|
}
|
||||||
|
if (componentType === 'progress') {
|
||||||
|
return generateProgressBar(50, visualProperties.width);
|
||||||
|
}
|
||||||
|
if (componentType === 'spinner') {
|
||||||
|
return generateSpinner(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard box components
|
||||||
|
const borderChars = selectBorderChars(state, visualProperties.borderStyle);
|
||||||
|
const fillPattern = selectFillPattern(state, componentType);
|
||||||
|
|
||||||
|
let text = visualProperties.textContent || '';
|
||||||
|
|
||||||
|
// Add state indicators
|
||||||
|
if (state === 'focus' && componentType === 'button') {
|
||||||
|
text += ' ✨';
|
||||||
|
}
|
||||||
|
if (state === 'error' && componentType === 'input') {
|
||||||
|
text = '⚠️ ' + text;
|
||||||
|
}
|
||||||
|
if (state === 'success' && componentType === 'input') {
|
||||||
|
text = '✅ ' + text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildASCIIBox(
|
||||||
|
visualProperties.width,
|
||||||
|
visualProperties.height,
|
||||||
|
text,
|
||||||
|
borderChars,
|
||||||
|
fillPattern
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Minimal Mode Usage**:
|
||||||
|
|
||||||
|
When `minimalMode` is `true`, this function is typically only called for the 'default' state during initial component creation. This enables fast MVP component generation.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Minimal mode - only generate default state
|
||||||
|
const defaultOnlyASCII = generateASCII(
|
||||||
|
'submit-button',
|
||||||
|
'default',
|
||||||
|
visualProperties,
|
||||||
|
'button',
|
||||||
|
true // minimalMode = true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Full mode - generate any state
|
||||||
|
const hoverASCII = generateASCII(
|
||||||
|
'submit-button',
|
||||||
|
'hover',
|
||||||
|
visualProperties,
|
||||||
|
'button',
|
||||||
|
false // minimalMode = false (default)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `minimalMode` parameter doesn't change the function behavior directly, but signals intent for documentation. When creating components in minimal mode, only call this function once for 'default' state instead of looping through all states.
|
||||||
|
|
||||||
|
## Special Component Generators
|
||||||
|
|
||||||
|
### generateCheckbox()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateCheckbox(state: string, label: string): string {
|
||||||
|
let box = '[ ]'; // Unchecked
|
||||||
|
|
||||||
|
if (state === 'checked') {
|
||||||
|
box = '[✓]';
|
||||||
|
} else if (state === 'indeterminate') {
|
||||||
|
box = '[▬]';
|
||||||
|
} else if (state === 'disabled') {
|
||||||
|
box = '[─]';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${box} ${label}`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**States:**
|
||||||
|
- Unchecked: `[ ]`
|
||||||
|
- Checked: `[✓]`
|
||||||
|
- Indeterminate: `[▬]`
|
||||||
|
- Disabled: `[─]`
|
||||||
|
|
||||||
|
### generateRadio()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateRadio(state: string, label: string): string {
|
||||||
|
let circle = '○'; // Unselected
|
||||||
|
|
||||||
|
if (state === 'selected') {
|
||||||
|
circle = '◉';
|
||||||
|
} else if (state === 'disabled') {
|
||||||
|
circle = '◌';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${circle} ${label}`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**States:**
|
||||||
|
- Unselected: `○`
|
||||||
|
- Selected: `◉`
|
||||||
|
- Disabled: `◌`
|
||||||
|
|
||||||
|
### generateProgressBar()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateProgressBar(percent: number, width: number): string {
|
||||||
|
const filled = Math.floor((width * percent) / 100);
|
||||||
|
const remaining = width - filled;
|
||||||
|
|
||||||
|
const bar = '█'.repeat(filled) + '░'.repeat(remaining);
|
||||||
|
return `${bar} ${percent}%`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example:** `████░░░░░░ 40%`
|
||||||
|
|
||||||
|
### generateSpinner()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateSpinner(frame: number): string {
|
||||||
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
||||||
|
return frames[frame % frames.length];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Animation:** 10 Braille pattern characters
|
||||||
|
|
||||||
|
## Utility Functions
|
||||||
|
|
||||||
|
### maskPassword()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function maskPassword(value: string): string {
|
||||||
|
return '•'.repeat(value.length);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### renderInputPlaceholder()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function renderInputPlaceholder(
|
||||||
|
placeholder: string,
|
||||||
|
value: string,
|
||||||
|
width: number
|
||||||
|
): string {
|
||||||
|
if (value) {
|
||||||
|
return value.padEnd(width - 2, ' ');
|
||||||
|
} else {
|
||||||
|
return placeholder.padEnd(width - 2, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### addGlowEffect()
|
||||||
|
|
||||||
|
For hover states:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function addGlowEffect(ascii: string): string {
|
||||||
|
const lines = ascii.split('\n');
|
||||||
|
const glowLines = lines.map(line => `░${line}░`);
|
||||||
|
|
||||||
|
const glowTop = '░'.repeat(glowLines[0].length);
|
||||||
|
const glowBottom = '░'.repeat(glowLines[0].length);
|
||||||
|
|
||||||
|
return [glowTop, ...glowLines, glowBottom].join('\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```
|
||||||
|
Without glow: With glow:
|
||||||
|
╭─────────╮ ░░░░░░░░░░░░░
|
||||||
|
│ Click │ ░╭─────────╮░
|
||||||
|
╰─────────╯ ░│ Click │░
|
||||||
|
░╰─────────╯░
|
||||||
|
░░░░░░░░░░░░░
|
||||||
|
```
|
||||||
|
|
||||||
|
### addValidationIndicator()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function addValidationIndicator(
|
||||||
|
ascii: string,
|
||||||
|
state: string,
|
||||||
|
message?: string
|
||||||
|
): string {
|
||||||
|
let indicator = '';
|
||||||
|
|
||||||
|
if (state === 'error') {
|
||||||
|
indicator = '⚠️';
|
||||||
|
if (message) {
|
||||||
|
indicator += '\n❌ ' + message;
|
||||||
|
}
|
||||||
|
} else if (state === 'success') {
|
||||||
|
indicator = '✅';
|
||||||
|
}
|
||||||
|
|
||||||
|
return ascii + indicator;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Generation Example
|
||||||
|
|
||||||
|
Putting it all together for a button:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Input from vision analysis
|
||||||
|
const componentData = {
|
||||||
|
id: 'submit-button',
|
||||||
|
type: 'button',
|
||||||
|
visualProperties: {
|
||||||
|
width: 20,
|
||||||
|
height: 3,
|
||||||
|
borderStyle: 'rounded',
|
||||||
|
textContent: 'Submit'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate default state
|
||||||
|
const defaultASCII = generateASCII(
|
||||||
|
'submit-button',
|
||||||
|
'default',
|
||||||
|
componentData.visualProperties,
|
||||||
|
'button'
|
||||||
|
);
|
||||||
|
// Result:
|
||||||
|
// ╭──────────────────╮
|
||||||
|
// │▓▓▓▓▓▓Submit▓▓▓▓▓▓│
|
||||||
|
// ╰──────────────────╯
|
||||||
|
|
||||||
|
// Generate hover state
|
||||||
|
const hoverASCII = generateASCII(
|
||||||
|
'submit-button',
|
||||||
|
'hover',
|
||||||
|
componentData.visualProperties,
|
||||||
|
'button'
|
||||||
|
);
|
||||||
|
// Result:
|
||||||
|
// ┏━━━━━━━━━━━━━━━━━━┓
|
||||||
|
// ┃████████Submit████┃
|
||||||
|
// ┗━━━━━━━━━━━━━━━━━━┛
|
||||||
|
|
||||||
|
// Generate disabled state
|
||||||
|
const disabledASCII = generateASCII(
|
||||||
|
'submit-button',
|
||||||
|
'disabled',
|
||||||
|
componentData.visualProperties,
|
||||||
|
'button'
|
||||||
|
);
|
||||||
|
// Result:
|
||||||
|
// ╭ ─ ─ ─ ─ ─ ─ ─ ─ ╮
|
||||||
|
// │ Submit │
|
||||||
|
// ╰ ─ ─ ─ ─ ─ ─ ─ ─ ╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## ASCII Generation Guidelines
|
||||||
|
|
||||||
|
**Consistency Rules:**
|
||||||
|
1. Same dimensions across all states
|
||||||
|
2. Border progression: default→light, hover→heavy, disabled→dashed
|
||||||
|
3. Text always centered (horizontal and vertical)
|
||||||
|
4. State indicators used sparingly (✨, ⚠️, ✅)
|
||||||
|
5. Test in monospace font
|
||||||
|
|
||||||
|
**Performance Tips:**
|
||||||
|
- Pre-compile border character sets
|
||||||
|
- Cache generated ASCII for repeated components
|
||||||
|
- Reuse fill patterns
|
||||||
|
|
||||||
|
**Accessibility:**
|
||||||
|
- Keep text readable (adequate padding)
|
||||||
|
- Use Unicode carefully (terminal support varies)
|
||||||
|
- Provide text alternatives in metadata
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
These functions implement ASCII patterns documented in:
|
||||||
|
- `fluxwing/data/docs/06-ascii-patterns.md` - Standard box-drawing patterns
|
||||||
|
- Examples in `fluxwing/data/examples/*.md` - Real component templates
|
||||||
|
|
||||||
|
Use these functions to generate consistent, high-quality ASCII representations for all uxscii components.
|
||||||
@@ -0,0 +1,715 @@
|
|||||||
|
# Screenshot Import Complete Examples
|
||||||
|
|
||||||
|
This document contains complete end-to-end examples of screenshot-to-uxscii conversion, showing input data, generated files, and expected outputs.
|
||||||
|
|
||||||
|
## Example 1: Simple Button Component
|
||||||
|
|
||||||
|
### Input: Vision Analysis Data
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "submit-button",
|
||||||
|
"type": "button",
|
||||||
|
"category": "atomic",
|
||||||
|
"visualProperties": {
|
||||||
|
"width": 20,
|
||||||
|
"height": 3,
|
||||||
|
"borderStyle": "rounded",
|
||||||
|
"textContent": "Submit"
|
||||||
|
},
|
||||||
|
"states": ["default", "hover", "focus", "disabled"],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"label": "Submit form"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output: submit-button.uxm
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "submit-button",
|
||||||
|
"type": "button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Submit Button",
|
||||||
|
"description": "An interactive button component for user actions. Submit form",
|
||||||
|
"author": "Fluxwing Screenshot Import",
|
||||||
|
"created": "2025-10-13T12:00:00Z",
|
||||||
|
"modified": "2025-10-13T12:00:00Z",
|
||||||
|
"tags": ["button", "imported", "screenshot-generated", "utility", "interactive"],
|
||||||
|
"category": "utility"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"text": "Submit",
|
||||||
|
"variant": "secondary",
|
||||||
|
"size": "small"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"border": "rounded",
|
||||||
|
"background": "filled",
|
||||||
|
"textColor": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hover",
|
||||||
|
"properties": {
|
||||||
|
"border": "heavy",
|
||||||
|
"background": "filled",
|
||||||
|
"textColor": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "focus",
|
||||||
|
"properties": {
|
||||||
|
"border": "heavy",
|
||||||
|
"background": "filled",
|
||||||
|
"textColor": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "disabled",
|
||||||
|
"properties": {
|
||||||
|
"border": "dashed",
|
||||||
|
"background": "filled",
|
||||||
|
"textColor": "default",
|
||||||
|
"opacity": 0.5,
|
||||||
|
"cursor": "not-allowed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interactions": ["click", "keyboard"],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "button",
|
||||||
|
"focusable": true,
|
||||||
|
"keyboardSupport": ["Enter", "Space"],
|
||||||
|
"ariaLabel": "Submit form"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": "inline-block",
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": {
|
||||||
|
"padding": { "x": 2, "y": 1 },
|
||||||
|
"margin": { "x": 0, "y": 0 }
|
||||||
|
},
|
||||||
|
"sizing": {
|
||||||
|
"minWidth": 20,
|
||||||
|
"height": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "submit-button.md",
|
||||||
|
"width": 20,
|
||||||
|
"height": 3,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "text",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"default": "Submit",
|
||||||
|
"description": "button label text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "variant",
|
||||||
|
"type": "string",
|
||||||
|
"required": false,
|
||||||
|
"default": "primary",
|
||||||
|
"description": "Button style variant (primary, secondary, danger)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "size",
|
||||||
|
"type": "string",
|
||||||
|
"required": false,
|
||||||
|
"default": "medium",
|
||||||
|
"description": "Component size (small, medium, large)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output: submit-button.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Submit Button
|
||||||
|
|
||||||
|
An interactive button component for user actions. Submit form
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│▓▓▓▓▓▓Submit▓▓▓▓▓▓│
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hover State
|
||||||
|
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃████████Submit████┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
## Focus State
|
||||||
|
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃▓▓▓▓▓Submit ✨▓▓▓▓┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabled State
|
||||||
|
|
||||||
|
```
|
||||||
|
╭ ─ ─ ─ ─ ─ ─ ─ ─ ╮
|
||||||
|
│ Submit │
|
||||||
|
╰ ─ ─ ─ ─ ─ ─ ─ ─ ╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): button label text
|
||||||
|
- Default: "Submit"
|
||||||
|
- Example: "Submit Form"
|
||||||
|
|
||||||
|
- `variant` (string, optional): Button style variant (primary, secondary, danger)
|
||||||
|
- Default: "primary"
|
||||||
|
|
||||||
|
- `size` (string, optional): Component size (small, medium, large)
|
||||||
|
- Default: "medium"
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: button
|
||||||
|
- **Focusable**: Yes
|
||||||
|
- **Keyboard Support**: Enter, Space
|
||||||
|
- **ARIA Label**: Submit form
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
╭──────────────────╮
|
||||||
|
│▓▓▓Submit Form▓▓▓▓│
|
||||||
|
╰──────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated by Fluxwing Screenshot Import*
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 2: Email Input Component
|
||||||
|
|
||||||
|
### Input: Vision Analysis Data
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "email-input",
|
||||||
|
"type": "input",
|
||||||
|
"category": "atomic",
|
||||||
|
"visualProperties": {
|
||||||
|
"width": 40,
|
||||||
|
"height": 3,
|
||||||
|
"borderStyle": "light",
|
||||||
|
"textContent": "Email",
|
||||||
|
"placeholder": "Enter your email"
|
||||||
|
},
|
||||||
|
"states": ["default", "focus", "error", "disabled"],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "textbox",
|
||||||
|
"label": "Email address"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output: email-input.uxm (abbreviated)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "email-input",
|
||||||
|
"type": "input",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Email Input",
|
||||||
|
"description": "A text input field for user data entry. Email address",
|
||||||
|
"category": "input"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"text": "Email",
|
||||||
|
"placeholder": "Enter your email",
|
||||||
|
"type": "email",
|
||||||
|
"size": "large",
|
||||||
|
"maxLength": 32
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "email-input.md",
|
||||||
|
"width": 40,
|
||||||
|
"height": 3,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "text",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"default": "Email",
|
||||||
|
"description": "input label text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "placeholder",
|
||||||
|
"type": "string",
|
||||||
|
"required": false,
|
||||||
|
"default": "Enter your email",
|
||||||
|
"description": "Placeholder text when empty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "value",
|
||||||
|
"type": "string",
|
||||||
|
"required": false,
|
||||||
|
"default": "",
|
||||||
|
"description": "Current value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output: email-input.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Email Input
|
||||||
|
|
||||||
|
A text input field for user data entry. Email address
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ Email │
|
||||||
|
│ Enter your email │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Focus State
|
||||||
|
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Email │┃
|
||||||
|
┃ john.doe@example.com ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error State
|
||||||
|
|
||||||
|
```
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ ⚠️ Email ┃
|
||||||
|
┃ invalid@email ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
❌ Please enter valid email address
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabled State
|
||||||
|
|
||||||
|
```
|
||||||
|
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
|
||||||
|
│ Email │
|
||||||
|
│ ────────────────────── │
|
||||||
|
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): input label text
|
||||||
|
- Default: "Email"
|
||||||
|
|
||||||
|
- `placeholder` (string, optional): Placeholder text when empty
|
||||||
|
- Default: "Enter your email"
|
||||||
|
|
||||||
|
- `value` (string, optional): Current value
|
||||||
|
- Default: ""
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: textbox
|
||||||
|
- **Focusable**: Yes
|
||||||
|
- **Keyboard Support**: Tab, Escape
|
||||||
|
- **ARIA Label**: Email address
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated by Fluxwing Screenshot Import*
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 3: Multi-Component Login Form
|
||||||
|
|
||||||
|
### Input: Vision Analysis Data
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"screen": {
|
||||||
|
"type": "login-form",
|
||||||
|
"name": "Login Screen",
|
||||||
|
"description": "User authentication screen with email/password inputs",
|
||||||
|
"layout": "vertical-center"
|
||||||
|
},
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"id": "email-input",
|
||||||
|
"type": "input",
|
||||||
|
"category": "atomic",
|
||||||
|
"visualProperties": { "width": 40, "height": 3, "borderStyle": "light", "textContent": "Email", "placeholder": "Enter your email" },
|
||||||
|
"states": ["default", "focus", "error"],
|
||||||
|
"accessibility": { "role": "textbox", "label": "Email address" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "password-input",
|
||||||
|
"type": "input",
|
||||||
|
"category": "atomic",
|
||||||
|
"visualProperties": { "width": 40, "height": 3, "borderStyle": "light", "textContent": "Password", "placeholder": "Enter your password" },
|
||||||
|
"states": ["default", "focus", "error"],
|
||||||
|
"accessibility": { "role": "textbox", "label": "Password" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "submit-button",
|
||||||
|
"type": "button",
|
||||||
|
"category": "atomic",
|
||||||
|
"visualProperties": { "width": 20, "height": 3, "borderStyle": "rounded", "textContent": "Sign In" },
|
||||||
|
"states": ["default", "hover", "disabled"],
|
||||||
|
"accessibility": { "role": "button", "label": "Sign in to account" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "login-form",
|
||||||
|
"type": "form",
|
||||||
|
"category": "composite",
|
||||||
|
"visualProperties": { "width": 50, "height": 20, "borderStyle": "rounded", "textContent": "Sign In" },
|
||||||
|
"states": ["default"],
|
||||||
|
"accessibility": { "role": "form", "label": "Login form" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"composition": {
|
||||||
|
"atomicComponents": ["email-input", "password-input", "submit-button"],
|
||||||
|
"compositeComponents": ["login-form"],
|
||||||
|
"screenComponents": ["login-screen"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Files Generated
|
||||||
|
|
||||||
|
1. **Atomic Components:**
|
||||||
|
- `./fluxwing/components/email-input.uxm`
|
||||||
|
- `./fluxwing/components/email-input.md`
|
||||||
|
- `./fluxwing/components/password-input.uxm`
|
||||||
|
- `./fluxwing/components/password-input.md`
|
||||||
|
- `./fluxwing/components/submit-button.uxm`
|
||||||
|
- `./fluxwing/components/submit-button.md`
|
||||||
|
|
||||||
|
2. **Composite Component:**
|
||||||
|
- `./fluxwing/components/login-form.uxm`
|
||||||
|
- `./fluxwing/components/login-form.md`
|
||||||
|
|
||||||
|
3. **Screen:**
|
||||||
|
- `./fluxwing/screens/login-screen.uxm`
|
||||||
|
- `./fluxwing/screens/login-screen.md`
|
||||||
|
- `./fluxwing/screens/login-screen.rendered.md`
|
||||||
|
|
||||||
|
### Output: login-form.uxm (composite)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "login-form",
|
||||||
|
"type": "form",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": "Login Form",
|
||||||
|
"description": "A form container for collecting user input. Login form",
|
||||||
|
"category": "utility"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": "Sign In",
|
||||||
|
"components": [
|
||||||
|
{ "id": "email-input", "slot": "field-1" },
|
||||||
|
{ "id": "password-input", "slot": "field-2" },
|
||||||
|
{ "id": "submit-button", "slot": "action" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": "login-form.md",
|
||||||
|
"width": 50,
|
||||||
|
"height": 20,
|
||||||
|
"variables": [
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"default": "Sign In",
|
||||||
|
"description": "Form title"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output: login-form.md (composite)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Login Form
|
||||||
|
|
||||||
|
A form container for collecting user input. Login form
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
╭────────────────────────────────────────────────╮
|
||||||
|
│ Sign In │
|
||||||
|
├────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ {{component:email-input}} │
|
||||||
|
│ │
|
||||||
|
│ {{component:password-input}} │
|
||||||
|
│ │
|
||||||
|
│ {{component:submit-button}} │
|
||||||
|
│ │
|
||||||
|
╰────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `title` (string, required): Form title
|
||||||
|
- Default: "Sign In"
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: form
|
||||||
|
- **Focusable**: No
|
||||||
|
- **ARIA Label**: Login form
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated by Fluxwing Screenshot Import*
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output: login-screen.rendered.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Login Screen
|
||||||
|
|
||||||
|
User authentication screen with email/password inputs
|
||||||
|
|
||||||
|
## Rendered Example
|
||||||
|
|
||||||
|
```
|
||||||
|
╭────────────────────────────────────────────────╮
|
||||||
|
│ Sign In │
|
||||||
|
├────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────┐ │
|
||||||
|
│ │ Email │ │
|
||||||
|
│ │ john.doe@example.com │ │
|
||||||
|
│ └──────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────────────────────────────┐ │
|
||||||
|
│ │ Password │ │
|
||||||
|
│ │ •••••••• │ │
|
||||||
|
│ └──────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ╭──────────────────╮ │
|
||||||
|
│ │▓▓▓▓Sign In▓▓▓▓▓▓▓│ │
|
||||||
|
│ ╰──────────────────╯ │
|
||||||
|
│ │
|
||||||
|
╰────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Data:**
|
||||||
|
- Email: john.doe@example.com
|
||||||
|
- Password: ••••••••
|
||||||
|
- Button: Sign In
|
||||||
|
|
||||||
|
**Actions:**
|
||||||
|
- Submit form → authenticate user
|
||||||
|
- Focus management → email → password → button
|
||||||
|
|
||||||
|
## Components Used
|
||||||
|
|
||||||
|
- `email-input` - Email Input (input)
|
||||||
|
- `password-input` - Password Input (input)
|
||||||
|
- `submit-button` - Submit Button (button)
|
||||||
|
- `login-form` - Login Form (form)
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated by Fluxwing Screenshot Import*
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 4: Checkbox Component
|
||||||
|
|
||||||
|
### Input: Vision Analysis Data
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "remember-me-checkbox",
|
||||||
|
"type": "checkbox",
|
||||||
|
"category": "atomic",
|
||||||
|
"visualProperties": {
|
||||||
|
"width": 20,
|
||||||
|
"height": 1,
|
||||||
|
"borderStyle": "none",
|
||||||
|
"textContent": "Remember me"
|
||||||
|
},
|
||||||
|
"states": ["default", "checked", "disabled"],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "checkbox",
|
||||||
|
"label": "Remember login credentials"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output: remember-me-checkbox.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Remember Me Checkbox
|
||||||
|
|
||||||
|
A checkbox input for boolean selection. Remember login credentials
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
[ ] Remember me
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checked State
|
||||||
|
|
||||||
|
```
|
||||||
|
[✓] Remember me
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabled State
|
||||||
|
|
||||||
|
```
|
||||||
|
[─] Remember me
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): checkbox label text
|
||||||
|
- Default: "Remember me"
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: checkbox
|
||||||
|
- **Focusable**: Yes
|
||||||
|
- **Keyboard Support**: Space
|
||||||
|
- **ARIA Label**: Remember login credentials
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated by Fluxwing Screenshot Import*
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 5: Badge Component
|
||||||
|
|
||||||
|
### Input: Vision Analysis Data
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "new-badge",
|
||||||
|
"type": "badge",
|
||||||
|
"category": "atomic",
|
||||||
|
"visualProperties": {
|
||||||
|
"width": 8,
|
||||||
|
"height": 1,
|
||||||
|
"borderStyle": "none",
|
||||||
|
"textContent": "New"
|
||||||
|
},
|
||||||
|
"states": ["default"],
|
||||||
|
"accessibility": {
|
||||||
|
"role": "status",
|
||||||
|
"label": "New item indicator"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output: new-badge.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# New Badge
|
||||||
|
|
||||||
|
A small badge component for labels or counts. New item indicator
|
||||||
|
|
||||||
|
## Default State
|
||||||
|
|
||||||
|
```
|
||||||
|
▓ New ▓
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
- `text` (string, required): badge label text
|
||||||
|
- Default: "New"
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
- **Role**: status
|
||||||
|
- **Focusable**: No
|
||||||
|
- **ARIA Label**: New item indicator
|
||||||
|
|
||||||
|
---
|
||||||
|
*Generated by Fluxwing Screenshot Import*
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage Patterns
|
||||||
|
|
||||||
|
### Pattern 1: Generate Single Atomic Component
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const uxmData = generateAtomicUXM(visionComponentData, timestamp);
|
||||||
|
const mdContent = generateAtomicMD(visionComponentData, uxmData);
|
||||||
|
await saveAtomicComponent(componentId, uxmData, mdContent);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 2: Generate All Components from Screen
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 1. Generate atomics first
|
||||||
|
for (const atomicId of composition.atomicComponents) {
|
||||||
|
const componentData = components.find(c => c.id === atomicId);
|
||||||
|
await generateAtomicComponent(componentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Generate composites (can reference atomics)
|
||||||
|
for (const compositeId of composition.compositeComponents) {
|
||||||
|
const componentData = components.find(c => c.id === compositeId);
|
||||||
|
await generateCompositeComponent(componentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Generate screen (references everything)
|
||||||
|
await generateScreen(screenData, composition);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
These examples demonstrate the complete screenshot-to-uxscii workflow:
|
||||||
|
|
||||||
|
1. **Vision analysis** produces structured JSON
|
||||||
|
2. **Helper functions** (see screenshot-import-helpers.md) transform data
|
||||||
|
3. **ASCII generation** (see screenshot-import-ascii.md) creates visual representations
|
||||||
|
4. **File generation** produces valid .uxm + .md pairs
|
||||||
|
5. **Validation** ensures quality and schema compliance
|
||||||
|
|
||||||
|
All generated files conform to:
|
||||||
|
- `fluxwing/data/schema/uxm-component.schema.json` - JSON Schema
|
||||||
|
- Quality standards documented in the schema
|
||||||
@@ -0,0 +1,610 @@
|
|||||||
|
# Screenshot Import Helper Functions
|
||||||
|
|
||||||
|
This document contains all helper functions used during screenshot-to-uxscii conversion. These functions transform vision analysis data into valid uxscii component structures.
|
||||||
|
|
||||||
|
## Component Metadata Helpers
|
||||||
|
|
||||||
|
### mapTypeToCategory()
|
||||||
|
|
||||||
|
Maps component type to UXM category for proper organization:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function mapTypeToCategory(componentType: string): string {
|
||||||
|
const categoryMap = {
|
||||||
|
// Input category - interactive data entry
|
||||||
|
'input': 'input', 'checkbox': 'input', 'radio': 'input',
|
||||||
|
'select': 'input', 'slider': 'input', 'toggle': 'input',
|
||||||
|
|
||||||
|
// Layout category - structural containers
|
||||||
|
'container': 'layout', 'card': 'layout', 'panel': 'layout',
|
||||||
|
'tabs': 'layout', 'fieldset': 'layout',
|
||||||
|
|
||||||
|
// Display category - content presentation
|
||||||
|
'text': 'display', 'heading': 'display', 'label': 'display',
|
||||||
|
'badge': 'display', 'icon': 'display', 'image': 'display',
|
||||||
|
'divider': 'display',
|
||||||
|
|
||||||
|
// Navigation category - movement and wayfinding
|
||||||
|
'navigation': 'navigation', 'breadcrumb': 'navigation',
|
||||||
|
'pagination': 'navigation', 'link': 'navigation',
|
||||||
|
|
||||||
|
// Feedback category - system responses
|
||||||
|
'alert': 'feedback', 'toast': 'feedback', 'progress': 'feedback',
|
||||||
|
'spinner': 'feedback',
|
||||||
|
|
||||||
|
// Utility category - action triggers
|
||||||
|
'button': 'utility', 'form': 'utility',
|
||||||
|
|
||||||
|
// Overlay category - modal displays
|
||||||
|
'modal': 'overlay',
|
||||||
|
|
||||||
|
// Data category - structured information
|
||||||
|
'list': 'data', 'table': 'data', 'tree': 'data', 'chart': 'data'
|
||||||
|
};
|
||||||
|
|
||||||
|
return categoryMap[componentType] || 'custom';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### generateComponentName()
|
||||||
|
|
||||||
|
Creates human-readable component name from kebab-case ID:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateComponentName(componentId: string): string {
|
||||||
|
// Convert kebab-case to Title Case
|
||||||
|
return componentId
|
||||||
|
.split('-')
|
||||||
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Examples:
|
||||||
|
// "email-input" → "Email Input"
|
||||||
|
// "submit-button" → "Submit Button"
|
||||||
|
// "user-profile-card" → "User Profile Card"
|
||||||
|
```
|
||||||
|
|
||||||
|
### generateComponentDescription()
|
||||||
|
|
||||||
|
Creates description based on type, properties, and accessibility:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateComponentDescription(
|
||||||
|
componentType: string,
|
||||||
|
visualProperties: any,
|
||||||
|
accessibility: any
|
||||||
|
): string {
|
||||||
|
const typeDescriptions = {
|
||||||
|
'button': 'An interactive button component for user actions',
|
||||||
|
'input': 'A text input field for user data entry',
|
||||||
|
'checkbox': 'A checkbox input for boolean selection',
|
||||||
|
'radio': 'A radio button for single selection from a group',
|
||||||
|
'select': 'A dropdown select component for choosing from options',
|
||||||
|
'card': 'A container component for grouping related content',
|
||||||
|
'modal': 'An overlay component for focused interactions',
|
||||||
|
'alert': 'A feedback component for displaying messages',
|
||||||
|
'navigation': 'A navigation component for site/app navigation',
|
||||||
|
'form': 'A form container for collecting user input',
|
||||||
|
'badge': 'A small badge component for labels or counts',
|
||||||
|
'icon': 'An icon component for visual symbols',
|
||||||
|
'text': 'A text display component',
|
||||||
|
'heading': 'A heading component for section titles',
|
||||||
|
'divider': 'A visual divider for separating content'
|
||||||
|
};
|
||||||
|
|
||||||
|
let description = typeDescriptions[componentType] || `A ${componentType} component`;
|
||||||
|
|
||||||
|
// Add context from accessibility label if available
|
||||||
|
if (accessibility?.label && accessibility.label !== visualProperties.textContent) {
|
||||||
|
description += `. ${accessibility.label}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Behavior Helpers
|
||||||
|
|
||||||
|
### inferBackground()
|
||||||
|
|
||||||
|
Determines background fill pattern based on component type:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function inferBackground(componentType: string): string {
|
||||||
|
const backgroundMap = {
|
||||||
|
'button': 'filled', // Buttons have solid background
|
||||||
|
'input': 'transparent', // Inputs are hollow
|
||||||
|
'card': 'filled', // Cards have background
|
||||||
|
'modal': 'filled', // Modals have background
|
||||||
|
'alert': 'filled', // Alerts have background
|
||||||
|
'badge': 'filled', // Badges have background
|
||||||
|
'panel': 'filled', // Panels have background
|
||||||
|
'toast': 'filled' // Toasts have background
|
||||||
|
};
|
||||||
|
|
||||||
|
return backgroundMap[componentType] || 'transparent';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### generateInteractions()
|
||||||
|
|
||||||
|
Generates interaction array based on component type:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateInteractions(componentType: string): string[] {
|
||||||
|
const interactionMap = {
|
||||||
|
'button': ['click', 'keyboard'],
|
||||||
|
'input': ['click', 'keyboard', 'type'],
|
||||||
|
'checkbox': ['click', 'keyboard'],
|
||||||
|
'radio': ['click', 'keyboard'],
|
||||||
|
'select': ['click', 'keyboard'],
|
||||||
|
'slider': ['click', 'drag', 'keyboard'],
|
||||||
|
'toggle': ['click', 'keyboard'],
|
||||||
|
'link': ['click', 'keyboard'],
|
||||||
|
'tabs': ['click', 'keyboard'],
|
||||||
|
'navigation': ['click', 'keyboard'],
|
||||||
|
'modal': ['click', 'keyboard'], // Close on ESC
|
||||||
|
'toast': [], // No interaction (auto-dismiss)
|
||||||
|
'progress': [], // No interaction (passive display)
|
||||||
|
'spinner': [] // No interaction (passive display)
|
||||||
|
};
|
||||||
|
|
||||||
|
return interactionMap[componentType] || [];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### isFocusable()
|
||||||
|
|
||||||
|
Determines if component should be focusable for keyboard navigation:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function isFocusable(componentType: string): boolean {
|
||||||
|
const focusableTypes = [
|
||||||
|
'button', 'input', 'checkbox', 'radio', 'select',
|
||||||
|
'slider', 'toggle', 'link', 'tabs', 'navigation'
|
||||||
|
];
|
||||||
|
|
||||||
|
return focusableTypes.includes(componentType);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### generateKeyboardSupport()
|
||||||
|
|
||||||
|
Generates keyboard shortcuts based on component type:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateKeyboardSupport(componentType: string): string[] {
|
||||||
|
const keyboardMap = {
|
||||||
|
'button': ['Enter', 'Space'],
|
||||||
|
'input': ['Tab', 'Escape'],
|
||||||
|
'checkbox': ['Space'],
|
||||||
|
'radio': ['Arrow keys'],
|
||||||
|
'select': ['Arrow keys', 'Enter', 'Escape'],
|
||||||
|
'slider': ['Arrow keys', 'Home', 'End'],
|
||||||
|
'toggle': ['Space'],
|
||||||
|
'link': ['Enter'],
|
||||||
|
'tabs': ['Arrow keys', 'Home', 'End'],
|
||||||
|
'navigation': ['Arrow keys', 'Enter'],
|
||||||
|
'modal': ['Escape']
|
||||||
|
};
|
||||||
|
|
||||||
|
return keyboardMap[componentType] || [];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### generateStatesFromList()
|
||||||
|
|
||||||
|
Creates state objects for each state in the states array:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateStatesFromList(
|
||||||
|
states: string[],
|
||||||
|
baseProperties: any,
|
||||||
|
componentType: string
|
||||||
|
): any[] {
|
||||||
|
const stateObjects = [];
|
||||||
|
|
||||||
|
for (const stateName of states) {
|
||||||
|
if (stateName === 'default') continue; // Skip default, handled separately
|
||||||
|
|
||||||
|
const stateProperties: any = {};
|
||||||
|
|
||||||
|
// State-specific border styles
|
||||||
|
if (stateName === 'hover' || stateName === 'focus') {
|
||||||
|
stateProperties.border = 'heavy';
|
||||||
|
} else if (stateName === 'disabled') {
|
||||||
|
stateProperties.border = 'dashed';
|
||||||
|
stateProperties.opacity = 0.5;
|
||||||
|
stateProperties.cursor = 'not-allowed';
|
||||||
|
} else if (stateName === 'error') {
|
||||||
|
stateProperties.border = 'heavy';
|
||||||
|
stateProperties.borderColor = 'red';
|
||||||
|
} else if (stateName === 'success') {
|
||||||
|
stateProperties.border = 'heavy';
|
||||||
|
stateProperties.borderColor = 'green';
|
||||||
|
} else if (stateName === 'loading') {
|
||||||
|
stateProperties.opacity = 0.7;
|
||||||
|
stateProperties.cursor = 'wait';
|
||||||
|
} else if (stateName === 'active') {
|
||||||
|
stateProperties.border = 'heavy';
|
||||||
|
stateProperties.background = 'filled';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy base properties and merge with state-specific ones
|
||||||
|
stateObjects.push({
|
||||||
|
name: stateName,
|
||||||
|
properties: { ...baseProperties, ...stateProperties }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateObjects;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### generateMinimalDefaultState()
|
||||||
|
|
||||||
|
Creates a single default state object for MVP component creation (fast mode):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateMinimalDefaultState(
|
||||||
|
visualProperties: any,
|
||||||
|
componentType: string
|
||||||
|
): any {
|
||||||
|
return {
|
||||||
|
name: 'default',
|
||||||
|
properties: {
|
||||||
|
border: visualProperties.borderStyle || 'light',
|
||||||
|
background: inferBackground(componentType),
|
||||||
|
textColor: 'default'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usage**: This function creates ONLY the default state, enabling fast MVP component creation. Use `generateStatesFromList()` later when expanding components with `/fluxwing-expand-component`.
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```typescript
|
||||||
|
// Minimal mode - single state
|
||||||
|
const minimalStates = [generateMinimalDefaultState(visualProps, 'button')];
|
||||||
|
|
||||||
|
// Full mode - multiple states
|
||||||
|
const fullStates = [
|
||||||
|
generateMinimalDefaultState(visualProps, 'button'),
|
||||||
|
...generateStatesFromList(['hover', 'active', 'disabled'], baseProps, 'button')
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Layout Helpers
|
||||||
|
|
||||||
|
### inferDisplay()
|
||||||
|
|
||||||
|
Determines CSS display property based on component type:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function inferDisplay(componentType: string): string {
|
||||||
|
const displayMap = {
|
||||||
|
'button': 'inline-block',
|
||||||
|
'input': 'inline-block',
|
||||||
|
'checkbox': 'inline-block',
|
||||||
|
'radio': 'inline-block',
|
||||||
|
'badge': 'inline',
|
||||||
|
'link': 'inline',
|
||||||
|
'text': 'inline',
|
||||||
|
'heading': 'block',
|
||||||
|
'divider': 'block',
|
||||||
|
'card': 'block',
|
||||||
|
'panel': 'block',
|
||||||
|
'modal': 'block',
|
||||||
|
'container': 'block',
|
||||||
|
'form': 'block',
|
||||||
|
'list': 'block',
|
||||||
|
'table': 'block'
|
||||||
|
};
|
||||||
|
|
||||||
|
return displayMap[componentType] || 'block';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### generateSpacing()
|
||||||
|
|
||||||
|
Calculates padding based on component dimensions (~10% of size):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateSpacing(width: number, height: number): any {
|
||||||
|
const paddingX = Math.max(1, Math.floor(width * 0.1));
|
||||||
|
const paddingY = Math.max(1, Math.floor(height * 0.1));
|
||||||
|
|
||||||
|
return {
|
||||||
|
padding: {
|
||||||
|
x: paddingX,
|
||||||
|
y: paddingY
|
||||||
|
},
|
||||||
|
margin: {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Props & Variables Helpers
|
||||||
|
|
||||||
|
### extractVariables()
|
||||||
|
|
||||||
|
Extracts variable definitions from visual properties:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function extractVariables(
|
||||||
|
visualProperties: any,
|
||||||
|
componentType: string
|
||||||
|
): any[] {
|
||||||
|
const variables: any[] = [];
|
||||||
|
|
||||||
|
// Text content variable (common to most components)
|
||||||
|
if (visualProperties.textContent) {
|
||||||
|
variables.push({
|
||||||
|
name: 'text',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: visualProperties.textContent,
|
||||||
|
description: `${componentType} label text`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholder variable (for inputs)
|
||||||
|
if (visualProperties.placeholder) {
|
||||||
|
variables.push({
|
||||||
|
name: 'placeholder',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
default: visualProperties.placeholder,
|
||||||
|
description: 'Placeholder text when empty'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value variable (for inputs/displays)
|
||||||
|
if (['input', 'select', 'text'].includes(componentType)) {
|
||||||
|
variables.push({
|
||||||
|
name: 'value',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
|
description: 'Current value'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variant variable (for buttons)
|
||||||
|
if (componentType === 'button') {
|
||||||
|
variables.push({
|
||||||
|
name: 'variant',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
default: 'primary',
|
||||||
|
description: 'Button style variant (primary, secondary, danger)'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size variable (for scalable components)
|
||||||
|
if (['button', 'input', 'badge'].includes(componentType)) {
|
||||||
|
variables.push({
|
||||||
|
name: 'size',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
default: 'medium',
|
||||||
|
description: 'Component size (small, medium, large)'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### extractPropsFromVisualProperties()
|
||||||
|
|
||||||
|
Extract component props based on type and visual properties:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function extractPropsFromVisualProperties(
|
||||||
|
visualProperties: any,
|
||||||
|
componentType: string
|
||||||
|
): any {
|
||||||
|
const props: any = {};
|
||||||
|
|
||||||
|
// Text content (most components)
|
||||||
|
if (visualProperties.textContent) {
|
||||||
|
props.text = visualProperties.textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholder (inputs)
|
||||||
|
if (visualProperties.placeholder) {
|
||||||
|
props.placeholder = visualProperties.placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-specific props
|
||||||
|
if (componentType === 'button') {
|
||||||
|
props.variant = inferButtonVariant(visualProperties);
|
||||||
|
props.size = inferSize(visualProperties.width, visualProperties.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (componentType === 'input') {
|
||||||
|
props.type = inferInputType(visualProperties);
|
||||||
|
props.size = inferSize(visualProperties.width, visualProperties.height);
|
||||||
|
props.maxLength = Math.floor(visualProperties.width * 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (componentType === 'checkbox' || componentType === 'radio') {
|
||||||
|
props.label = visualProperties.textContent;
|
||||||
|
props.checked = false; // Default unchecked
|
||||||
|
}
|
||||||
|
|
||||||
|
if (componentType === 'badge') {
|
||||||
|
props.variant = inferBadgeVariant(visualProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (componentType === 'icon') {
|
||||||
|
props.name = visualProperties.textContent || 'icon';
|
||||||
|
props.size = inferSize(visualProperties.width, visualProperties.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inference Helpers
|
||||||
|
|
||||||
|
### inferButtonVariant()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function inferButtonVariant(vp: any): string {
|
||||||
|
// Primary buttons typically have filled backgrounds
|
||||||
|
// Secondary buttons have borders
|
||||||
|
if (vp.borderStyle === 'heavy' || vp.borderStyle === 'double') {
|
||||||
|
return 'primary';
|
||||||
|
}
|
||||||
|
return 'secondary';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### inferInputType()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function inferInputType(vp: any): string {
|
||||||
|
const text = vp.textContent?.toLowerCase() || '';
|
||||||
|
const placeholder = vp.placeholder?.toLowerCase() || '';
|
||||||
|
|
||||||
|
if (text.includes('password') || placeholder.includes('password')) {
|
||||||
|
return 'password';
|
||||||
|
}
|
||||||
|
if (text.includes('email') || placeholder.includes('email')) {
|
||||||
|
return 'email';
|
||||||
|
}
|
||||||
|
if (text.includes('number') || placeholder.includes('number')) {
|
||||||
|
return 'number';
|
||||||
|
}
|
||||||
|
if (text.includes('search')) {
|
||||||
|
return 'search';
|
||||||
|
}
|
||||||
|
return 'text';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### inferSize()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function inferSize(width: number, height: number): string {
|
||||||
|
// Small: < 20 width or < 3 height
|
||||||
|
// Medium: 20-40 width, 3-5 height
|
||||||
|
// Large: > 40 width or > 5 height
|
||||||
|
if (width < 20 || height < 3) return 'small';
|
||||||
|
if (width > 40 || height > 5) return 'large';
|
||||||
|
return 'medium';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### inferBadgeVariant()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function inferBadgeVariant(vp: any): string {
|
||||||
|
// Could be inferred from color analysis in future
|
||||||
|
// For now, default to neutral
|
||||||
|
return 'neutral';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### inferAdditionalTags()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function inferAdditionalTags(type: string, vp: any): string[] {
|
||||||
|
const tags: string[] = [];
|
||||||
|
|
||||||
|
// Add category tags
|
||||||
|
const category = mapTypeToCategory(type);
|
||||||
|
if (category !== 'custom') {
|
||||||
|
tags.push(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add interaction tags
|
||||||
|
if (isFocusable(type)) {
|
||||||
|
tags.push('interactive');
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Pattern
|
||||||
|
|
||||||
|
These helper functions work together to transform vision analysis into complete .uxm files:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Example: Generate atomic component .uxm
|
||||||
|
function generateAtomicUXM(componentData: any, timestamp: string): any {
|
||||||
|
const { id, type, visualProperties, states, accessibility } = componentData;
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": id,
|
||||||
|
"type": type,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": generateComponentName(id),
|
||||||
|
"description": generateComponentDescription(type, visualProperties, accessibility),
|
||||||
|
"author": "Fluxwing Screenshot Import",
|
||||||
|
"created": timestamp,
|
||||||
|
"modified": timestamp,
|
||||||
|
"tags": [type, "imported", "screenshot-generated", ...inferAdditionalTags(type, visualProperties)],
|
||||||
|
"category": mapTypeToCategory(type)
|
||||||
|
},
|
||||||
|
"props": extractPropsFromVisualProperties(visualProperties, type),
|
||||||
|
"behavior": {
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"properties": {
|
||||||
|
"border": visualProperties.borderStyle,
|
||||||
|
"background": inferBackground(type),
|
||||||
|
"textColor": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...generateStatesFromList(
|
||||||
|
states.filter(s => s !== 'default'),
|
||||||
|
{ border: visualProperties.borderStyle, background: inferBackground(type), textColor: "default" },
|
||||||
|
type
|
||||||
|
)
|
||||||
|
],
|
||||||
|
"interactions": generateInteractions(type),
|
||||||
|
"accessibility": {
|
||||||
|
"role": accessibility.role,
|
||||||
|
"focusable": isFocusable(type),
|
||||||
|
"keyboardSupport": generateKeyboardSupport(type),
|
||||||
|
"ariaLabel": accessibility.label || visualProperties.textContent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"display": inferDisplay(type),
|
||||||
|
"positioning": "static",
|
||||||
|
"spacing": generateSpacing(visualProperties.width, visualProperties.height),
|
||||||
|
"sizing": {
|
||||||
|
"minWidth": visualProperties.width,
|
||||||
|
"height": visualProperties.height
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": `${id}.md`,
|
||||||
|
"width": visualProperties.width,
|
||||||
|
"height": visualProperties.height,
|
||||||
|
"variables": extractVariables(visualProperties, type)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
These functions implement the uxscii component specification documented in:
|
||||||
|
- `fluxwing/data/docs/01-uxscii-specification.md` - Format specification
|
||||||
|
- `fluxwing/data/docs/03-component-creation.md` - Component structure
|
||||||
|
- `fluxwing/data/schema/uxm-component.schema.json` - JSON Schema validation
|
||||||
|
|
||||||
|
Use these helpers to ensure consistent, valid component generation from screenshot analysis data.
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
# Screenshot Import Screen Generation
|
||||||
|
|
||||||
|
This document provides helper functions for generating screen files (.uxm, .md, .rendered.md) during screenshot import.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Screen generation creates 3 files:
|
||||||
|
1. **`.uxm`** - Screen metadata and component references
|
||||||
|
2. **`.md`** - Template with `{{component:id}}` references
|
||||||
|
3. **`.rendered.md`** - Actual ASCII preview with real data
|
||||||
|
|
||||||
|
## Step 1: Generate Screen Metadata (.uxm)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateScreenUxm(mergedData) {
|
||||||
|
const screenId = `${mergedData.screen.type}-screen`;
|
||||||
|
const screenName = mergedData.screen.name;
|
||||||
|
const screenDescription = mergedData.screen.description;
|
||||||
|
const allComponentIds = [
|
||||||
|
...mergedData.composition.atomicComponents,
|
||||||
|
...mergedData.composition.compositeComponents
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": screenId,
|
||||||
|
"type": "container",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"name": screenName,
|
||||||
|
"description": screenDescription,
|
||||||
|
"author": "Fluxwing Screenshot Import",
|
||||||
|
"created": new Date().toISOString(),
|
||||||
|
"modified": new Date().toISOString(),
|
||||||
|
"tags": ["screen", mergedData.screen.type, "imported"],
|
||||||
|
"category": "layout"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"title": screenName,
|
||||||
|
"layout": mergedData.screen.layout,
|
||||||
|
"components": allComponentIds
|
||||||
|
},
|
||||||
|
"ascii": {
|
||||||
|
"templateFile": `${screenId}.md`,
|
||||||
|
"width": 80,
|
||||||
|
"height": 40
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Generate Screen Template (.md)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateScreenTemplate(screenId, screenName, description, components, mergedData) {
|
||||||
|
let markdown = `# ${screenName}\n\n${description}\n\n`;
|
||||||
|
|
||||||
|
markdown += `## Layout\n\n\`\`\`\n`;
|
||||||
|
|
||||||
|
// Reference all components
|
||||||
|
for (const compId of components) {
|
||||||
|
markdown += `{{component:${compId}}}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown += '\`\`\`\n\n';
|
||||||
|
|
||||||
|
markdown += `## Components Used\n\n`;
|
||||||
|
for (const compId of components) {
|
||||||
|
const comp = mergedData.components.find(c => c.id === compId);
|
||||||
|
const compName = comp ? generateComponentName(compId) : compId;
|
||||||
|
markdown += `- \`${compId}\` - ${compName} (${comp?.type || 'unknown'})\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: Convert kebab-case ID to Title Case name
|
||||||
|
function generateComponentName(id) {
|
||||||
|
return id.split('-').map(word =>
|
||||||
|
word.charAt(0).toUpperCase() + word.slice(1)
|
||||||
|
).join(' ');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3: Generate Rendered Screen (.rendered.md)
|
||||||
|
|
||||||
|
**CRITICAL:** Embed actual ASCII from component files, NOT `{{variables}}`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function generateScreenRendered(screenId, screenName, description, mergedData) {
|
||||||
|
let markdown = `# ${screenName}\n\n`;
|
||||||
|
|
||||||
|
markdown += `## Rendered Example\n\n\`\`\`\n`;
|
||||||
|
|
||||||
|
// Build complete rendered layout
|
||||||
|
// Stack all components vertically (user can refine layout)
|
||||||
|
const allComponentIds = [
|
||||||
|
...mergedData.composition.atomicComponents,
|
||||||
|
...mergedData.composition.compositeComponents
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const compId of allComponentIds) {
|
||||||
|
// Read component .md file
|
||||||
|
const compMdPath = `./fluxwing/components/${compId}.md`;
|
||||||
|
try {
|
||||||
|
const compMdContent = await read(compMdPath);
|
||||||
|
|
||||||
|
// Extract default state ASCII (between first ``` pair after "## Default State")
|
||||||
|
const asciiMatch = compMdContent.match(/## Default State\n\n```\n([\s\S]*?)\n```/);
|
||||||
|
if (asciiMatch) {
|
||||||
|
markdown += asciiMatch[1] + '\n\n';
|
||||||
|
} else {
|
||||||
|
markdown += `[Component ${compId} - ASCII not found]\n\n`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
markdown += `[Component ${compId} - File not found]\n\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown += '\`\`\`\n\n';
|
||||||
|
|
||||||
|
// Add example data section
|
||||||
|
markdown += `**Example Data:**\n`;
|
||||||
|
const exampleData = generateExampleData(mergedData.screen.type);
|
||||||
|
for (const [key, value] of Object.entries(exampleData)) {
|
||||||
|
markdown += `- ${key}: ${value}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Generate Example Data by Screen Type
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function generateExampleData(screenType) {
|
||||||
|
const examples = {
|
||||||
|
"login": {
|
||||||
|
"Email": "john.doe@example.com",
|
||||||
|
"Password": "••••••••",
|
||||||
|
"Button": "Sign In"
|
||||||
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"Revenue": "$24,567",
|
||||||
|
"Users": "1,234",
|
||||||
|
"Growth": "+12.5%"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"Name": "Jane Smith",
|
||||||
|
"Role": "Product Manager",
|
||||||
|
"Email": "jane.smith@company.com"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"Theme": "Dark Mode",
|
||||||
|
"Language": "English",
|
||||||
|
"Notifications": "Enabled"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"Field 1": "Example value",
|
||||||
|
"Field 2": "Another value"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return examples[screenType] || examples["form"];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Write All Files Concurrently
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function writeScreenFiles(screenId, screenUxm, screenMd, screenRendered) {
|
||||||
|
// Create screens directory
|
||||||
|
await bash('mkdir -p ./fluxwing/screens');
|
||||||
|
|
||||||
|
const screenDir = './fluxwing/screens';
|
||||||
|
const uxmPath = `${screenDir}/${screenId}.uxm`;
|
||||||
|
const mdPath = `${screenDir}/${screenId}.md`;
|
||||||
|
const renderedPath = `${screenDir}/${screenId}.rendered.md`;
|
||||||
|
|
||||||
|
// Write all 3 files concurrently
|
||||||
|
await Promise.all([
|
||||||
|
write(uxmPath, JSON.stringify(screenUxm, null, 2)),
|
||||||
|
write(mdPath, screenMd),
|
||||||
|
write(renderedPath, screenRendered)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return { uxmPath, mdPath, renderedPath };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Workflow
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function generateScreen(mergedData) {
|
||||||
|
const screenId = `${mergedData.screen.type}-screen`;
|
||||||
|
const allComponentIds = [
|
||||||
|
...mergedData.composition.atomicComponents,
|
||||||
|
...mergedData.composition.compositeComponents
|
||||||
|
];
|
||||||
|
|
||||||
|
// Generate all content
|
||||||
|
const screenUxm = generateScreenUxm(mergedData);
|
||||||
|
const screenMd = generateScreenTemplate(
|
||||||
|
screenId,
|
||||||
|
mergedData.screen.name,
|
||||||
|
mergedData.screen.description,
|
||||||
|
allComponentIds,
|
||||||
|
mergedData
|
||||||
|
);
|
||||||
|
const screenRendered = await generateScreenRendered(
|
||||||
|
screenId,
|
||||||
|
mergedData.screen.name,
|
||||||
|
mergedData.screen.description,
|
||||||
|
mergedData
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write all files
|
||||||
|
const paths = await writeScreenFiles(screenId, screenUxm, screenMd, screenRendered);
|
||||||
|
|
||||||
|
console.log(`✓ Created: ${paths.uxmPath}`);
|
||||||
|
console.log(`✓ Created: ${paths.mdPath}`);
|
||||||
|
console.log(`✓ Created: ${paths.renderedPath}`);
|
||||||
|
|
||||||
|
return { screenId, ...paths };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const screenResult = await generateScreen(mergedData);
|
||||||
|
console.log(`✅ Screen generated with ${allComponentIds.length} components`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Failed to generate screen: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
# Screenshot Import Validation Functions
|
||||||
|
|
||||||
|
This document provides validation function implementations for verifying generated uxscii components during screenshot import.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Five validation checks run concurrently for each component:
|
||||||
|
|
||||||
|
1. **validateSchema()** - Check against JSON Schema
|
||||||
|
2. **validateFileIntegrity()** - Check template file exists and matches
|
||||||
|
3. **validateVariableConsistency()** - Check variables are defined and used
|
||||||
|
4. **validateComponentReferences()** - Check referenced components exist
|
||||||
|
5. **validateBestPractices()** - Check quality standards (warnings only)
|
||||||
|
|
||||||
|
## Validation Function Implementations
|
||||||
|
|
||||||
|
### 1. validateSchema()
|
||||||
|
|
||||||
|
Check against JSON Schema:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function validateSchema(uxmPath) {
|
||||||
|
const schemaPath = '{PLUGIN_ROOT}/data/schema/uxm-component.schema.json';
|
||||||
|
const schema = JSON.parse(await read(schemaPath));
|
||||||
|
const uxmData = JSON.parse(await read(uxmPath));
|
||||||
|
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
// Required fields
|
||||||
|
if (!uxmData.id || !uxmData.type || !uxmData.version || !uxmData.metadata) {
|
||||||
|
errors.push("Missing required top-level fields");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID format
|
||||||
|
if (uxmData.id && !uxmData.id.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)) {
|
||||||
|
errors.push(`Invalid ID format: ${uxmData.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version format
|
||||||
|
if (uxmData.version && !uxmData.version.match(/^\d+\.\d+\.\d+$/)) {
|
||||||
|
errors.push(`Invalid version format: ${uxmData.version}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw new Error(`Schema validation errors: ${errors.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { passed: true, message: "Schema compliance verified" };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. validateFileIntegrity()
|
||||||
|
|
||||||
|
Check template file exists and matches:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function validateFileIntegrity(uxmPath, mdPath) {
|
||||||
|
const uxmData = JSON.parse(await read(uxmPath));
|
||||||
|
const expectedTemplateName = uxmData.ascii.templateFile;
|
||||||
|
|
||||||
|
// Check .md file exists
|
||||||
|
try {
|
||||||
|
await read(mdPath);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Template file missing: ${mdPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check filename matches reference
|
||||||
|
const actualFileName = mdPath.split('/').pop();
|
||||||
|
if (expectedTemplateName !== actualFileName) {
|
||||||
|
throw new Error(`Template filename mismatch: expected ${expectedTemplateName}, got ${actualFileName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { passed: true, message: "File integrity verified" };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. validateVariableConsistency()
|
||||||
|
|
||||||
|
Check variables are defined and used:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function validateVariableConsistency(uxmPath, mdPath) {
|
||||||
|
const uxmData = JSON.parse(await read(uxmPath));
|
||||||
|
const mdContent = await read(mdPath);
|
||||||
|
|
||||||
|
// Extract defined variables from .uxm
|
||||||
|
const definedVariables = uxmData.ascii?.variables ? uxmData.ascii.variables.map(v => v.name) : [];
|
||||||
|
|
||||||
|
// Extract used variables from .md (matches {{variableName}} but NOT {{component:id}})
|
||||||
|
const usedVariables = [...mdContent.matchAll(/\{\{(?!component:)(\w+)\}\}/g)].map(m => m[1]);
|
||||||
|
|
||||||
|
// Check all used variables are defined
|
||||||
|
const undefinedVars = usedVariables.filter(v => !definedVariables.includes(v));
|
||||||
|
if (undefinedVars.length > 0) {
|
||||||
|
throw new Error(`Undefined variables in template: ${undefinedVars.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn about unused variables (non-blocking)
|
||||||
|
const unusedVars = definedVariables.filter(v => !usedVariables.includes(v));
|
||||||
|
if (unusedVars.length > 0) {
|
||||||
|
console.warn(`⚠️ Unused variables defined in ${uxmPath}: ${unusedVars.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { passed: true, message: "Variable consistency verified", warnings: unusedVars.length };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. validateComponentReferences()
|
||||||
|
|
||||||
|
Check referenced components exist:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function validateComponentReferences(uxmPath) {
|
||||||
|
const uxmData = JSON.parse(await read(uxmPath));
|
||||||
|
|
||||||
|
// Check if component has child references
|
||||||
|
if (!uxmData.props.components || uxmData.props.components.length === 0) {
|
||||||
|
return { passed: true, message: "No component references to validate" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each referenced component exists
|
||||||
|
for (const ref of uxmData.props.components) {
|
||||||
|
const refPath = `./fluxwing/components/${ref.id}.uxm`;
|
||||||
|
try {
|
||||||
|
await read(refPath);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Referenced component not found: ${ref.id} (expected at ${refPath})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { passed: true, message: `${uxmData.props.components.length} component references verified` };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. validateBestPractices()
|
||||||
|
|
||||||
|
Check quality standards (warnings only):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function validateBestPractices(uxmPath) {
|
||||||
|
const uxmData = JSON.parse(await read(uxmPath));
|
||||||
|
const warnings = [];
|
||||||
|
|
||||||
|
// Check multiple states
|
||||||
|
if (uxmData.behavior?.states && uxmData.behavior.states.length < 3) {
|
||||||
|
warnings.push("Consider adding more states (recommended: 3+)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check accessibility
|
||||||
|
if (!uxmData.behavior?.accessibility?.ariaLabel) {
|
||||||
|
warnings.push("Missing ARIA label for accessibility");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check description
|
||||||
|
if (!uxmData.metadata.description || uxmData.metadata.description.length < 10) {
|
||||||
|
warnings.push("Description is too brief");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tags
|
||||||
|
if (!uxmData.metadata.tags || uxmData.metadata.tags.length < 2) {
|
||||||
|
warnings.push("Add more tags for discoverability");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warnings.length > 0) {
|
||||||
|
console.warn(`⚠️ Best practices warnings for ${uxmData.id}:\n - ${warnings.join('\n - ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { passed: true, warnings: warnings.length, messages: warnings };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Pattern
|
||||||
|
|
||||||
|
Run all 5 validations concurrently for each component:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const validationResults = await Promise.all(
|
||||||
|
allFiles.map(async (fileSet) => {
|
||||||
|
const { uxmPath, mdPath, id } = fileSet;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [
|
||||||
|
schemaResult,
|
||||||
|
integrityResult,
|
||||||
|
variableResult,
|
||||||
|
referenceResult,
|
||||||
|
practicesResult
|
||||||
|
] = await Promise.all([
|
||||||
|
validateSchema(uxmPath),
|
||||||
|
validateFileIntegrity(uxmPath, mdPath),
|
||||||
|
validateVariableConsistency(uxmPath, mdPath),
|
||||||
|
validateComponentReferences(uxmPath),
|
||||||
|
validateBestPractices(uxmPath)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
componentId: id,
|
||||||
|
success: true,
|
||||||
|
checks: {
|
||||||
|
schema: schemaResult,
|
||||||
|
integrity: integrityResult,
|
||||||
|
variables: variableResult,
|
||||||
|
references: referenceResult,
|
||||||
|
practices: practicesResult
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Validation failed for ${id}: ${error.message}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
- **Schema, Integrity, Variables, References**: Fail-fast (throw errors)
|
||||||
|
- **Best Practices**: Non-blocking (warnings only)
|
||||||
|
- Always provide component ID in error messages
|
||||||
|
- Collect all warnings for summary report
|
||||||
325
skills/fluxwing-validator/README.md
Normal file
325
skills/fluxwing-validator/README.md
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
# Fluxwing Validator
|
||||||
|
|
||||||
|
Deterministic validation for uxscii components and screens.
|
||||||
|
|
||||||
|
## Two Modes of Operation
|
||||||
|
|
||||||
|
### Interactive Mode (User Invocation)
|
||||||
|
|
||||||
|
When users say "Validate my components", the skill:
|
||||||
|
1. Presents menu (everything/components/screens/custom)
|
||||||
|
2. Runs appropriate validators
|
||||||
|
3. Shows minimal summary with optional details
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```
|
||||||
|
✓ 12/14 components valid
|
||||||
|
✗ 2/14 components failed
|
||||||
|
|
||||||
|
Show error details? (yes/no)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Direct Mode (Skill-to-Skill)
|
||||||
|
|
||||||
|
Other skills call validator scripts directly:
|
||||||
|
```bash
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-component.js \
|
||||||
|
./fluxwing/components/button.uxm \
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: Always verbose (full errors, warnings, stats)
|
||||||
|
Exit codes: 0 = valid, 1 = errors, 2 = invalid args
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This skill contains validation scripts that can be called from Claude Code skills to validate `.uxm` component files against JSON Schema with additional uxscii-specific checks.
|
||||||
|
|
||||||
|
**Key features:**
|
||||||
|
- ✅ **Deterministic** - Locked dependencies via `package-lock.json`
|
||||||
|
- ✅ **Zero setup** - `node_modules` bundled in repository
|
||||||
|
- ✅ **Fast** - ~80ms execution time with ajv
|
||||||
|
- ✅ **Comprehensive** - Schema validation + uxscii-specific rules
|
||||||
|
- ✅ **Structured output** - JSON and human-readable formats
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This skill is bundled with fluxwing-skills plugin.
|
||||||
|
|
||||||
|
**Plugin install:**
|
||||||
|
```bash
|
||||||
|
/plugin install fluxwing-skills
|
||||||
|
```
|
||||||
|
|
||||||
|
**Script install (development):**
|
||||||
|
```bash
|
||||||
|
./scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
No npm install required - dependencies bundled with skill.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
```
|
||||||
|
skills/fluxwing-validator/
|
||||||
|
├── SKILL.md # Interactive validation workflow
|
||||||
|
├── validate-component.js # Component validator
|
||||||
|
├── validate-screen.js # Screen validator
|
||||||
|
├── validate-batch.js # Batch validator
|
||||||
|
├── test-validator.js # Component test suite
|
||||||
|
├── test-screen-validator.js # Screen test suite
|
||||||
|
├── package.json # Dependencies (ajv 8.12.0)
|
||||||
|
├── package-lock.json # Locked versions
|
||||||
|
├── node_modules/ # Bundled dependencies (~7.4 MB)
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### From Command Line
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From project root
|
||||||
|
node skills/fluxwing-validator/validate-component.js \
|
||||||
|
./fluxwing/components/component.uxm \
|
||||||
|
skills/fluxwing-component-creator/schemas/uxm-component.schema.json
|
||||||
|
|
||||||
|
# JSON output (for programmatic use)
|
||||||
|
node skills/fluxwing-validator/validate-component.js \
|
||||||
|
./fluxwing/components/component.uxm \
|
||||||
|
skills/fluxwing-component-creator/schemas/uxm-component.schema.json \
|
||||||
|
--json
|
||||||
|
```
|
||||||
|
|
||||||
|
### From Other Skills (using SKILL_ROOT)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In SKILL.md - Direct mode validation
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-component.js \
|
||||||
|
./fluxwing/components/button.uxm \
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exit Codes
|
||||||
|
|
||||||
|
- `0` - Component is valid
|
||||||
|
- `1` - Validation errors found
|
||||||
|
- `2` - Missing dependencies or invalid arguments
|
||||||
|
|
||||||
|
## Validation Checks
|
||||||
|
|
||||||
|
### 1. JSON Schema Validation
|
||||||
|
|
||||||
|
Validates against `uxm-component.schema.json` using ajv (Draft 7):
|
||||||
|
- Required fields (id, type, version, metadata, props, ascii)
|
||||||
|
- Field types and formats
|
||||||
|
- Enum constraints (type, category, fidelity)
|
||||||
|
- Pattern matching (id format, version format)
|
||||||
|
- Array and object structures
|
||||||
|
|
||||||
|
### 2. ASCII Template File
|
||||||
|
|
||||||
|
- Checks that corresponding `.md` file exists
|
||||||
|
- Validates path: `component.uxm` → `component.md`
|
||||||
|
|
||||||
|
### 3. Template Variables
|
||||||
|
|
||||||
|
- Extracts `{{variables}}` from `.md` template
|
||||||
|
- Compares against `ascii.variables` in `.uxm`
|
||||||
|
- Warns if variables in `.md` are not defined in `.uxm`
|
||||||
|
- Supports both formats:
|
||||||
|
- Array of strings: `["text", "value"]`
|
||||||
|
- Array of objects: `[{"name": "text", "type": "string"}]`
|
||||||
|
|
||||||
|
### 4. Accessibility
|
||||||
|
|
||||||
|
For interactive components (with `behavior.interactions`):
|
||||||
|
- Warns if missing `behavior.accessibility.role`
|
||||||
|
- Warns if `focusable` is not set
|
||||||
|
|
||||||
|
### 5. ASCII Dimensions
|
||||||
|
|
||||||
|
- Warns if `ascii.width` > 120 (terminal width limit)
|
||||||
|
- Warns if `ascii.height` > 50 (single viewport limit)
|
||||||
|
|
||||||
|
### 6. State Properties
|
||||||
|
|
||||||
|
- Warns if states exist but have no properties defined
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
### Human-Readable
|
||||||
|
|
||||||
|
```
|
||||||
|
✓ Valid: primary-button
|
||||||
|
Type: button
|
||||||
|
Version: 1.0.0
|
||||||
|
States: 4
|
||||||
|
Props: 5
|
||||||
|
|
||||||
|
Warnings: 1
|
||||||
|
1. Interactive component should have ARIA role
|
||||||
|
Location: behavior → accessibility → role
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"errors": [],
|
||||||
|
"warnings": [
|
||||||
|
{
|
||||||
|
"path": ["behavior", "accessibility", "role"],
|
||||||
|
"message": "Interactive component should have ARIA role",
|
||||||
|
"type": "accessibility"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stats": {
|
||||||
|
"id": "primary-button",
|
||||||
|
"type": "button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"states": 4,
|
||||||
|
"props": 5,
|
||||||
|
"interactive": true,
|
||||||
|
"hasAccessibility": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Only 6 packages, 2.7 MB total:
|
||||||
|
|
||||||
|
- `ajv@8.12.0` - JSON Schema validator (industry standard)
|
||||||
|
- `ajv-formats@2.1.1` - Format validation (date-time, etc.)
|
||||||
|
- Supporting packages (fast-deep-equal, fast-uri, json-schema-traverse, require-from-string)
|
||||||
|
|
||||||
|
**All dependencies are bundled in `node_modules/` and checked into git.**
|
||||||
|
|
||||||
|
## Node.js Version
|
||||||
|
|
||||||
|
- **Minimum:** Node.js 14.0.0
|
||||||
|
- **Recommended:** Node.js 18+ or 20+ (LTS)
|
||||||
|
- **Tested:** Node.js 18, 20, 22
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run test suite against bundled templates
|
||||||
|
npm test
|
||||||
|
|
||||||
|
# Or directly
|
||||||
|
node test-validator.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests validate components from:
|
||||||
|
- `skills/fluxwing-component-creator/templates/*.uxm`
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Updating Validation Logic
|
||||||
|
|
||||||
|
1. Edit `validate-component.js`
|
||||||
|
2. Test changes: `npm test`
|
||||||
|
3. Commit (no build step needed)
|
||||||
|
|
||||||
|
### Updating Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm update
|
||||||
|
npm audit fix
|
||||||
|
npm test
|
||||||
|
git add package-lock.json node_modules/
|
||||||
|
git commit -m "Update dependencies"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding New Validators
|
||||||
|
|
||||||
|
Create new scripts following the same pattern:
|
||||||
|
- `validate-screen.js` - For screen validation
|
||||||
|
- `validate-variable.js` - For variable substitution
|
||||||
|
|
||||||
|
## Why Node.js?
|
||||||
|
|
||||||
|
Compared to Python or native binaries:
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
- ✅ No build step (unlike Go/Rust/Deno compiled)
|
||||||
|
- ✅ Readable source code (easier debugging)
|
||||||
|
- ✅ Small diffs (text, not binaries)
|
||||||
|
- ✅ Likely installed (most developers have Node.js)
|
||||||
|
- ✅ Mature ecosystem (ajv is industry standard)
|
||||||
|
- ✅ Fast enough (~80ms including startup)
|
||||||
|
|
||||||
|
**Tradeoffs:**
|
||||||
|
- ⚠️ Requires Node.js runtime (not zero-dependency)
|
||||||
|
- ⚠️ Slower than native (~80ms vs ~5ms for Go)
|
||||||
|
|
||||||
|
## Integration Examples
|
||||||
|
|
||||||
|
### Component Creator Skill
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# After creating component (from SKILL.md)
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-component.js \
|
||||||
|
./fluxwing/components/${COMPONENT_ID}.uxm \
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "✓ Component validated successfully"
|
||||||
|
else
|
||||||
|
echo "✗ Validation failed - see errors above"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pre-commit Hook
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .claude/hooks/pre-commit.sh
|
||||||
|
for uxm_file in fluxwing/components/*.uxm; do
|
||||||
|
node skills/fluxwing-validator/validate-component.js \
|
||||||
|
"$uxm_file" \
|
||||||
|
skills/fluxwing-component-creator/schemas/uxm-component.schema.json \
|
||||||
|
|| exit 1
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "ajv libraries not found"
|
||||||
|
|
||||||
|
The validator couldn't find the ajv dependency. This should never happen since `node_modules/` is bundled, but if it does:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd skills/fluxwing-validator
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Component file not found"
|
||||||
|
|
||||||
|
Check that the path to the `.uxm` file is correct. Paths are relative to the current working directory.
|
||||||
|
|
||||||
|
### "Invalid JSON"
|
||||||
|
|
||||||
|
The `.uxm` file has syntax errors. Use a JSON validator to find the issue:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat component.uxm | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
- [x] Screen validation (`validate-screen.js`)
|
||||||
|
- [x] Batch validation (`validate-batch.js`)
|
||||||
|
- [ ] Variable substitution validation
|
||||||
|
- [ ] Watch mode for development
|
||||||
|
- [ ] Integration with CI/CD pipelines
|
||||||
|
- [ ] VSCode extension integration
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
354
skills/fluxwing-validator/SKILL.md
Normal file
354
skills/fluxwing-validator/SKILL.md
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
---
|
||||||
|
name: fluxwing-validator
|
||||||
|
description: Validate uxscii components and screens against schema with interactive menu or direct script calls
|
||||||
|
version: 1.0.0
|
||||||
|
author: Fluxwing Team
|
||||||
|
allowed-tools: Read, Bash, AskUserQuestion, TodoWrite
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fluxwing Validator
|
||||||
|
|
||||||
|
Validate uxscii components and screens against JSON Schema with interactive workflows.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This skill provides two modes of operation:
|
||||||
|
|
||||||
|
1. **Interactive Mode**: User invocation with menu and minimal output
|
||||||
|
2. **Direct Mode**: Script calls from other skills with verbose output
|
||||||
|
|
||||||
|
## When to Use This Skill
|
||||||
|
|
||||||
|
**User says:**
|
||||||
|
- "Validate my components"
|
||||||
|
- "Check if everything is valid"
|
||||||
|
- "Run validation on my screens"
|
||||||
|
- "Validate the project"
|
||||||
|
|
||||||
|
**Other skills:** Call validator scripts directly (see Technical Reference below)
|
||||||
|
|
||||||
|
## Interactive Validation Workflow
|
||||||
|
|
||||||
|
### Step 1: Present Validation Options
|
||||||
|
|
||||||
|
Use AskUserQuestion to present menu:
|
||||||
|
|
||||||
|
```
|
||||||
|
What would you like to validate?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
1. **Everything in this project** - Validates all components and screens
|
||||||
|
2. **Just components** - Validates `./fluxwing/components/*.uxm`
|
||||||
|
3. **Just screens** - Validates `./fluxwing/screens/*.uxm`
|
||||||
|
4. **Let me specify a file or pattern** - Custom path/glob pattern
|
||||||
|
|
||||||
|
### Step 2: Check What Exists
|
||||||
|
|
||||||
|
Before running validation, check if directories exist:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check for components
|
||||||
|
test -d ./fluxwing/components
|
||||||
|
|
||||||
|
# Check for screens
|
||||||
|
test -d ./fluxwing/screens
|
||||||
|
```
|
||||||
|
|
||||||
|
**If neither exists:**
|
||||||
|
- Inform user: "No components or screens found. Create some first!"
|
||||||
|
- Exit cleanly
|
||||||
|
|
||||||
|
**If option 4 (custom) selected:**
|
||||||
|
- Ask user for the pattern/file path
|
||||||
|
- Validate it's not empty
|
||||||
|
- Proceed with user-provided pattern
|
||||||
|
|
||||||
|
### Step 3: Run Validation
|
||||||
|
|
||||||
|
Based on user selection:
|
||||||
|
|
||||||
|
**Option 1: Everything**
|
||||||
|
```bash
|
||||||
|
# Validate components
|
||||||
|
node {SKILL_ROOT}/validate-batch.js \
|
||||||
|
"./fluxwing/components/*.uxm" \
|
||||||
|
"{SKILL_ROOT}/../fluxwing-component-creator/schemas/uxm-component.schema.json" \
|
||||||
|
--json
|
||||||
|
|
||||||
|
# Validate screens
|
||||||
|
node {SKILL_ROOT}/validate-batch.js \
|
||||||
|
"./fluxwing/screens/*.uxm" \
|
||||||
|
"{SKILL_ROOT}/../fluxwing-component-creator/schemas/uxm-component.schema.json" \
|
||||||
|
--screens \
|
||||||
|
--json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2: Just components**
|
||||||
|
```bash
|
||||||
|
node {SKILL_ROOT}/validate-batch.js \
|
||||||
|
"./fluxwing/components/*.uxm" \
|
||||||
|
"{SKILL_ROOT}/../fluxwing-component-creator/schemas/uxm-component.schema.json" \
|
||||||
|
--json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 3: Just screens**
|
||||||
|
```bash
|
||||||
|
node {SKILL_ROOT}/validate-batch.js \
|
||||||
|
"./fluxwing/screens/*.uxm" \
|
||||||
|
"{SKILL_ROOT}/../fluxwing-component-creator/schemas/uxm-component.schema.json" \
|
||||||
|
--screens \
|
||||||
|
--json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 4: Custom pattern**
|
||||||
|
```bash
|
||||||
|
# Use user-provided pattern
|
||||||
|
node {SKILL_ROOT}/validate-batch.js \
|
||||||
|
"${userPattern}" \
|
||||||
|
"{SKILL_ROOT}/../fluxwing-component-creator/schemas/uxm-component.schema.json" \
|
||||||
|
--json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Parse Results and Show Minimal Summary
|
||||||
|
|
||||||
|
Parse JSON output from validator to extract:
|
||||||
|
- `total`: Total files validated
|
||||||
|
- `passed`: Number of valid files
|
||||||
|
- `failed`: Number of failed files
|
||||||
|
- `warnings`: Total warning count
|
||||||
|
|
||||||
|
**Display minimal summary:**
|
||||||
|
|
||||||
|
```
|
||||||
|
✓ 12/14 components valid
|
||||||
|
✗ 2/14 components failed
|
||||||
|
⚠ 3 warnings total
|
||||||
|
```
|
||||||
|
|
||||||
|
**If all passed:**
|
||||||
|
```
|
||||||
|
✓ All 14 components valid
|
||||||
|
⚠ 3 warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
**If everything failed:**
|
||||||
|
```
|
||||||
|
✗ All 14 components failed
|
||||||
|
```
|
||||||
|
|
||||||
|
**If nothing to validate:**
|
||||||
|
```
|
||||||
|
No files found matching pattern
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Ask About Details
|
||||||
|
|
||||||
|
Use AskUserQuestion to ask:
|
||||||
|
|
||||||
|
```
|
||||||
|
Show error details?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
1. **Yes** - Display full validation output
|
||||||
|
2. **No** - Clean exit
|
||||||
|
|
||||||
|
### Step 6: Display Details (if requested)
|
||||||
|
|
||||||
|
If user selects "Yes", show full validation output including:
|
||||||
|
|
||||||
|
**Failed files section:**
|
||||||
|
```
|
||||||
|
Failed Files:
|
||||||
|
|
||||||
|
✗ broken-button (./fluxwing/components/broken-button.uxm)
|
||||||
|
Errors: 2
|
||||||
|
1. must have required property 'fidelity'
|
||||||
|
2. ASCII template file not found
|
||||||
|
|
||||||
|
✗ old-card (./fluxwing/components/old-card.uxm)
|
||||||
|
Errors: 1
|
||||||
|
1. invalid version format
|
||||||
|
```
|
||||||
|
|
||||||
|
**Passed with warnings section:**
|
||||||
|
```
|
||||||
|
Passed with Warnings:
|
||||||
|
|
||||||
|
✓ login-screen (2 warnings)
|
||||||
|
✓ dashboard (1 warning)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fully passed section (optional, only if not too many):**
|
||||||
|
```
|
||||||
|
Fully Passed:
|
||||||
|
|
||||||
|
✓ primary-button
|
||||||
|
✓ secondary-button
|
||||||
|
✓ email-input
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Edge Cases
|
||||||
|
|
||||||
|
### No fluxwing directory exists
|
||||||
|
```
|
||||||
|
No components or screens found. Create some first!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Empty directories
|
||||||
|
```
|
||||||
|
✓ 0/0 components valid
|
||||||
|
```
|
||||||
|
|
||||||
|
### Invalid glob pattern (option 4)
|
||||||
|
```
|
||||||
|
No files found matching pattern: ${pattern}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validation script fails to execute
|
||||||
|
```
|
||||||
|
Error: Cannot execute validator. Node.js required.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Reference (For Other Skills)
|
||||||
|
|
||||||
|
### Direct Script Calls
|
||||||
|
|
||||||
|
Other skills (component-creator, screen-scaffolder) call validator scripts directly:
|
||||||
|
|
||||||
|
**Validate single component:**
|
||||||
|
```bash
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-component.js \
|
||||||
|
./fluxwing/components/button.uxm \
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validate single screen:**
|
||||||
|
```bash
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-screen.js \
|
||||||
|
./fluxwing/screens/login-screen.uxm \
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Batch validate:**
|
||||||
|
```bash
|
||||||
|
node {SKILL_ROOT}/../fluxwing-validator/validate-batch.js \
|
||||||
|
"./fluxwing/components/*.uxm" \
|
||||||
|
{SKILL_ROOT}/schemas/uxm-component.schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output modes:**
|
||||||
|
- Default: Human-readable (verbose, full errors/warnings)
|
||||||
|
- `--json`: Machine-readable JSON
|
||||||
|
|
||||||
|
**Exit codes:**
|
||||||
|
- `0`: All files valid
|
||||||
|
- `1`: One or more files invalid
|
||||||
|
- `2`: Missing dependencies or invalid arguments
|
||||||
|
|
||||||
|
### Validator Scripts
|
||||||
|
|
||||||
|
**Available scripts:**
|
||||||
|
- `validate-component.js` - Validate single component file
|
||||||
|
- `validate-screen.js` - Validate single screen file (with screen-specific checks)
|
||||||
|
- `validate-batch.js` - Validate multiple files with glob patterns
|
||||||
|
- `test-validator.js` - Test component templates
|
||||||
|
- `test-screen-validator.js` - Test screen templates
|
||||||
|
|
||||||
|
### npm Scripts (for testing)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd {SKILL_ROOT}
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
npm test # Test component templates
|
||||||
|
npm run test:screens # Test screen templates
|
||||||
|
|
||||||
|
# Batch validation
|
||||||
|
npm run validate:components # Validate all components
|
||||||
|
npm run validate:screens # Validate all screens
|
||||||
|
npm run validate:all # Validate everything
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Interactions
|
||||||
|
|
||||||
|
### Example 1: Validate Everything
|
||||||
|
|
||||||
|
**User:** "Validate my components"
|
||||||
|
|
||||||
|
**Skill:**
|
||||||
|
```
|
||||||
|
What would you like to validate?
|
||||||
|
|
||||||
|
1. Everything in this project
|
||||||
|
2. Just components
|
||||||
|
3. Just screens
|
||||||
|
4. Let me specify a file or pattern
|
||||||
|
```
|
||||||
|
|
||||||
|
**User selects:** Option 1
|
||||||
|
|
||||||
|
**Skill runs validation and shows:**
|
||||||
|
```
|
||||||
|
✓ 12/14 components valid
|
||||||
|
✗ 2/14 components failed
|
||||||
|
⚠ 3 warnings total
|
||||||
|
|
||||||
|
✓ 2/2 screens valid
|
||||||
|
⚠ 1 warning
|
||||||
|
|
||||||
|
Show error details?
|
||||||
|
```
|
||||||
|
|
||||||
|
**User:** "yes"
|
||||||
|
|
||||||
|
**Skill shows full error details for failed files**
|
||||||
|
|
||||||
|
### Example 2: Validate Specific Pattern
|
||||||
|
|
||||||
|
**User:** "Validate my components"
|
||||||
|
|
||||||
|
**Skill:** (presents menu)
|
||||||
|
|
||||||
|
**User selects:** Option 4 (custom pattern)
|
||||||
|
|
||||||
|
**Skill:** "What file or pattern would you like to validate?"
|
||||||
|
|
||||||
|
**User:** "./fluxwing/components/*-button.uxm"
|
||||||
|
|
||||||
|
**Skill validates and shows:**
|
||||||
|
```
|
||||||
|
✓ 3/3 files valid
|
||||||
|
|
||||||
|
Show error details?
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
**Parse JSON output:**
|
||||||
|
```javascript
|
||||||
|
const result = JSON.parse(bashOutput);
|
||||||
|
const total = result.total;
|
||||||
|
const passed = result.passed;
|
||||||
|
const failed = result.failed;
|
||||||
|
const warnings = result.warnings;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Summary formatting:**
|
||||||
|
- Show passed/failed ratio for quick scan
|
||||||
|
- Highlight failures prominently
|
||||||
|
- Warnings shown but not intrusive
|
||||||
|
- Clean, minimal output by default
|
||||||
|
|
||||||
|
**Error detail formatting:**
|
||||||
|
- Group by status (failed, warnings, passed)
|
||||||
|
- Show file path and error count
|
||||||
|
- Display first 2-3 errors per file
|
||||||
|
- Indicate if more errors exist
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Skill Status:** Ready for use
|
||||||
|
**Version:** 1.0.0
|
||||||
23
skills/fluxwing-validator/package.json
Normal file
23
skills/fluxwing-validator/package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "fluxwing-validators",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Deterministic Node.js validators for uxscii components and screens",
|
||||||
|
"main": "validate-component.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node test-validator.js",
|
||||||
|
"test:screens": "node test-screen-validator.js",
|
||||||
|
"validate:components": "node validate-batch.js '../fluxwing/components/*.uxm' '../skills/fluxwing-component-creator/schemas/uxm-component.schema.json'",
|
||||||
|
"validate:screens": "node validate-batch.js '../fluxwing/screens/*.uxm' '../skills/fluxwing-component-creator/schemas/uxm-component.schema.json' --screens",
|
||||||
|
"validate:all": "npm run validate:components && npm run validate:screens"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": "^8.12.0",
|
||||||
|
"ajv-formats": "^2.1.1",
|
||||||
|
"glob": "^10.3.10"
|
||||||
|
},
|
||||||
|
"author": "Fluxwing",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
89
skills/fluxwing-validator/test-screen-validator.js
Executable file
89
skills/fluxwing-validator/test-screen-validator.js
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Test suite for screen validator
|
||||||
|
* Tests bundled screen templates against the schema
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { validateScreen } = require('./validate-screen.js');
|
||||||
|
|
||||||
|
// ANSI color codes
|
||||||
|
const GREEN = '\x1b[32m';
|
||||||
|
const RED = '\x1b[31m';
|
||||||
|
const YELLOW = '\x1b[33m';
|
||||||
|
const RESET = '\x1b[0m';
|
||||||
|
|
||||||
|
console.log('Testing Fluxwing Screen Validator\n');
|
||||||
|
console.log('==================================================\n');
|
||||||
|
|
||||||
|
// Test configuration
|
||||||
|
const schemaPath = path.join(__dirname, '..', 'fluxwing-component-creator', 'schemas', 'uxm-component.schema.json');
|
||||||
|
const templatesDir = path.join(__dirname, '..', 'fluxwing-screen-scaffolder', 'templates');
|
||||||
|
|
||||||
|
// Find all .uxm files in templates directory
|
||||||
|
const screenFiles = fs.readdirSync(templatesDir)
|
||||||
|
.filter(file => file.endsWith('.uxm'))
|
||||||
|
.map(file => path.join(templatesDir, file));
|
||||||
|
|
||||||
|
if (screenFiles.length === 0) {
|
||||||
|
console.log(`${YELLOW}⚠ No screen templates found in ${templatesDir}${RESET}`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test results
|
||||||
|
const results = {
|
||||||
|
passed: 0,
|
||||||
|
failed: 0,
|
||||||
|
warnings: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
for (const screenFile of screenFiles) {
|
||||||
|
const screenName = path.basename(screenFile, '.uxm');
|
||||||
|
console.log(`Testing: ${screenName}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = validateScreen(screenFile, schemaPath);
|
||||||
|
|
||||||
|
if (result.valid) {
|
||||||
|
console.log(` ${GREEN}✓ PASS${RESET} - ${screenName}`);
|
||||||
|
results.passed++;
|
||||||
|
|
||||||
|
if (result.warnings.length > 0) {
|
||||||
|
console.log(` (${result.warnings.length} warnings)`);
|
||||||
|
results.warnings += result.warnings.length;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(` ${RED}✗ FAIL${RESET} - ${screenName}`);
|
||||||
|
console.log(` Errors: ${result.errors.length}`);
|
||||||
|
results.failed++;
|
||||||
|
|
||||||
|
// Show first few errors
|
||||||
|
result.errors.slice(0, 3).forEach((error, i) => {
|
||||||
|
console.log(` ${i + 1}. ${error.message}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.errors.length > 3) {
|
||||||
|
console.log(` ... and ${result.errors.length - 3} more errors`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(` ${RED}✗ ERROR${RESET} - ${screenName}`);
|
||||||
|
console.log(` ${error.message}`);
|
||||||
|
results.failed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print summary
|
||||||
|
console.log('==================================================');
|
||||||
|
console.log(`Results: ${GREEN}${results.passed} passed${RESET}, ${results.failed > 0 ? RED : ''}${results.failed} failed${RESET}`);
|
||||||
|
|
||||||
|
if (results.warnings > 0) {
|
||||||
|
console.log(`Warnings: ${YELLOW}${results.warnings} total${RESET}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit with appropriate code
|
||||||
|
process.exit(results.failed > 0 ? 1 : 0);
|
||||||
68
skills/fluxwing-validator/test-validator.js
Executable file
68
skills/fluxwing-validator/test-validator.js
Executable file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Test script for validate-component.js
|
||||||
|
* Tests against bundled template components
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { validateComponent } = require('./validate-component.js');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Test components from templates
|
||||||
|
const testComponents = [
|
||||||
|
'../fluxwing-component-creator/templates/primary-button.uxm',
|
||||||
|
'../fluxwing-component-creator/templates/email-input.uxm',
|
||||||
|
'../fluxwing-component-creator/templates/card.uxm',
|
||||||
|
'../fluxwing-component-creator/templates/badge.uxm',
|
||||||
|
'../fluxwing-component-creator/templates/alert.uxm'
|
||||||
|
];
|
||||||
|
|
||||||
|
const schemaPath = path.join(__dirname, '../fluxwing-component-creator/schemas/uxm-component.schema.json');
|
||||||
|
|
||||||
|
console.log('Testing Fluxwing Component Validator\n');
|
||||||
|
console.log('='.repeat(50));
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
let passed = 0;
|
||||||
|
let failed = 0;
|
||||||
|
|
||||||
|
for (const componentRelPath of testComponents) {
|
||||||
|
const componentPath = path.join(__dirname, componentRelPath);
|
||||||
|
const componentName = path.basename(componentPath);
|
||||||
|
|
||||||
|
if (!fs.existsSync(componentPath)) {
|
||||||
|
console.log(`⊘ SKIP: ${componentName} (file not found)`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Testing: ${componentName}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = validateComponent(componentPath, schemaPath);
|
||||||
|
|
||||||
|
if (result.valid) {
|
||||||
|
passed++;
|
||||||
|
console.log(` ✓ PASS - ${result.stats.id}`);
|
||||||
|
if (result.warnings.length > 0) {
|
||||||
|
console.log(` (${result.warnings.length} warnings)`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failed++;
|
||||||
|
console.log(` ✗ FAIL - ${result.errors.length} errors`);
|
||||||
|
result.errors.slice(0, 2).forEach(err => {
|
||||||
|
console.log(` • ${err.message}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
failed++;
|
||||||
|
console.log(` ✗ ERROR: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('='.repeat(50));
|
||||||
|
console.log(`Results: ${passed} passed, ${failed} failed`);
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
process.exit(failed > 0 ? 1 : 0);
|
||||||
208
skills/fluxwing-validator/validate-batch.js
Executable file
208
skills/fluxwing-validator/validate-batch.js
Executable file
@@ -0,0 +1,208 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Batch validation for multiple .uxm files
|
||||||
|
* Validates multiple component or screen files in one command
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node validate-batch.js "./fluxwing/components/FILE.uxm" <schema.json>
|
||||||
|
* node validate-batch.js "./fluxwing/screens/FILE.uxm" <schema.json> --json
|
||||||
|
* node validate-batch.js "./fluxwing/ALL/FILE.uxm" <schema.json> --screens
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
* --json Output results as JSON
|
||||||
|
* --screens Use screen validator instead of component validator
|
||||||
|
*
|
||||||
|
* Exit codes:
|
||||||
|
* 0 - All files valid
|
||||||
|
* 1 - One or more files invalid
|
||||||
|
* 2 - Missing dependencies or invalid arguments
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Check for glob dependency
|
||||||
|
let glob;
|
||||||
|
try {
|
||||||
|
glob = require('glob');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error: glob library not found');
|
||||||
|
console.error('Install with: npm install glob');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import validators
|
||||||
|
const { validateComponent } = require('./validate-component.js');
|
||||||
|
const { validateScreen } = require('./validate-screen.js');
|
||||||
|
|
||||||
|
// ANSI color codes
|
||||||
|
const GREEN = '\x1b[32m';
|
||||||
|
const RED = '\x1b[31m';
|
||||||
|
const YELLOW = '\x1b[33m';
|
||||||
|
const BLUE = '\x1b[34m';
|
||||||
|
const RESET = '\x1b[0m';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate multiple files matching a glob pattern
|
||||||
|
* @param {string} pattern - Glob pattern for files to validate
|
||||||
|
* @param {string} schemaPath - Path to JSON schema file
|
||||||
|
* @param {Object} options - Validation options
|
||||||
|
* @returns {Object} Batch validation results
|
||||||
|
*/
|
||||||
|
function validateBatch(pattern, schemaPath, options = {}) {
|
||||||
|
const useScreenValidator = options.screens || false;
|
||||||
|
const validator = useScreenValidator ? validateScreen : validateComponent;
|
||||||
|
|
||||||
|
// Find all matching files
|
||||||
|
const files = glob.sync(pattern);
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
return {
|
||||||
|
total: 0,
|
||||||
|
passed: 0,
|
||||||
|
failed: 0,
|
||||||
|
warnings: 0,
|
||||||
|
files: [],
|
||||||
|
message: `No files found matching pattern: ${pattern}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
total: files.length,
|
||||||
|
passed: 0,
|
||||||
|
failed: 0,
|
||||||
|
warnings: 0,
|
||||||
|
files: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate each file
|
||||||
|
for (const file of files) {
|
||||||
|
const result = validator(file, schemaPath);
|
||||||
|
|
||||||
|
const fileResult = {
|
||||||
|
file: file,
|
||||||
|
id: result.stats?.id || path.basename(file, '.uxm'),
|
||||||
|
valid: result.valid,
|
||||||
|
errors: result.errors.length,
|
||||||
|
warnings: result.warnings.length,
|
||||||
|
errorDetails: result.errors,
|
||||||
|
warningDetails: result.warnings
|
||||||
|
};
|
||||||
|
|
||||||
|
results.files.push(fileResult);
|
||||||
|
|
||||||
|
if (result.valid) {
|
||||||
|
results.passed++;
|
||||||
|
} else {
|
||||||
|
results.failed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.warnings += result.warnings.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print human-readable batch results
|
||||||
|
* @param {Object} results - Batch validation results
|
||||||
|
*/
|
||||||
|
function printHumanReadable(results) {
|
||||||
|
if (results.total === 0) {
|
||||||
|
console.log(`${YELLOW}${results.message}${RESET}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${BLUE}Batch Validation Results${RESET}\n`);
|
||||||
|
console.log(`Total files: ${results.total}`);
|
||||||
|
console.log(`${GREEN}Passed: ${results.passed}${RESET}`);
|
||||||
|
|
||||||
|
if (results.failed > 0) {
|
||||||
|
console.log(`${RED}Failed: ${results.failed}${RESET}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.warnings > 0) {
|
||||||
|
console.log(`${YELLOW}Total warnings: ${results.warnings}${RESET}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
// Show failed files
|
||||||
|
const failedFiles = results.files.filter(f => !f.valid);
|
||||||
|
if (failedFiles.length > 0) {
|
||||||
|
console.log(`${RED}Failed Files:${RESET}\n`);
|
||||||
|
failedFiles.forEach(file => {
|
||||||
|
console.log(` ${RED}✗${RESET} ${file.id} (${file.file})`);
|
||||||
|
console.log(` Errors: ${file.errors}`);
|
||||||
|
|
||||||
|
// Show first 2 errors
|
||||||
|
file.errorDetails.slice(0, 2).forEach((error, i) => {
|
||||||
|
console.log(` ${i + 1}. ${error.message}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file.errors > 2) {
|
||||||
|
console.log(` ... and ${file.errors - 2} more errors`);
|
||||||
|
}
|
||||||
|
console.log('');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show passed files with warnings
|
||||||
|
const passedWithWarnings = results.files.filter(f => f.valid && f.warnings > 0);
|
||||||
|
if (passedWithWarnings.length > 0) {
|
||||||
|
console.log(`${YELLOW}Passed with Warnings:${RESET}\n`);
|
||||||
|
passedWithWarnings.forEach(file => {
|
||||||
|
console.log(` ${GREEN}✓${RESET} ${file.id} ${YELLOW}(${file.warnings} warnings)${RESET}`);
|
||||||
|
});
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show fully passed files (compact)
|
||||||
|
const fullPassed = results.files.filter(f => f.valid && f.warnings === 0);
|
||||||
|
if (fullPassed.length > 0) {
|
||||||
|
console.log(`${GREEN}Fully Passed:${RESET}\n`);
|
||||||
|
fullPassed.forEach(file => {
|
||||||
|
console.log(` ${GREEN}✓${RESET} ${file.id}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print JSON results
|
||||||
|
* @param {Object} results - Batch validation results
|
||||||
|
*/
|
||||||
|
function printJSON(results) {
|
||||||
|
console.log(JSON.stringify(results, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI interface
|
||||||
|
if (require.main === module) {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
console.error('Usage: node validate-batch.js <pattern> <schema.json> [--json] [--screens]');
|
||||||
|
console.error('');
|
||||||
|
console.error('Examples:');
|
||||||
|
console.error(' node validate-batch.js "./fluxwing/components/*.uxm" schema.json');
|
||||||
|
console.error(' node validate-batch.js "./fluxwing/**/*.uxm" schema.json --json');
|
||||||
|
console.error(' node validate-batch.js "./fluxwing/screens/*.uxm" schema.json --screens');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pattern = args[0];
|
||||||
|
const schemaPath = args[1];
|
||||||
|
const jsonOutput = args.includes('--json');
|
||||||
|
const useScreens = args.includes('--screens');
|
||||||
|
|
||||||
|
const results = validateBatch(pattern, schemaPath, { screens: useScreens });
|
||||||
|
|
||||||
|
if (jsonOutput) {
|
||||||
|
printJSON(results);
|
||||||
|
} else {
|
||||||
|
printHumanReadable(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(results.failed > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { validateBatch };
|
||||||
359
skills/fluxwing-validator/validate-component.js
Executable file
359
skills/fluxwing-validator/validate-component.js
Executable file
@@ -0,0 +1,359 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Fast, deterministic component validation using JSON Schema (ajv).
|
||||||
|
* Validates .uxm component files against schema with uxscii-specific checks.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node validate-component.js <component.uxm> <schema.json>
|
||||||
|
* node validate-component.js <component.uxm> <schema.json> --json
|
||||||
|
*
|
||||||
|
* Exit codes:
|
||||||
|
* 0 - Valid component
|
||||||
|
* 1 - Validation errors found
|
||||||
|
* 2 - Missing dependencies or invalid arguments
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Check for ajv dependency
|
||||||
|
let Ajv, addFormats;
|
||||||
|
try {
|
||||||
|
Ajv = require('ajv');
|
||||||
|
addFormats = require('ajv-formats');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error: ajv libraries not found');
|
||||||
|
console.error('Install with: npm install ajv ajv-formats');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a .uxm component file against the schema
|
||||||
|
* @param {string} uxmFilePath - Path to .uxm file
|
||||||
|
* @param {string} schemaPath - Path to JSON schema file
|
||||||
|
* @returns {Object} Validation result object
|
||||||
|
*/
|
||||||
|
function validateComponent(uxmFilePath, schemaPath) {
|
||||||
|
const result = {
|
||||||
|
valid: true,
|
||||||
|
errors: [],
|
||||||
|
warnings: [],
|
||||||
|
stats: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load component file
|
||||||
|
let component;
|
||||||
|
try {
|
||||||
|
const uxmContent = fs.readFileSync(uxmFilePath, 'utf8');
|
||||||
|
component = JSON.parse(uxmContent);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
result.valid = false;
|
||||||
|
result.errors.push({
|
||||||
|
path: [],
|
||||||
|
message: `Component file not found: ${uxmFilePath}`,
|
||||||
|
type: 'file_not_found'
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (error instanceof SyntaxError) {
|
||||||
|
result.valid = false;
|
||||||
|
result.errors.push({
|
||||||
|
path: [],
|
||||||
|
message: `Invalid JSON: ${error.message}`,
|
||||||
|
type: 'json_error'
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load schema file
|
||||||
|
let schema;
|
||||||
|
try {
|
||||||
|
const schemaContent = fs.readFileSync(schemaPath, 'utf8');
|
||||||
|
schema = JSON.parse(schemaContent);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
result.valid = false;
|
||||||
|
result.errors.push({
|
||||||
|
path: [],
|
||||||
|
message: `Schema file not found: ${schemaPath}`,
|
||||||
|
type: 'file_not_found'
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate against JSON schema
|
||||||
|
const ajv = new Ajv({ allErrors: true, strict: false });
|
||||||
|
addFormats(ajv);
|
||||||
|
|
||||||
|
const validate = ajv.compile(schema);
|
||||||
|
const valid = validate(component);
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
for (const error of validate.errors) {
|
||||||
|
result.errors.push({
|
||||||
|
path: error.instancePath.split('/').filter(p => p),
|
||||||
|
message: error.message,
|
||||||
|
type: 'schema_violation',
|
||||||
|
details: error.params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
result.valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// uxscii-specific validation checks
|
||||||
|
performUxsciiChecks(component, uxmFilePath, result);
|
||||||
|
|
||||||
|
// Collect stats
|
||||||
|
result.stats = {
|
||||||
|
id: component.id,
|
||||||
|
type: component.type,
|
||||||
|
version: component.version,
|
||||||
|
states: component.behavior?.states?.length || 0,
|
||||||
|
props: Object.keys(component.props || {}).length,
|
||||||
|
interactive: component.behavior?.interactions?.length > 0,
|
||||||
|
hasAccessibility: !!component.behavior?.accessibility
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform uxscii-specific validation checks
|
||||||
|
* @param {Object} component - Parsed component object
|
||||||
|
* @param {string} uxmFilePath - Path to .uxm file
|
||||||
|
* @param {Object} result - Result object to populate
|
||||||
|
*/
|
||||||
|
function performUxsciiChecks(component, uxmFilePath, result) {
|
||||||
|
// Check 1: ASCII template file exists
|
||||||
|
const mdFilePath = uxmFilePath.replace('.uxm', '.md');
|
||||||
|
let mdContent = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
mdContent = fs.readFileSync(mdFilePath, 'utf8');
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
result.valid = false;
|
||||||
|
result.errors.push({
|
||||||
|
path: ['ascii', 'templateFile'],
|
||||||
|
message: `ASCII template file not found: ${mdFilePath}`,
|
||||||
|
type: 'missing_file'
|
||||||
|
});
|
||||||
|
return; // Can't do variable checks without .md file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check 2: Template variables match
|
||||||
|
if (mdContent) {
|
||||||
|
checkTemplateVariables(component, mdContent, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check 3: Accessibility requirements for interactive components
|
||||||
|
checkAccessibility(component, result);
|
||||||
|
|
||||||
|
// Check 4: ASCII dimensions
|
||||||
|
checkAsciiDimensions(component, result);
|
||||||
|
|
||||||
|
// Check 5: States have properties
|
||||||
|
checkStates(component, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that template variables in .md match those defined in .uxm
|
||||||
|
* @param {Object} component - Component object
|
||||||
|
* @param {string} mdContent - Content of .md file
|
||||||
|
* @param {Object} result - Result object
|
||||||
|
*/
|
||||||
|
function checkTemplateVariables(component, mdContent, result) {
|
||||||
|
// Extract {{variables}} from markdown template
|
||||||
|
const varPattern = /\{\{(\w+)\}\}/g;
|
||||||
|
const mdVars = new Set();
|
||||||
|
let match;
|
||||||
|
while ((match = varPattern.exec(mdContent)) !== null) {
|
||||||
|
mdVars.add(match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get variables from .uxm (handle both array of strings and array of objects)
|
||||||
|
const asciiVars = component.ascii?.variables || [];
|
||||||
|
const uxmVars = new Set();
|
||||||
|
|
||||||
|
if (asciiVars.length > 0) {
|
||||||
|
if (typeof asciiVars[0] === 'object') {
|
||||||
|
// Array of objects format: [{"name": "text", "type": "string"}]
|
||||||
|
asciiVars.forEach(v => {
|
||||||
|
if (v && typeof v === 'object' && v.name) {
|
||||||
|
uxmVars.add(v.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Array of strings format: ["text", "value"]
|
||||||
|
asciiVars.forEach(v => uxmVars.add(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for variables in .md but not defined in .uxm
|
||||||
|
const missing = [...mdVars].filter(v => !uxmVars.has(v));
|
||||||
|
if (missing.length > 0) {
|
||||||
|
result.warnings.push({
|
||||||
|
path: ['ascii', 'variables'],
|
||||||
|
message: `Variables in .md but not defined in .uxm: ${missing.sort().join(', ')}`,
|
||||||
|
type: 'variable_mismatch'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check accessibility requirements
|
||||||
|
* @param {Object} component - Component object
|
||||||
|
* @param {Object} result - Result object
|
||||||
|
*/
|
||||||
|
function checkAccessibility(component, result) {
|
||||||
|
const hasInteractions = component.behavior?.interactions?.length > 0;
|
||||||
|
|
||||||
|
if (hasInteractions) {
|
||||||
|
const accessibility = component.behavior?.accessibility || {};
|
||||||
|
|
||||||
|
if (!accessibility.role) {
|
||||||
|
result.warnings.push({
|
||||||
|
path: ['behavior', 'accessibility', 'role'],
|
||||||
|
message: 'Interactive component should have ARIA role',
|
||||||
|
type: 'accessibility'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accessibility.focusable) {
|
||||||
|
result.warnings.push({
|
||||||
|
path: ['behavior', 'accessibility', 'focusable'],
|
||||||
|
message: 'Interactive component should be focusable',
|
||||||
|
type: 'accessibility'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check ASCII dimensions are within recommended limits
|
||||||
|
* @param {Object} component - Component object
|
||||||
|
* @param {Object} result - Result object
|
||||||
|
*/
|
||||||
|
function checkAsciiDimensions(component, result) {
|
||||||
|
const ascii = component.ascii || {};
|
||||||
|
const width = ascii.width || 0;
|
||||||
|
const height = ascii.height || 0;
|
||||||
|
|
||||||
|
if (width > 120) {
|
||||||
|
result.warnings.push({
|
||||||
|
path: ['ascii', 'width'],
|
||||||
|
message: `Width ${width} exceeds recommended max of 120`,
|
||||||
|
type: 'dimensions'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height > 50) {
|
||||||
|
result.warnings.push({
|
||||||
|
path: ['ascii', 'height'],
|
||||||
|
message: `Height ${height} exceeds recommended max of 50`,
|
||||||
|
type: 'dimensions'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that states have properties defined
|
||||||
|
* @param {Object} component - Component object
|
||||||
|
* @param {Object} result - Result object
|
||||||
|
*/
|
||||||
|
function checkStates(component, result) {
|
||||||
|
const states = component.behavior?.states || [];
|
||||||
|
|
||||||
|
for (const state of states) {
|
||||||
|
if (!state.properties || Object.keys(state.properties).length === 0) {
|
||||||
|
result.warnings.push({
|
||||||
|
path: ['behavior', 'states', state.name || 'unknown'],
|
||||||
|
message: `State '${state.name || 'unknown'}' has no properties defined`,
|
||||||
|
type: 'incomplete_state'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print results in human-readable format
|
||||||
|
* @param {Object} result - Validation result
|
||||||
|
*/
|
||||||
|
function printHumanReadable(result) {
|
||||||
|
if (result.valid) {
|
||||||
|
console.log(`✓ Valid: ${result.stats.id}`);
|
||||||
|
console.log(` Type: ${result.stats.type}`);
|
||||||
|
console.log(` Version: ${result.stats.version}`);
|
||||||
|
console.log(` States: ${result.stats.states}`);
|
||||||
|
console.log(` Props: ${result.stats.props}`);
|
||||||
|
|
||||||
|
if (result.warnings.length > 0) {
|
||||||
|
console.log(`\n Warnings: ${result.warnings.length}`);
|
||||||
|
result.warnings.forEach((warning, i) => {
|
||||||
|
const pathStr = warning.path.join(' → ') || 'root';
|
||||||
|
console.log(` ${i + 1}. ${warning.message}`);
|
||||||
|
console.log(` Location: ${pathStr}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`✗ Validation Failed`);
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
const maxErrors = 5;
|
||||||
|
result.errors.slice(0, maxErrors).forEach((error, i) => {
|
||||||
|
const pathStr = error.path.join(' → ') || 'root';
|
||||||
|
console.log(` Error ${i + 1}: ${error.message}`);
|
||||||
|
console.log(` Location: ${pathStr}`);
|
||||||
|
console.log('');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.errors.length > maxErrors) {
|
||||||
|
console.log(` ... and ${result.errors.length - maxErrors} more errors`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main entry point
|
||||||
|
*/
|
||||||
|
function main() {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
console.log('Usage: validate-component.js <component.uxm> <schema.json> [--json]');
|
||||||
|
console.log('');
|
||||||
|
console.log('Validates a uxscii component against the JSON schema.');
|
||||||
|
console.log('Returns JSON with validation results and exits 0 if valid, 1 if invalid.');
|
||||||
|
console.log('');
|
||||||
|
console.log('Options:');
|
||||||
|
console.log(' --json Output results as JSON instead of human-readable format');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uxmFile = args[0];
|
||||||
|
const schemaFile = args[1];
|
||||||
|
const jsonOutput = args.includes('--json');
|
||||||
|
|
||||||
|
const result = validateComponent(uxmFile, schemaFile);
|
||||||
|
|
||||||
|
if (jsonOutput) {
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
} else {
|
||||||
|
printHumanReadable(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(result.valid ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run if called directly
|
||||||
|
if (require.main === module) {
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { validateComponent };
|
||||||
222
skills/fluxwing-validator/validate-screen.js
Executable file
222
skills/fluxwing-validator/validate-screen.js
Executable file
@@ -0,0 +1,222 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Screen validation using component validator + screen-specific checks.
|
||||||
|
* Validates .uxm screen files with additional checks for rendered examples and composed components.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node validate-screen.js <screen.uxm> <schema.json>
|
||||||
|
* node validate-screen.js <screen.uxm> <schema.json> --json
|
||||||
|
*
|
||||||
|
* Exit codes:
|
||||||
|
* 0 - Valid screen
|
||||||
|
* 1 - Validation errors found
|
||||||
|
* 2 - Missing dependencies or invalid arguments
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Import component validator
|
||||||
|
const { validateComponent } = require('./validate-component.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a .uxm screen file with screen-specific checks
|
||||||
|
* @param {string} uxmFilePath - Path to .uxm file
|
||||||
|
* @param {string} schemaPath - Path to JSON schema file
|
||||||
|
* @returns {Object} Validation result object
|
||||||
|
*/
|
||||||
|
function validateScreen(uxmFilePath, schemaPath) {
|
||||||
|
// 1. Run standard component validation
|
||||||
|
const result = validateComponent(uxmFilePath, schemaPath);
|
||||||
|
|
||||||
|
// If component validation failed completely, return early
|
||||||
|
if (!fs.existsSync(uxmFilePath)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load screen data for screen-specific checks
|
||||||
|
let screen;
|
||||||
|
try {
|
||||||
|
const uxmContent = fs.readFileSync(uxmFilePath, 'utf8');
|
||||||
|
screen = JSON.parse(uxmContent);
|
||||||
|
} catch (error) {
|
||||||
|
// Already handled by validateComponent
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Screen-specific checks
|
||||||
|
checkRenderedFile(uxmFilePath, result);
|
||||||
|
checkComposedComponents(screen, uxmFilePath, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if .rendered.md file exists (recommended for screens)
|
||||||
|
* @param {string} uxmFilePath - Path to .uxm file
|
||||||
|
* @param {Object} result - Result object to populate
|
||||||
|
*/
|
||||||
|
function checkRenderedFile(uxmFilePath, result) {
|
||||||
|
const renderedFilePath = uxmFilePath.replace('.uxm', '.rendered.md');
|
||||||
|
|
||||||
|
if (!fs.existsSync(renderedFilePath)) {
|
||||||
|
result.warnings.push({
|
||||||
|
path: ['screen'],
|
||||||
|
message: `Rendered example file recommended for screens: ${renderedFilePath}`,
|
||||||
|
type: 'missing_rendered',
|
||||||
|
severity: 'info'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if components referenced in screen exist
|
||||||
|
* @param {Object} screen - Parsed screen object
|
||||||
|
* @param {string} uxmFilePath - Path to .uxm file
|
||||||
|
* @param {Object} result - Result object to populate
|
||||||
|
*/
|
||||||
|
function checkComposedComponents(screen, uxmFilePath, result) {
|
||||||
|
// Extract component references from screen
|
||||||
|
const composedComponents = extractComponentReferences(screen);
|
||||||
|
|
||||||
|
if (composedComponents.length === 0) {
|
||||||
|
return; // No composed components to check
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine base path for component lookups
|
||||||
|
const screenDir = path.dirname(uxmFilePath);
|
||||||
|
const projectRoot = findProjectRoot(screenDir);
|
||||||
|
const componentsDir = path.join(projectRoot, 'fluxwing', 'components');
|
||||||
|
|
||||||
|
for (const componentId of composedComponents) {
|
||||||
|
const componentPath = path.join(componentsDir, `${componentId}.uxm`);
|
||||||
|
|
||||||
|
if (!fs.existsSync(componentPath)) {
|
||||||
|
result.warnings.push({
|
||||||
|
path: ['composed'],
|
||||||
|
message: `Referenced component not found: ${componentId} (expected at ${componentPath})`,
|
||||||
|
type: 'missing_component',
|
||||||
|
severity: 'warning'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract component IDs referenced in screen
|
||||||
|
* @param {Object} screen - Parsed screen object
|
||||||
|
* @returns {Array<string>} Array of component IDs
|
||||||
|
*/
|
||||||
|
function extractComponentReferences(screen) {
|
||||||
|
const componentIds = new Set();
|
||||||
|
|
||||||
|
// Check if screen extends another component
|
||||||
|
if (screen.extends) {
|
||||||
|
componentIds.add(screen.extends);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check slots for component references
|
||||||
|
if (screen.slots) {
|
||||||
|
for (const slot of Object.values(screen.slots)) {
|
||||||
|
if (slot.component) {
|
||||||
|
componentIds.add(slot.component);
|
||||||
|
}
|
||||||
|
if (Array.isArray(slot.components)) {
|
||||||
|
slot.components.forEach(c => componentIds.add(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ASCII template content for component references (basic pattern matching)
|
||||||
|
// Pattern: {{component:component-id}}
|
||||||
|
const mdFilePath = screen.ascii?.templateFile;
|
||||||
|
if (mdFilePath) {
|
||||||
|
try {
|
||||||
|
const screenDir = path.dirname(screen.id);
|
||||||
|
const mdContent = fs.readFileSync(mdFilePath, 'utf8');
|
||||||
|
const componentPattern = /\{\{component:([a-z0-9-]+)\}\}/g;
|
||||||
|
let match;
|
||||||
|
while ((match = componentPattern.exec(mdContent)) !== null) {
|
||||||
|
componentIds.add(match[1]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Template file not found - already reported by component validator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(componentIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find project root directory (containing fluxwing/ directory)
|
||||||
|
* @param {string} startDir - Starting directory
|
||||||
|
* @returns {string} Project root path
|
||||||
|
*/
|
||||||
|
function findProjectRoot(startDir) {
|
||||||
|
let currentDir = startDir;
|
||||||
|
|
||||||
|
while (currentDir !== path.dirname(currentDir)) {
|
||||||
|
const fluxwingDir = path.join(currentDir, 'fluxwing');
|
||||||
|
if (fs.existsSync(fluxwingDir)) {
|
||||||
|
return currentDir;
|
||||||
|
}
|
||||||
|
currentDir = path.dirname(currentDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to current working directory
|
||||||
|
return process.cwd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI interface
|
||||||
|
if (require.main === module) {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
console.error('Usage: node validate-screen.js <screen.uxm> <schema.json> [--json]');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uxmFilePath = args[0];
|
||||||
|
const schemaPath = args[1];
|
||||||
|
const jsonOutput = args.includes('--json');
|
||||||
|
|
||||||
|
const result = validateScreen(uxmFilePath, schemaPath);
|
||||||
|
|
||||||
|
if (jsonOutput) {
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
} else {
|
||||||
|
if (result.valid) {
|
||||||
|
console.log(`✓ Valid: ${result.stats.id || 'screen'}`);
|
||||||
|
console.log(` Type: ${result.stats.type || 'unknown'}`);
|
||||||
|
console.log(` Version: ${result.stats.version || 'unknown'}`);
|
||||||
|
console.log(` States: ${result.stats.states || 0}`);
|
||||||
|
console.log(` Props: ${result.stats.props || 0}`);
|
||||||
|
|
||||||
|
if (result.warnings.length > 0) {
|
||||||
|
console.log(`\n⚠ Warnings:`);
|
||||||
|
result.warnings.forEach((warning, i) => {
|
||||||
|
console.log(`\n Warning ${i + 1}: ${warning.message}`);
|
||||||
|
if (warning.path.length > 0) {
|
||||||
|
console.log(` Location: ${warning.path.join(' → ')}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('✗ Validation Failed\n');
|
||||||
|
result.errors.forEach((error, i) => {
|
||||||
|
console.error(` Error ${i + 1}: ${error.message}`);
|
||||||
|
if (error.path.length > 0) {
|
||||||
|
console.error(` Location: ${error.path.join(' → ')}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.errors.length > 8) {
|
||||||
|
console.error(`\n ... and ${result.errors.length - 8} more errors`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(result.valid ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { validateScreen };
|
||||||
659
skills/shared/docs/copy-versioning.md
Normal file
659
skills/shared/docs/copy-versioning.md
Normal file
@@ -0,0 +1,659 @@
|
|||||||
|
# Copy-on-Update Versioning Pattern
|
||||||
|
|
||||||
|
**Version**: 1.0.0
|
||||||
|
**Purpose**: Non-destructive component and screen modifications
|
||||||
|
**Applies to**: component-creator, screen-scaffolder, enhancer
|
||||||
|
**Does NOT apply to**: component-expander (uses in-place updates)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
When modifying existing components or screens, create versioned copies instead of overwriting originals. This ensures non-destructive editing and preserves component history.
|
||||||
|
|
||||||
|
**Core Principle**: Always start with UXM file, even if user mentions `.md` file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When to Use Copy-on-Update
|
||||||
|
|
||||||
|
### ✅ Use Copy-on-Update For:
|
||||||
|
|
||||||
|
1. **Creating component that already exists** (component-creator)
|
||||||
|
- User: "Create a submit button"
|
||||||
|
- Existing: `submit-button.uxm` already exists
|
||||||
|
- Action: Ask user, offer to create versioned copy
|
||||||
|
|
||||||
|
2. **Enhancing component fidelity** (enhancer)
|
||||||
|
- User: "Enhance button to production fidelity"
|
||||||
|
- Existing: `button.uxm` (fidelity=sketch)
|
||||||
|
- Action: Create `button-v2.uxm` (fidelity=production)
|
||||||
|
|
||||||
|
3. **Creating screen that already exists** (screen-scaffolder)
|
||||||
|
- User: "Create a login screen"
|
||||||
|
- Existing: `login-screen.uxm` already exists
|
||||||
|
- Action: Ask user, offer to create versioned copy
|
||||||
|
|
||||||
|
4. **Modifying layout/structure** (any skill)
|
||||||
|
- User: "Change button.md to use different colors"
|
||||||
|
- Existing: `button.uxm` exists
|
||||||
|
- Action: Create `button-v2.uxm` and `button-v2.md` with changes
|
||||||
|
|
||||||
|
### ❌ Do NOT Use Copy-on-Update For:
|
||||||
|
|
||||||
|
1. **Adding states to components** (component-expander only)
|
||||||
|
- User: "Add hover state to my button"
|
||||||
|
- Action: Modify `button.uxm` in place (same component, enhanced)
|
||||||
|
- Reason: Adding states enhances the same component ID
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version Numbering
|
||||||
|
|
||||||
|
### File Naming Pattern
|
||||||
|
|
||||||
|
```
|
||||||
|
Original: {base-name}.uxm (e.g., submit-button.uxm)
|
||||||
|
Version 2: {base-name}-v2.uxm (e.g., submit-button-v2.uxm)
|
||||||
|
Version 3: {base-name}-v3.uxm (e.g., submit-button-v3.uxm)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rules**:
|
||||||
|
- Original file has NO version suffix
|
||||||
|
- Versions start at `-v2` (not `-v1`)
|
||||||
|
- Version number matches across `.uxm` and `.md` files
|
||||||
|
- For screens: All three files use same suffix (`.uxm`, `.md`, `.rendered.md`)
|
||||||
|
|
||||||
|
### Component ID Pattern
|
||||||
|
|
||||||
|
The `id` field in the `.uxm` file must match the filename:
|
||||||
|
|
||||||
|
```json
|
||||||
|
// Original: submit-button.uxm
|
||||||
|
{
|
||||||
|
"id": "submit-button",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version 2: submit-button-v2.uxm
|
||||||
|
{
|
||||||
|
"id": "submit-button-v2",
|
||||||
|
"version": "1.1.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Semantic Versioning
|
||||||
|
|
||||||
|
**Format**: `MAJOR.MINOR.PATCH` (e.g., `1.2.3`)
|
||||||
|
|
||||||
|
**Increment rules**:
|
||||||
|
- **Minor version** (1.X.0): New features, fidelity enhancements, layout changes
|
||||||
|
- Original: 1.0.0 → First copy: 1.1.0 → Second copy: 1.2.0
|
||||||
|
- **Major version** (X.0.0): Breaking changes (manual only, not auto-incremented)
|
||||||
|
- **Patch version** (1.0.X): Not used in copy-on-update pattern
|
||||||
|
|
||||||
|
**Copy-on-update always increments minor version**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detection Algorithm
|
||||||
|
|
||||||
|
### Step 1: Parse Component/Screen Name
|
||||||
|
|
||||||
|
Extract the base name from user request, ignoring `.uxm` or `.md` extensions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Examples:
|
||||||
|
"Update submit-button.md" → componentName="submit-button"
|
||||||
|
"Enhance button to production" → componentName="button"
|
||||||
|
"Create a login screen" → screenName="login-screen"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Check if Original Exists
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For components:
|
||||||
|
if [[ -f "./fluxwing/components/${componentName}.uxm" ]]; then
|
||||||
|
echo "Component exists - entering copy-on-update mode"
|
||||||
|
else
|
||||||
|
echo "Component does not exist - entering create mode"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For screens:
|
||||||
|
if [[ -f "./fluxwing/screens/${screenName}.uxm" ]]; then
|
||||||
|
echo "Screen exists - entering copy-on-update mode"
|
||||||
|
else
|
||||||
|
echo "Screen does not exist - entering create mode"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Find Highest Existing Version
|
||||||
|
|
||||||
|
If original exists, find the highest versioned copy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Initialize
|
||||||
|
maxVersion=1 # Original is considered v1
|
||||||
|
|
||||||
|
# Find all versioned files
|
||||||
|
for file in ./fluxwing/components/${componentName}-v*.uxm; do
|
||||||
|
if [[ -f "$file" ]]; then
|
||||||
|
# Extract version number (e.g., submit-button-v3.uxm → 3)
|
||||||
|
version=$(echo "$file" | grep -oP 'v\K\d+')
|
||||||
|
if [[ $version -gt $maxVersion ]]; then
|
||||||
|
maxVersion=$version
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Calculate next version
|
||||||
|
nextVersion=$((maxVersion + 1))
|
||||||
|
newId="${componentName}-v${nextVersion}"
|
||||||
|
|
||||||
|
echo "Creating version: ${newId}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Edge case - Version gaps**: If v2 is missing but v3 and v5 exist, use MAX(versions)+1 = v6. Gaps are acceptable.
|
||||||
|
|
||||||
|
### Step 4: Load Source for Copying
|
||||||
|
|
||||||
|
Determine which file to base the new version on:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check if highest version exists
|
||||||
|
if [[ -f "./fluxwing/components/${componentName}-v${maxVersion}.uxm" ]]; then
|
||||||
|
# Copy from highest version (e.g., v3 if it exists)
|
||||||
|
sourceFile="${componentName}-v${maxVersion}.uxm"
|
||||||
|
else
|
||||||
|
# Copy from original (v1)
|
||||||
|
sourceFile="${componentName}.uxm"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Basing new version on: ${sourceFile}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Metadata Updates
|
||||||
|
|
||||||
|
### Required Changes in .uxm File
|
||||||
|
|
||||||
|
When creating a versioned copy, update these fields:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "submit-button-v2", // Add -v{N} suffix
|
||||||
|
"version": "1.1.0", // Increment minor version
|
||||||
|
"metadata": {
|
||||||
|
"created": "2024-10-11T12:00:00Z", // Preserve from original
|
||||||
|
"modified": "2024-10-12T15:30:00Z", // Update to current timestamp
|
||||||
|
"fidelity": "detailed" // Update if enhancing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Field-by-Field Rules
|
||||||
|
|
||||||
|
| Field | Action | Example |
|
||||||
|
|-------|--------|---------|
|
||||||
|
| `id` | Add `-v{N}` suffix | `submit-button` → `submit-button-v2` |
|
||||||
|
| `version` | Increment minor | `1.0.0` → `1.1.0` → `1.2.0` |
|
||||||
|
| `metadata.created` | **Preserve** from original | `2024-10-11T12:00:00Z` (unchanged) |
|
||||||
|
| `metadata.modified` | **Update** to current time | `2024-10-12T15:30:00Z` (new) |
|
||||||
|
| `metadata.fidelity` | Update if enhancing | `sketch` → `detailed` |
|
||||||
|
| `metadata.name` | Keep same display name | `Submit Button` (unchanged) |
|
||||||
|
| `metadata.description` | Preserve or enhance | May be improved |
|
||||||
|
|
||||||
|
### Current Timestamp Format
|
||||||
|
|
||||||
|
Use ISO 8601 format:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate current timestamp
|
||||||
|
currentTimestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
# Example: 2024-10-12T15:30:45Z
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Communication
|
||||||
|
|
||||||
|
### Clear Messaging Patterns
|
||||||
|
|
||||||
|
Always inform user about versioning actions:
|
||||||
|
|
||||||
|
**✅ Good examples**:
|
||||||
|
```
|
||||||
|
"Found existing submit-button (v1.0.0). Creating submit-button-v2..."
|
||||||
|
"Created submit-button-v2 (v1.1.0) with production fidelity. Original preserved."
|
||||||
|
"Enhanced login-screen → login-screen-v2. Files created:
|
||||||
|
- ./fluxwing/screens/login-screen-v2.uxm
|
||||||
|
- ./fluxwing/screens/login-screen-v2.md
|
||||||
|
- ./fluxwing/screens/login-screen-v2.rendered.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
**❌ Bad examples**:
|
||||||
|
```
|
||||||
|
"Updated submit-button" (doesn't mention versioning)
|
||||||
|
"Modified button.md" (implies in-place change)
|
||||||
|
"Created new component" (vague, no version info)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interactive Prompts
|
||||||
|
|
||||||
|
When component/screen already exists, ask user for choice:
|
||||||
|
|
||||||
|
```
|
||||||
|
Component 'submit-button' already exists (v1.0.0).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
(a) Create new version (submit-button-v2)
|
||||||
|
(b) Create with different name
|
||||||
|
(c) Cancel operation
|
||||||
|
|
||||||
|
What would you like to do?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Handle user responses**:
|
||||||
|
- **(a)**: Proceed with copy-on-update (create v2)
|
||||||
|
- **(b)**: Ask for new name, create with that name
|
||||||
|
- **(c)**: Cancel, no files created
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File System Layout
|
||||||
|
|
||||||
|
### Components Example
|
||||||
|
|
||||||
|
```
|
||||||
|
./fluxwing/components/
|
||||||
|
├── submit-button.uxm # Original (v1.0.0, fidelity=sketch)
|
||||||
|
├── submit-button.md
|
||||||
|
├── submit-button-v2.uxm # First update (v1.1.0, fidelity=detailed)
|
||||||
|
├── submit-button-v2.md
|
||||||
|
├── submit-button-v3.uxm # Second update (v1.2.0, fidelity=production)
|
||||||
|
└── submit-button-v3.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### Screens Example
|
||||||
|
|
||||||
|
```
|
||||||
|
./fluxwing/screens/
|
||||||
|
├── login-screen.uxm # Original (v1.0.0)
|
||||||
|
├── login-screen.md
|
||||||
|
├── login-screen.rendered.md
|
||||||
|
├── login-screen-v2.uxm # First update (v1.1.0)
|
||||||
|
├── login-screen-v2.md
|
||||||
|
└── login-screen-v2.rendered.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: Screens have THREE files per version, all with matching suffix.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Edge Cases
|
||||||
|
|
||||||
|
### Edge Case 1: User Mentions .md File Only
|
||||||
|
|
||||||
|
**Request**: "Update submit-button.md to use darker colors"
|
||||||
|
|
||||||
|
**Handling**:
|
||||||
|
1. Parse component name: `submit-button`
|
||||||
|
2. Check for `submit-button.uxm` (ALWAYS start with UXM, not .md)
|
||||||
|
3. If `.uxm` exists: Enter copy-on-update mode
|
||||||
|
4. Create `submit-button-v2.uxm` AND `submit-button-v2.md`
|
||||||
|
5. Apply color changes to BOTH files
|
||||||
|
6. Inform user: "Updated both submit-button-v2.uxm and .md (UXM-first approach)"
|
||||||
|
|
||||||
|
**Why**: UXM file is source of truth. Changes to .md alone would break synchronization.
|
||||||
|
|
||||||
|
### Edge Case 2: Multiple Rapid Updates
|
||||||
|
|
||||||
|
**Requests**:
|
||||||
|
1. User: "Enhance button to detailed fidelity"
|
||||||
|
2. User: "Now enhance to production fidelity"
|
||||||
|
|
||||||
|
**Handling**:
|
||||||
|
1. First request: Create `button-v2.uxm` (detailed fidelity)
|
||||||
|
2. Second request:
|
||||||
|
- Find highest version (v2)
|
||||||
|
- Create `button-v3.uxm` (production fidelity)
|
||||||
|
- Base v3 on v2 content
|
||||||
|
|
||||||
|
**Result**: Three versions exist (original, v2, v3), each preserved.
|
||||||
|
|
||||||
|
**Note**: If user says "Add hover state" then "Add focus state", component-expander handles this with in-place updates (NO versioning).
|
||||||
|
|
||||||
|
### Edge Case 3: Version Number Gaps
|
||||||
|
|
||||||
|
**Scenario**: `button.uxm` exists, `button-v3.uxm` exists, but `button-v2.uxm` is missing
|
||||||
|
|
||||||
|
**Handling**:
|
||||||
|
1. Find ALL versions: v1 (original), v3
|
||||||
|
2. MAX(versions) = 3
|
||||||
|
3. Next version = 4
|
||||||
|
4. Create `button-v4.uxm`
|
||||||
|
|
||||||
|
**Result**: Gaps in sequence are acceptable. Always use MAX+1.
|
||||||
|
|
||||||
|
### Edge Case 4: Mixing Versioned and Original References
|
||||||
|
|
||||||
|
**Scenario**: User creates `button-v2`, then says "enhance button to production"
|
||||||
|
|
||||||
|
**Handling**:
|
||||||
|
1. "enhance button" likely refers to latest version
|
||||||
|
2. Check for highest version: v2
|
||||||
|
3. Base new version (v3) on v2
|
||||||
|
4. User can explicitly reference: "enhance button-v1 to production" if they want original
|
||||||
|
|
||||||
|
**Guideline**: Default to highest version when ambiguous.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration with Skills
|
||||||
|
|
||||||
|
### For component-creator
|
||||||
|
|
||||||
|
**Load this module**: ✅ Yes
|
||||||
|
|
||||||
|
**When to use**:
|
||||||
|
- Before invoking designer agent
|
||||||
|
- Check if `./fluxwing/components/{component-id}.uxm` exists
|
||||||
|
- If exists: Ask user, offer copy-on-update
|
||||||
|
- If creating version: Pass base component info to agent
|
||||||
|
|
||||||
|
**Agent prompt addition**:
|
||||||
|
```
|
||||||
|
If creating versioned copy:
|
||||||
|
1. Read existing {component-id}.uxm
|
||||||
|
2. Find highest version using algorithm above
|
||||||
|
3. Generate new ID: {component-id}-v{N+1}
|
||||||
|
4. Copy content from source version
|
||||||
|
5. Apply requested changes
|
||||||
|
6. Update metadata (created, modified, version)
|
||||||
|
7. Save as {component-id}-v{N+1}.uxm and .md
|
||||||
|
```
|
||||||
|
|
||||||
|
### For screen-scaffolder
|
||||||
|
|
||||||
|
**Load this module**: ✅ Yes
|
||||||
|
|
||||||
|
**When to use**:
|
||||||
|
- Before Phase 1 (component creation)
|
||||||
|
- Check if `./fluxwing/screens/{screen-name}.uxm` exists
|
||||||
|
- If exists: Ask user, offer copy-on-update
|
||||||
|
- If creating version: Pass to composer agent
|
||||||
|
|
||||||
|
**Agent prompt addition**:
|
||||||
|
```
|
||||||
|
If creating versioned screen:
|
||||||
|
1. Read existing {screen-name}.uxm
|
||||||
|
2. Find highest version
|
||||||
|
3. Generate new ID: {screen-name}-v{N+1}
|
||||||
|
4. Create THREE files:
|
||||||
|
- {screen-name}-v{N+1}.uxm
|
||||||
|
- {screen-name}-v{N+1}.md
|
||||||
|
- {screen-name}-v{N+1}.rendered.md
|
||||||
|
5. All files use same version suffix
|
||||||
|
```
|
||||||
|
|
||||||
|
### For enhancer
|
||||||
|
|
||||||
|
**Load this module**: ✅ Yes
|
||||||
|
|
||||||
|
**When to use**:
|
||||||
|
- Always use copy-on-update for fidelity enhancements
|
||||||
|
- Each fidelity level creates new version
|
||||||
|
- Original fidelity preserved
|
||||||
|
|
||||||
|
**Agent prompt addition**:
|
||||||
|
```
|
||||||
|
When enhancing component:
|
||||||
|
1. Read current {component-id}.uxm
|
||||||
|
2. Note current fidelity level
|
||||||
|
3. Find highest version
|
||||||
|
4. Create {component-id}-v{N+1}.uxm and .md
|
||||||
|
5. Apply fidelity enhancements to new version
|
||||||
|
6. Update metadata.fidelity field
|
||||||
|
7. Update metadata.version (increment minor)
|
||||||
|
8. DO NOT overwrite original
|
||||||
|
```
|
||||||
|
|
||||||
|
### For component-expander
|
||||||
|
|
||||||
|
**Load this module**: ❌ **NO - Do not load**
|
||||||
|
|
||||||
|
**Reason**: component-expander uses in-place updates. Adding states enhances the same component ID, not creating a new version.
|
||||||
|
|
||||||
|
**Keep existing behavior**:
|
||||||
|
```
|
||||||
|
When adding states:
|
||||||
|
1. Read {component-name}.uxm
|
||||||
|
2. Modify in place (same file)
|
||||||
|
3. Add new states to behavior.states array
|
||||||
|
4. Append state sections to .md file
|
||||||
|
5. Update metadata.modified timestamp
|
||||||
|
6. Save (overwrite existing files)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation Checklist
|
||||||
|
|
||||||
|
After creating versioned copy, verify:
|
||||||
|
|
||||||
|
- [ ] Filename has `-v{N}` suffix (e.g., `submit-button-v2.uxm`)
|
||||||
|
- [ ] `id` field matches filename (e.g., `"id": "submit-button-v2"`)
|
||||||
|
- [ ] `version` field incremented correctly (e.g., `1.0.0` → `1.1.0`)
|
||||||
|
- [ ] `metadata.created` preserved from original
|
||||||
|
- [ ] `metadata.modified` set to current timestamp
|
||||||
|
- [ ] Both `.uxm` and `.md` created with same version suffix
|
||||||
|
- [ ] For screens: All three files created (`.uxm`, `.md`, `.rendered.md`)
|
||||||
|
- [ ] Original files unchanged
|
||||||
|
- [ ] JSON schema validation passes
|
||||||
|
- [ ] Variables match between `.uxm` and `.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Version Suffix Rules
|
||||||
|
|
||||||
|
| Item | Format | Example |
|
||||||
|
|------|--------|---------|
|
||||||
|
| Original filename | `{name}.uxm` | `button.uxm` |
|
||||||
|
| Original ID | `{name}` | `button` |
|
||||||
|
| Original version | `1.0.0` | `1.0.0` |
|
||||||
|
| First copy filename | `{name}-v2.uxm` | `button-v2.uxm` |
|
||||||
|
| First copy ID | `{name}-v2` | `button-v2` |
|
||||||
|
| First copy version | `1.1.0` | `1.1.0` |
|
||||||
|
| Second copy filename | `{name}-v3.uxm` | `button-v3.uxm` |
|
||||||
|
| Second copy ID | `{name}-v3` | `button-v3` |
|
||||||
|
| Second copy version | `1.2.0` | `1.2.0` |
|
||||||
|
|
||||||
|
### Skills Decision Tree
|
||||||
|
|
||||||
|
```
|
||||||
|
User requests modification
|
||||||
|
│
|
||||||
|
├─ "Add hover/focus/disabled state"
|
||||||
|
│ └─ Use component-expander
|
||||||
|
│ └─ In-place update (NO versioning)
|
||||||
|
│
|
||||||
|
├─ "Enhance to X fidelity"
|
||||||
|
│ └─ Use enhancer
|
||||||
|
│ └─ Copy-on-update (create v2)
|
||||||
|
│
|
||||||
|
├─ "Create component" (exists)
|
||||||
|
│ └─ Use component-creator
|
||||||
|
│ └─ Ask user, offer copy-on-update
|
||||||
|
│
|
||||||
|
├─ "Create screen" (exists)
|
||||||
|
│ └─ Use screen-scaffolder
|
||||||
|
│ └─ Ask user, offer copy-on-update
|
||||||
|
│
|
||||||
|
└─ "Update/modify .md file"
|
||||||
|
└─ Any skill
|
||||||
|
└─ UXM-first, then copy-on-update
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Enhance Component Fidelity
|
||||||
|
|
||||||
|
**User request**: "Enhance button to production fidelity"
|
||||||
|
|
||||||
|
**Current state**:
|
||||||
|
- `button.uxm` exists (v1.0.0, fidelity=sketch)
|
||||||
|
- `button.md` exists
|
||||||
|
|
||||||
|
**Process**:
|
||||||
|
1. Parse name: `button`
|
||||||
|
2. Check existence: `button.uxm` exists ✓
|
||||||
|
3. Find versions: Only original (v1) exists
|
||||||
|
4. Next version: v2
|
||||||
|
5. Create: `button-v2.uxm` and `button-v2.md`
|
||||||
|
6. Copy content from `button.uxm`
|
||||||
|
7. Update:
|
||||||
|
- `id`: `button` → `button-v2`
|
||||||
|
- `version`: `1.0.0` → `1.1.0`
|
||||||
|
- `metadata.fidelity`: `sketch` → `production`
|
||||||
|
- `metadata.modified`: current timestamp
|
||||||
|
- `metadata.created`: preserve from original
|
||||||
|
8. Apply production-level enhancements
|
||||||
|
9. Validate both files
|
||||||
|
|
||||||
|
**Result**:
|
||||||
|
- `button.uxm` unchanged (v1.0.0, sketch)
|
||||||
|
- `button-v2.uxm` created (v1.1.0, production)
|
||||||
|
- `button-v2.md` created
|
||||||
|
- User message: "Enhanced button → button-v2 (fidelity: production). Original preserved."
|
||||||
|
|
||||||
|
### Example 2: Create Existing Screen
|
||||||
|
|
||||||
|
**User request**: "Create a login screen"
|
||||||
|
|
||||||
|
**Current state**:
|
||||||
|
- `login-screen.uxm` exists (v1.0.0)
|
||||||
|
- `login-screen.md` exists
|
||||||
|
- `login-screen.rendered.md` exists
|
||||||
|
|
||||||
|
**Process**:
|
||||||
|
1. Parse name: `login-screen`
|
||||||
|
2. Check existence: `login-screen.uxm` exists ✓
|
||||||
|
3. Ask user:
|
||||||
|
```
|
||||||
|
Screen 'login-screen' already exists (v1.0.0).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
(a) Create new version (login-screen-v2)
|
||||||
|
(b) Create with different name
|
||||||
|
(c) Cancel operation
|
||||||
|
|
||||||
|
What would you like to do?
|
||||||
|
```
|
||||||
|
4. User chooses: (a) Create new version
|
||||||
|
5. Find versions: Only original (v1) exists
|
||||||
|
6. Next version: v2
|
||||||
|
7. Create THREE files:
|
||||||
|
- `login-screen-v2.uxm`
|
||||||
|
- `login-screen-v2.md`
|
||||||
|
- `login-screen-v2.rendered.md`
|
||||||
|
8. Copy and update metadata
|
||||||
|
9. Apply any requested changes
|
||||||
|
|
||||||
|
**Result**:
|
||||||
|
- Original files unchanged
|
||||||
|
- Three new files created with `-v2` suffix
|
||||||
|
- User message: "Created login-screen-v2. Original login-screen preserved."
|
||||||
|
|
||||||
|
### Example 3: User Mentions .md Only
|
||||||
|
|
||||||
|
**User request**: "Update button.md to use Unicode box drawing"
|
||||||
|
|
||||||
|
**Current state**:
|
||||||
|
- `button.uxm` exists (v1.0.0)
|
||||||
|
- `button.md` exists (ASCII box drawing)
|
||||||
|
|
||||||
|
**Process**:
|
||||||
|
1. Parse name: `button` (strip .md extension)
|
||||||
|
2. **UXM-first**: Check `button.uxm` exists ✓
|
||||||
|
3. Find versions: Only original exists
|
||||||
|
4. Next version: v2
|
||||||
|
5. Create: `button-v2.uxm` AND `button-v2.md`
|
||||||
|
6. Apply Unicode box drawing to `.md`
|
||||||
|
7. Update `.uxm` metadata
|
||||||
|
|
||||||
|
**Result**:
|
||||||
|
- Both `button-v2.uxm` and `button-v2.md` created
|
||||||
|
- User message: "Updated both button-v2.uxm and .md with Unicode box drawing (UXM-first approach). Original preserved."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Issue: Can't find highest version
|
||||||
|
|
||||||
|
**Symptom**: Multiple versions exist but algorithm fails
|
||||||
|
|
||||||
|
**Debug**:
|
||||||
|
```bash
|
||||||
|
# List all versions
|
||||||
|
ls -1 ./fluxwing/components/button*.uxm
|
||||||
|
# Should show: button.uxm, button-v2.uxm, button-v3.uxm
|
||||||
|
|
||||||
|
# Test regex extraction
|
||||||
|
for f in ./fluxwing/components/button-v*.uxm; do
|
||||||
|
echo "$f" | grep -oP 'v\K\d+'
|
||||||
|
done
|
||||||
|
# Should output: 2, 3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix**: Ensure version suffix matches pattern `-v{digits}` exactly.
|
||||||
|
|
||||||
|
### Issue: Version number collision
|
||||||
|
|
||||||
|
**Symptom**: Two processes create same version number
|
||||||
|
|
||||||
|
**Prevention**: Always re-check highest version immediately before creating new file.
|
||||||
|
|
||||||
|
**Recovery**: If collision occurs, increment again and retry.
|
||||||
|
|
||||||
|
### Issue: Metadata.created not preserved
|
||||||
|
|
||||||
|
**Symptom**: New version has different created timestamp
|
||||||
|
|
||||||
|
**Fix**: Always read original's `metadata.created` and copy to new version.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract created timestamp from original
|
||||||
|
originalCreated=$(jq -r '.metadata.created' ./fluxwing/components/button.uxm)
|
||||||
|
|
||||||
|
# Use in new version
|
||||||
|
jq --arg created "$originalCreated" '.metadata.created = $created' button-v2.uxm
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Copy-on-update ensures**:
|
||||||
|
- ✅ Non-destructive editing
|
||||||
|
- ✅ Component history preserved
|
||||||
|
- ✅ Clear version progression
|
||||||
|
- ✅ UXM-first consistency
|
||||||
|
- ✅ User awareness and control
|
||||||
|
|
||||||
|
**Remember**:
|
||||||
|
- Start with UXM, even if user mentions .md
|
||||||
|
- Always ask user when component/screen exists
|
||||||
|
- Increment minor version automatically
|
||||||
|
- Preserve created timestamp
|
||||||
|
- Update modified timestamp
|
||||||
|
- Create matching .uxm and .md files together
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**End of Copy-on-Update Versioning Pattern Documentation**
|
||||||
Reference in New Issue
Block a user