@goblin-systems/goblin-design-system 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +152 -0
- package/dist/headless/dom.d.ts +12 -0
- package/dist/headless/modal.d.ts +57 -0
- package/dist/headless/navigation.d.ts +33 -0
- package/dist/headless/range.d.ts +28 -0
- package/dist/headless/search.d.ts +31 -0
- package/dist/headless/split-pane.d.ts +17 -0
- package/dist/headless/tabs.d.ts +13 -0
- package/dist/headless/toast.d.ts +9 -0
- package/dist/icons/index.d.ts +20 -0
- package/dist/index.cjs +5 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +716 -0
- package/dist/platform/tauri-window.d.ts +7 -0
- package/dist/style.css +1 -0
- package/dist/waveform/waveform.d.ts +21 -0
- package/package.json +46 -0
- package/skills/SKILL.md +372 -0
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--goblin-purple-500: #6c63ff;--goblin-purple-400: #7c73ff;--goblin-purple-300: #8f87ff;--goblin-purple-200: #b8b4ff;--goblin-green-400: #4ade80;--goblin-green-300: #34d399;--goblin-amber-400: #fbbf24;--goblin-amber-300: #f59e0b;--goblin-red-400: #f87171;--goblin-red-500: #ef4444;--goblin-blue-400: #60a5fa;--goblin-blue-300: #7dd3fc;--goblin-cyan-400: #22d3ee;--goblin-pink-400: #f472b6;--bg: #0d1324;--bg-section: #16213e;--bg-input: #0f1729;--bg-deep: #0b1020;--border: #2a2a4a;--border-focus: #6c63ff;--text: #e0e0e0;--text-muted: #8888aa;--text-heading: #ffffff;--text-subtle: #b8b8d4;--accent: #6c63ff;--accent-hover: #7c73ff;--success: #4ade80;--warning: #fbbf24;--error: #f87171;--badge-beta: #f59e0b;--badge-default: #6c63ff;--font: "Segoe UI", -apple-system, BlinkMacSystemFont, sans-serif;--font-mono: "Cascadia Code", "Fira Code", "Consolas", monospace;--font-size-xs: 10px;--font-size-sm: 11px;--font-size-md: 12px;--font-size-base: 13px;--font-size-lg: 14px;--font-size-xl: 15px;--font-size-2xl: 16px;--shadow-card: 0 14px 26px rgba(4, 8, 18, .45);--shadow-modal: 0 20px 45px rgba(4, 8, 18, .58);--shadow-toast: 0 18px 45px rgba(0, 0, 0, .38);--shadow-inset: inset 0 1px 0 rgba(255, 255, 255, .04);--space-1: 4px;--space-2: 6px;--space-3: 8px;--space-4: 10px;--space-5: 12px;--space-6: 14px;--space-7: 16px;--space-8: 20px;--space-9: 24px;--space-10: 32px;--duration-fast: .15s;--duration-base: .22s;--duration-slow: .3s;--easing-standard: ease;--easing-spring: cubic-bezier(.34, 1.56, .64, 1);--scrollbar-thumb: linear-gradient(180deg, rgba(124, 115, 255, .88), rgba(89, 81, 220, .86));--scrollbar-thumb-hover: linear-gradient(180deg, rgba(143, 135, 255, .95), rgba(107, 98, 234, .92));--scrollbar-track: rgba(10, 14, 26, .42)}*{margin:0;padding:0;box-sizing:border-box;scrollbar-width:thin;scrollbar-color:rgba(108,99,255,.45) rgba(10,14,26,.42)}*::-webkit-scrollbar{width:10px}*::-webkit-scrollbar-track{background:var(--scrollbar-track);border-radius:0}*::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border:2px solid rgba(10,14,26,.42);border-radius:0}*::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}html{font-size:14px}body{font-family:var(--font);background:radial-gradient(120% 80% at 50% 0%,rgba(108,99,255,.16),transparent 55%),radial-gradient(90% 60% at 100% 100%,rgba(34,197,94,.08),transparent 60%),var(--bg);color:var(--text);font-size:var(--font-size-lg);line-height:1.5;-webkit-user-select:none;user-select:none}button,input,select{font:inherit}:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px}::selection{background:#6c63ff59;color:var(--text-heading)}input[type=text],input[type=password],input[type=number],select{background:var(--bg-input);border:1px solid var(--border);border-radius:0;color:var(--text);padding:8px 12px;font-family:var(--font-mono);outline:none;transition:border-color var(--duration-base);width:100%}input:focus,select:focus{border-color:var(--border-focus)}select{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238888aa' d='M2 4l4 4 4-4'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;padding-right:32px}input[type=radio],input[type=checkbox]{accent-color:var(--accent);width:16px;height:16px;cursor:pointer}input[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:3px;background:linear-gradient(to right,var(--accent) var(--_pct, 0%),var(--border) var(--_pct, 0%));outline:none;cursor:pointer;padding:0;border:none}input[type=range]::-webkit-slider-runnable-track{height:3px}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:13px;height:13px;background:var(--accent);border:2px solid var(--bg);cursor:grab;margin-top:-5px;transition:background var(--duration-fast)}input[type=range]::-webkit-slider-thumb:active{cursor:grabbing;background:var(--accent-hover)}input[type=range]::-moz-range-thumb{width:13px;height:13px;background:var(--accent);border:2px solid var(--bg);border-radius:0;cursor:grab}input[type=range]::-moz-range-track{height:3px;background:var(--border)}input[type=range]::-moz-range-progress{height:3px;background:var(--accent)}.overlay-base html,.overlay-base body{background:transparent;overflow:hidden}.app-shell{min-height:100vh;padding:0 20px 20px;display:flex;flex-direction:column;gap:14px}.window-frame-row{display:flex;align-items:flex-end;gap:8px;min-height:54px;padding-top:10px;app-region:drag;-webkit-app-region:drag}.window-title-wrap{flex:1;padding:0 4px}.window-title{font-size:15px;letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted);font-weight:600}.window-subtitle{margin-top:2px;color:var(--text-subtle);font-size:var(--font-size-md)}.window-actions{display:flex;gap:6px;app-region:no-drag;-webkit-app-region:no-drag}.window-action-btn{width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center;font-size:18px}.icon-btn,.secondary-btn,.top-tab,.document-tab{border:1px solid var(--border);cursor:pointer;transition:border-color var(--duration-base),color var(--duration-base),background var(--duration-base),transform var(--duration-fast)}.icon-btn{background:var(--bg-input);border-radius:0;color:var(--text-muted);padding:6px 10px;font-size:16px;line-height:1;display:inline-flex;align-items:center;justify-content:center;gap:5px}.icon-btn svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}.icon-btn-sm{width:26px;height:26px;padding:0}.icon-btn-sm svg{width:13px;height:13px}.icon-btn-md{width:32px;height:32px;padding:0}.icon-btn-md svg{width:15px;height:15px}.icon-btn-lg{width:40px;height:40px;padding:0}.icon-btn-lg svg{width:18px;height:18px}.secondary-btn{background:var(--bg-input);border-radius:0;color:var(--text);padding:8px 12px;font-size:var(--font-size-md);font-family:var(--font-mono);display:inline-flex;align-items:center;gap:6px}.secondary-btn svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}.slim-btn{min-width:34px;padding:6px 10px}.help-btn{width:24px;height:24px;padding:0;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;border-radius:0}.modal-close-btn{font-size:20px;width:32px;height:32px;padding:0;display:inline-flex;align-items:center;justify-content:center}.icon-btn:hover,.secondary-btn:hover,.top-tab:hover,.document-tab:hover{border-color:var(--border-focus);color:var(--text-heading)}.icon-btn:active,.secondary-btn:active{transform:scale(.97)}.secondary-btn:disabled,.icon-btn:disabled{opacity:.45;cursor:not-allowed}.top-tabs{display:flex;gap:8px;flex-wrap:wrap;app-region:no-drag;-webkit-app-region:no-drag}.top-tab{background:linear-gradient(180deg,#ffffff08,#fff0),#0f1729e0;color:var(--text-muted);border-radius:0;padding:10px 16px;font-weight:600;font-size:var(--font-size-base)}.top-tab.is-active,.document-tab.is-active{background:linear-gradient(180deg,#6c63ff33,#6c63ff14),#0f1729eb;color:var(--text-heading);border-color:#6c63ff94}.tab-panel{display:none;min-height:0;flex:1}.tab-panel.is-active{display:block}.settings-section,.mini-panel{background:linear-gradient(180deg,#ffffff05,#fff0),var(--bg-section);border:1px solid var(--border);border-radius:0;box-shadow:var(--shadow-inset),var(--shadow-card)}.settings-section{padding:var(--space-7);display:flex;flex-direction:column;gap:var(--space-4)}.mini-panel{padding:var(--space-5);display:flex;flex-direction:column;gap:var(--space-4)}.settings-section h2,.mini-panel h3,.mini-panel h4{font-size:var(--font-size-base);font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em}.section-heading-row{display:flex;align-items:center;justify-content:space-between;gap:var(--space-4)}.toolbar-actions{display:flex;flex-wrap:wrap;gap:var(--space-3);align-items:center}.badge{font-size:var(--font-size-xs);font-weight:600;text-transform:uppercase;letter-spacing:.05em;padding:1px 6px;border-radius:0;display:inline-block}.badge.beta{background:#f59e0b26;color:var(--badge-beta);border:1px solid rgba(245,158,11,.3)}.badge.default{background:#6c63ff26;color:var(--goblin-purple-300);border:1px solid rgba(108,99,255,.3)}.badge.success{background:#4ade801f;color:var(--success);border:1px solid rgba(74,222,128,.28)}.badge.error{background:#f871711f;color:var(--error);border:1px solid rgba(248,113,113,.28)}.status-row{display:flex;align-items:center;justify-content:space-between;gap:var(--space-4)}.status-indicator{display:flex;align-items:center;gap:6px;font-size:var(--font-size-md)}.status-dot{width:8px;height:8px;border-radius:50%;display:inline-block;flex-shrink:0}.status-indicator.connected .status-dot{background:var(--success);box-shadow:0 0 6px var(--success)}.status-indicator.untested .status-dot{background:var(--warning);box-shadow:0 0 6px var(--warning)}.status-indicator.disconnected .status-dot{background:var(--text-muted)}.status-indicator.error .status-dot{background:var(--error);box-shadow:0 0 6px var(--error)}.hint{font-size:var(--font-size-md);color:var(--text-muted);line-height:1.45}kbd{background:var(--bg-input);border:1px solid var(--border);border-radius:0;padding:3px 8px;font-family:var(--font-mono);font-size:var(--font-size-md);color:var(--text-heading);min-width:28px;text-align:center}.field{display:flex;gap:8px;align-items:center}.field-block{display:flex;flex-direction:column;gap:6px;color:var(--text-muted);font-size:var(--font-size-md)}.field-block label{font-size:var(--font-size-md);color:var(--text-muted)}.inline-field{display:flex;align-items:center;gap:8px;padding-left:32px;font-size:13px;color:var(--text-muted)}.inline-field input[type=range]{flex:1;min-width:120px}.inline-field input[type=number]{width:70px;padding:4px 8px;font-size:13px}.language-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.language-field{display:flex;flex-direction:column;gap:6px}.language-field label{font-size:var(--font-size-md);color:var(--text-muted)}.unit{color:var(--text-muted);font-size:var(--font-size-md);white-space:nowrap}.radio-group{display:flex;flex-direction:column;gap:8px}.radio-label,.checkbox-label{display:flex;align-items:center;gap:8px;cursor:pointer;padding:6px 8px;border-radius:0;transition:background var(--duration-fast)}.radio-label:hover,.checkbox-label:hover{background:#6c63ff14}.app-toast{position:fixed;left:20px;bottom:20px;z-index:1200;max-width:min(360px,calc(100vw - 32px));padding:12px 14px;border-radius:0;border:1px solid rgba(74,222,128,.35);background:linear-gradient(180deg,#ffffff12,#ffffff03),#090d18f5;color:#d9ffe8;box-shadow:var(--shadow-toast);font-size:var(--font-size-md);line-height:1.45;opacity:0;transform:translateY(12px);pointer-events:none;transition:opacity var(--duration-base) var(--easing-standard),transform var(--duration-base) var(--easing-standard)}.app-toast.visible{opacity:1;transform:translateY(0)}.app-toast.success{border-color:#4ade8059;color:#d9ffe8}.app-toast.error{border-color:#f8717173;color:#ffd7d7}.app-toast.info{border-color:#60a5fa66;color:#dbeeff}body.modal-open{overflow:hidden}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#080c18c2;display:flex;align-items:center;justify-content:center;padding:20px;z-index:1000}.modal-backdrop[hidden]{display:none}.modal-card{width:min(420px,100%);border:1px solid var(--border);border-radius:0;background:linear-gradient(180deg,#ffffff08,#fff0),var(--bg-section);box-shadow:var(--shadow-modal);padding:14px;-webkit-user-select:text;user-select:text}.modal-header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.modal-header h3{font-size:var(--font-size-lg);color:var(--text-heading);font-weight:600}.modal-body-text{color:var(--text-muted);font-size:var(--font-size-base);line-height:1.5;margin:0 0 4px}.modal-list{margin:0;padding-left:20px;color:var(--text);display:flex;flex-direction:column;gap:6px;line-height:1.4;font-size:var(--font-size-base)}.modal-footer{display:flex;justify-content:flex-end;gap:8px;margin-top:16px;padding-top:12px;border-top:1px solid var(--border)}.modal-btn-accept{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:0;border:none;background:var(--accent);color:#fff;font-size:var(--font-size-sm);font-weight:500;cursor:pointer;transition:opacity .15s}.modal-btn-accept:hover{opacity:.88}.modal-btn-accept.danger{background:var(--error)}.document-tabs{display:flex;flex-wrap:wrap;gap:8px}.document-tab-wrap{display:inline-flex;align-items:center;gap:6px}.document-tab{border-radius:0;padding:9px 14px;background:#0c1222e0;color:var(--text-muted);display:inline-flex;align-items:center;gap:8px;font-size:var(--font-size-base)}.document-tab-dirty{width:8px;height:8px;border-radius:50%;background:var(--warning);box-shadow:0 0 8px #fbbf2485;flex-shrink:0}.document-tab-close{width:18px;height:18px;border:0;border-radius:0;background:#ffffff14;color:inherit;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:12px;transition:background var(--duration-fast)}.document-tab-close:hover{background:#ffffff29}.editor-workspace{display:grid;grid-template-columns:var(--left-panel-width, 220px) 8px minmax(0,1fr) 8px var(--right-panel-width, 260px);gap:12px;min-height:0;flex:1}.editor-workspace.left-collapsed{grid-template-columns:0 0 minmax(0,1fr) 8px var(--right-panel-width, 260px)}.editor-workspace.right-collapsed{grid-template-columns:var(--left-panel-width, 220px) 8px minmax(0,1fr) 0 0}.editor-workspace.left-collapsed.right-collapsed{grid-template-columns:0 0 minmax(0,1fr) 0 0}.editor-tools-pane,.editor-sidebar-pane{display:flex;flex-direction:column;gap:12px;min-width:0;overflow:hidden;transition:opacity var(--duration-base)}.editor-workspace.left-collapsed .editor-tools-pane,.editor-workspace.right-collapsed .editor-sidebar-pane{opacity:0;pointer-events:none}.pane-resizer{width:8px;border-radius:0;background:linear-gradient(180deg,#6c63ff3d,#6c63ff0f);border:1px solid rgba(108,99,255,.12);cursor:col-resize;transition:background var(--duration-base),border-color var(--duration-base)}.pane-resizer:hover{background:linear-gradient(180deg,#6c63ff66,#6c63ff26);border-color:#6c63ff4d}.editor-workspace.left-collapsed #left-pane-resizer,.editor-workspace.right-collapsed #right-pane-resizer{opacity:.35}.editor-canvas-pane{min-width:0;display:flex;flex-direction:column;gap:12px}.editor-status-bar{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 14px;border-radius:0;background:#0b1020b8;border:1px solid rgba(255,255,255,.05);font-size:var(--font-size-md);color:var(--text-muted)}.compact-list{padding-left:18px;color:var(--text);display:flex;flex-direction:column;gap:6px;font-size:var(--font-size-base)}.stack-actions{display:flex;flex-wrap:wrap;gap:8px}.stat-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.stat-card{padding:10px;border-radius:0;background:#0c1222cc;border:1px solid rgba(255,255,255,.04);display:flex;flex-direction:column;gap:4px}.stat-card span{font-size:var(--font-size-sm);color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em}.stat-card strong{font-size:var(--font-size-xl);color:var(--text-heading)}.empty-state{display:grid;place-items:center;min-height:180px}.empty-state[hidden]{display:none}.empty-state-card{width:min(420px,100%);text-align:center;padding:22px;border-radius:0;background:#0b1020b8;border:1px solid rgba(108,99,255,.22);box-shadow:0 18px 45px #00000047;display:flex;flex-direction:column;gap:12px}.empty-state-card .badge{align-self:center}.wave-panel{border:1px solid rgba(108,99,255,.25);border-radius:0;background:linear-gradient(180deg,#070b15f2,#0d121ff2);overflow:hidden;position:relative}.wave-canvas-wrap{width:100%;overflow:hidden;background:#0a0e1acc}.wave-canvas-wrap canvas{width:100%;display:block}.wave-controls{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:10px 12px}.wave-label{font-size:var(--font-size-sm);font-family:var(--font-mono);color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em}.overlay-pill{display:inline-flex;flex-direction:column;align-items:stretch;gap:6px;background:#141428eb;border:1px solid rgba(108,99,255,.4);border-radius:0;padding:8px 12px 10px;box-shadow:none;cursor:move;-webkit-user-select:none;user-select:none;app-region:drag;-webkit-app-region:drag}.overlay-main-row{display:flex;align-items:center;justify-content:center;gap:10px;min-width:170px}.recording-dot{width:10px;height:10px;border-radius:50%;background:var(--error);box-shadow:0 0 8px var(--error);animation:recording-pulse 1.2s ease-in-out infinite;flex-shrink:0}.recording-dot.loading{background:var(--warning);box-shadow:0 0 10px #fbbf24e6;animation-duration:1.5s}.recording-dot.listening{background:var(--error);box-shadow:0 0 8px var(--error)}.recording-dot.transcribing{background:var(--goblin-blue-400);box-shadow:0 0 10px #60a5fae6;animation-duration:1.1s}.recording-dot.correcting{background:var(--goblin-green-300);box-shadow:0 0 10px #34d399e6;animation-duration:1.4s}.recording-dot.done{background:var(--success);box-shadow:0 0 8px #4ade80d9;animation:none}@keyframes recording-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.85)}}.overlay-label{font-size:13px;font-weight:500;color:var(--text);white-space:nowrap}.overlay-timer{font-size:var(--font-size-md);font-family:var(--font-mono);color:var(--text-muted);width:40px;text-align:right}.overlay-wave-wrap{width:100%;height:28px;border-radius:0;background:transparent}.overlay-wave-wrap canvas{width:100%;height:100%;display:block}.overlay-hud{display:flex;justify-content:center;gap:10px;font-size:var(--font-size-xs);font-family:var(--font-mono);color:#bec3e1c7;letter-spacing:.02em;text-transform:uppercase}.overlay-transcript{font-size:var(--font-size-sm);color:#c8c8dc99;text-align:center;padding:0 16px;max-height:0;overflow:hidden;transition:max-height var(--duration-slow) var(--easing-standard);white-space:nowrap;text-overflow:ellipsis}.overlay-transcript.visible{max-height:24px}.demo-section{display:flex;flex-direction:column;gap:20px}.demo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px}.demo-row{display:flex;flex-wrap:wrap;align-items:center;gap:10px}.demo-swatch-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:8px}.demo-swatch{height:52px;border-radius:0;border:1px solid rgba(255,255,255,.06);display:flex;align-items:flex-end;padding:6px 8px}.demo-swatch span{font-size:var(--font-size-xs);font-family:var(--font-mono);color:#ffffffb3;text-shadow:0 1px 2px rgba(0,0,0,.6)}.demo-radius-strip{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end}.demo-radius-box{width:56px;height:56px;background:#6c63ff2e;border:1px solid rgba(108,99,255,.35);display:flex;align-items:center;justify-content:center}.demo-radius-box span{font-size:var(--font-size-xs);color:var(--text-muted);font-family:var(--font-mono)}.demo-type-scale{display:flex;flex-direction:column;gap:8px}.demo-type-row{display:flex;align-items:baseline;gap:14px}.demo-type-label{width:80px;font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);flex-shrink:0}.nav-bar{display:flex;align-items:center;gap:2px;position:relative;z-index:200}.nav-item{position:relative}.nav-trigger{display:flex;align-items:center;gap:5px;padding:6px 10px;border-radius:0;border:1px solid transparent;background:transparent;color:var(--text-muted);font-size:var(--font-size-base);font-family:var(--font);cursor:pointer;white-space:nowrap;transition:background var(--duration-fast),color var(--duration-fast),border-color var(--duration-fast)}.nav-trigger:hover,.nav-trigger.is-open{background:var(--bg-input);border-color:var(--border);color:var(--text-heading)}.nav-trigger svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;opacity:.55;transition:transform var(--duration-fast),opacity var(--duration-fast);flex-shrink:0}.nav-trigger.is-open svg{transform:rotate(180deg);opacity:1}.nav-dropdown{position:absolute;top:calc(100% + 4px);left:0;min-width:210px;background:linear-gradient(180deg,#ffffff06,#fff0),var(--bg-section);border:1px solid var(--border);border-radius:0;box-shadow:var(--shadow-modal);padding:4px;z-index:400;opacity:0;transform:translateY(-4px) scale(.98);pointer-events:none;transition:opacity var(--duration-fast),transform var(--duration-fast);transform-origin:top left}.nav-dropdown.is-open{opacity:1;transform:translateY(0) scale(1);pointer-events:all}.nav-option{display:flex;align-items:center;gap:8px;width:100%;padding:7px 10px;border:none;border-radius:0;background:transparent;color:var(--text);font-size:var(--font-size-base);font-family:var(--font);cursor:pointer;white-space:nowrap;text-align:left;transition:background var(--duration-fast),color var(--duration-fast);position:relative}.nav-option:hover,.nav-option--has-sub:hover,.nav-option--has-sub.is-sub-open{background:#6c63ff1a;color:var(--text-heading)}.nav-option--disabled{opacity:.35;cursor:not-allowed;pointer-events:none}.nav-option-icon{width:14px;height:14px;flex-shrink:0;opacity:.65;display:flex;align-items:center;justify-content:center}.nav-option-icon svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}.nav-option-label{flex:1}.nav-option-shortcut{font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-muted);padding-left:20px;flex-shrink:0}.nav-option-arrow{width:12px;height:12px;flex-shrink:0;display:flex;align-items:center;justify-content:center;opacity:.5;margin-left:auto}.nav-option-arrow svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.nav-divider{height:1px;background:var(--border);margin:4px 6px}.nav-option--has-sub{position:relative}.nav-submenu{position:absolute;top:-4px;left:calc(100% + 4px);min-width:190px;background:linear-gradient(180deg,#ffffff06,#fff0),var(--bg-section);border:1px solid var(--border);border-radius:0;box-shadow:var(--shadow-modal);padding:4px;z-index:500;opacity:0;transform:translate(-4px) scale(.98);pointer-events:none;transition:opacity var(--duration-fast),transform var(--duration-fast);transform-origin:top left}.nav-option--has-sub:hover>.nav-submenu,.nav-option--has-sub.is-sub-open>.nav-submenu{opacity:1;transform:translate(0) scale(1);pointer-events:all}.range-slider{--range-track-h: 3px;--range-thumb-size: 13px;padding:6px 0;user-select:none;-webkit-user-select:none}.range-track{position:relative;height:var(--range-track-h);background:var(--border);cursor:pointer}.range-fill,.range-fill-end{position:absolute;top:0;height:100%;background:var(--accent);pointer-events:none}.range-thumb{position:absolute;top:50%;transform:translate(-50%,-50%);width:var(--range-thumb-size);height:var(--range-thumb-size);background:var(--accent);border:2px solid var(--bg);cursor:grab;transition:background var(--duration-fast),box-shadow var(--duration-fast);touch-action:none}.range-thumb:hover{background:var(--accent-hover)}.range-thumb:focus-visible{outline:none;box-shadow:0 0 0 3px #6c63ff66}.range-thumb.is-dragging,.range-thumb:active{cursor:grabbing;background:var(--accent-hover);box-shadow:0 0 0 4px #6c63ff40}.range-labels{display:flex;justify-content:space-between;margin-top:8px;font-size:var(--font-size-xs, 11px);color:var(--text-muted);pointer-events:none}.search-field{position:relative;display:inline-flex;align-items:center;background:var(--bg-input);border:1px solid var(--border);transition:border-color var(--duration-fast)}.search-field:focus-within{border-color:var(--accent)}.search-field-icon{position:absolute;left:9px;display:flex;align-items:center;color:var(--text-muted);pointer-events:none}.search-field-icon svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2}.search-field input{flex:1;min-width:0;background:transparent;border:none;outline:none;padding:7px 10px 7px 32px;color:var(--text);font-size:var(--font-size-sm);font-family:var(--font);width:100%}.search-field input::placeholder{color:var(--text-muted)}.search-field input::-webkit-search-cancel-button{-webkit-appearance:none}.search-suggestions{display:none;flex-direction:column;position:absolute;top:calc(100% + 2px);left:0;right:0;background:linear-gradient(180deg,#ffffff06,#fff0),var(--bg-section);border:1px solid var(--border);box-shadow:var(--shadow-modal);z-index:400;max-height:240px;overflow-y:auto;padding:4px}.search-suggestions.is-open{display:flex}.search-suggestion{display:block;width:100%;background:transparent;border:none;padding:6px 10px;color:var(--text);font-size:var(--font-size-sm);font-family:var(--font);text-align:left;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:background var(--duration-fast),color var(--duration-fast)}.search-suggestion:hover,.search-suggestion.is-active{background:#6c63ff1a;color:var(--text-heading)}.scroll-panel{--scroll-panel-height: 360px;max-height:var(--scroll-panel-height);overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track)}.scroll-panel::-webkit-scrollbar{width:6px}.scroll-panel::-webkit-scrollbar-track{background:var(--scrollbar-track)}.scroll-panel::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);border:1px solid var(--scrollbar-track)}.scroll-panel::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover)}.scroll-panel-sm{--scroll-panel-height: 200px}.scroll-panel-md{--scroll-panel-height: 360px}.scroll-panel-lg{--scroll-panel-height: 520px}.scroll-panel-xl{--scroll-panel-height: 680px}.icon-gallery-cell{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;padding:8px 4px;border-radius:0;border:1px solid transparent;background:transparent;color:var(--text-muted);cursor:pointer;transition:border-color var(--duration-fast),background var(--duration-fast),color var(--duration-fast)}.icon-gallery-cell:hover{border-color:var(--border);background:var(--bg-input);color:var(--text-heading)}.icon-gallery-cell svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}.icon-gallery-cell span{font-size:9px;font-family:var(--font-mono);text-align:center;line-height:1.2;word-break:break-all;color:inherit}@media(max-width:1100px){.editor-workspace{grid-template-columns:1fr}.pane-resizer{display:none}.stat-grid{grid-template-columns:1fr}}@media(max-width:760px){.app-shell{padding:0 12px 12px}.window-frame-row{align-items:flex-start;flex-direction:column}.top-tabs{flex-wrap:wrap}.language-grid{grid-template-columns:1fr}.app-toast{left:12px;right:12px;bottom:12px;max-width:none}.demo-grid{grid-template-columns:1fr}}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const WAVEFORM_STYLES: readonly ["classic", "bars", "pulse", "bloom", "fan"];
|
|
2
|
+
export declare const WAVEFORM_COLOR_SCHEMES: readonly ["aurora", "ember", "glacier", "sunset", "monochrome"];
|
|
3
|
+
export type WaveformStyle = (typeof WAVEFORM_STYLES)[number];
|
|
4
|
+
export type WaveformColorScheme = (typeof WAVEFORM_COLOR_SCHEMES)[number];
|
|
5
|
+
export declare function isWaveformStyle(value: unknown): value is WaveformStyle;
|
|
6
|
+
export declare function isWaveformColorScheme(value: unknown): value is WaveformColorScheme;
|
|
7
|
+
export declare function cycleWaveformStyle(current: WaveformStyle): WaveformStyle;
|
|
8
|
+
export declare function cycleWaveformColorScheme(current: WaveformColorScheme): WaveformColorScheme;
|
|
9
|
+
export declare function getWaveformStyleLabel(style: WaveformStyle): string;
|
|
10
|
+
export declare function getWaveformColorSchemeLabel(scheme: WaveformColorScheme): string;
|
|
11
|
+
export interface DrawWaveformOptions {
|
|
12
|
+
ctx: CanvasRenderingContext2D;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
amplitude: number;
|
|
16
|
+
phase: number;
|
|
17
|
+
active: boolean;
|
|
18
|
+
colorScheme: WaveformColorScheme;
|
|
19
|
+
}
|
|
20
|
+
export declare function drawWaveform(style: WaveformStyle, options: DrawWaveformOptions): void;
|
|
21
|
+
export declare function createWaveProgressGradient(ctx: CanvasRenderingContext2D, height: number, colorScheme: WaveformColorScheme): CanvasGradient;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@goblin-systems/goblin-design-system",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./style.css": "./dist/style.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"skills"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"dev": "vite",
|
|
22
|
+
"build": "tsc && vite build",
|
|
23
|
+
"build:lib": "tsc --project tsconfig.lib.json --noEmit && vite build --config vite.lib.config.ts",
|
|
24
|
+
"lint": "tsc --noEmit",
|
|
25
|
+
"preview": "vite preview",
|
|
26
|
+
"tauri": "tauri"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"lucide": ">=0.400.0",
|
|
30
|
+
"@tauri-apps/api": ">=2.0.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependenciesMeta": {
|
|
33
|
+
"@tauri-apps/api": {
|
|
34
|
+
"optional": true
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@tauri-apps/api": "^2",
|
|
39
|
+
"@tauri-apps/cli": "^2",
|
|
40
|
+
"@tauri-apps/plugin-store": "^2",
|
|
41
|
+
"lucide": "^0.577.0",
|
|
42
|
+
"typescript": "~5.6.2",
|
|
43
|
+
"vite": "^6.0.3",
|
|
44
|
+
"vite-plugin-dts": "^4.5.4"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
You are helping build UI using the **Goblin Design System** (`@goblin-systems/goblin-design-system`).
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// main entry
|
|
7
|
+
import { applyIcons, showToast, bindTabs, /* etc */ } from "@goblin-systems/goblin-design-system";
|
|
8
|
+
// styles (import once in your app entry)
|
|
9
|
+
import "@goblin-systems/goblin-design-system/style.css";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
After injecting any HTML that contains `<i data-lucide="...">` icons, call `applyIcons()`.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Design constraints
|
|
17
|
+
|
|
18
|
+
- **Dark theme only** — all colours from CSS custom properties
|
|
19
|
+
- **No border-radius** — all corners are square by design
|
|
20
|
+
- **Vanilla TS/JS only** — no framework required; all behaviour is DOM-based
|
|
21
|
+
- **Headless pattern** — CSS classes handle appearance; TypeScript functions handle behaviour
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## CSS classes reference
|
|
26
|
+
|
|
27
|
+
### Buttons
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<button class="secondary-btn">Label</button>
|
|
31
|
+
<button class="secondary-btn" disabled>Disabled</button>
|
|
32
|
+
|
|
33
|
+
<!-- Icon buttons -->
|
|
34
|
+
<button class="icon-btn"><i data-lucide="search"></i></button>
|
|
35
|
+
<button class="icon-btn icon-btn-sm">…</button> <!-- 26px -->
|
|
36
|
+
<button class="icon-btn icon-btn-md">…</button> <!-- 32px -->
|
|
37
|
+
<button class="icon-btn icon-btn-lg">…</button> <!-- 40px -->
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Active/selected state on any button: add `is-active` class.
|
|
41
|
+
|
|
42
|
+
### Tabs
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<!-- Top-level tabs -->
|
|
46
|
+
<div data-tab-group="my-group">
|
|
47
|
+
<button class="top-tab is-active" data-tab-trigger="panel-a">Tab A</button>
|
|
48
|
+
<button class="top-tab" data-tab-trigger="panel-b">Tab B</button>
|
|
49
|
+
</div>
|
|
50
|
+
<div data-tab-panel="panel-a">…content…</div>
|
|
51
|
+
<div data-tab-panel="panel-b" hidden>…content…</div>
|
|
52
|
+
|
|
53
|
+
<!-- Document-style tabs -->
|
|
54
|
+
<button class="document-tab is-active" data-tab-trigger="doc-1">doc-1</button>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { bindTabs } from "@goblin-systems/goblin-design-system";
|
|
59
|
+
bindTabs({ root: document });
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Modal
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<!-- Static markup modal -->
|
|
66
|
+
<div id="my-modal" class="modal-backdrop" hidden>
|
|
67
|
+
<div class="modal-card">
|
|
68
|
+
<div class="modal-header">
|
|
69
|
+
<h3>Title</h3>
|
|
70
|
+
<button class="icon-btn modal-close-btn modal-btn-reject" aria-label="Close">
|
|
71
|
+
<i data-lucide="x"></i>
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
<p class="modal-body-text">Body text here.</p>
|
|
75
|
+
<div class="modal-footer">
|
|
76
|
+
<button class="secondary-btn modal-btn-reject">Cancel</button>
|
|
77
|
+
<button class="modal-btn-accept">Confirm</button>
|
|
78
|
+
<!-- danger variant: -->
|
|
79
|
+
<button class="modal-btn-accept danger">Delete</button>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { openModal, closeModal, confirmModal } from "@goblin-systems/goblin-design-system";
|
|
87
|
+
|
|
88
|
+
// Static modal with callbacks
|
|
89
|
+
openModal({
|
|
90
|
+
backdrop: document.getElementById("my-modal")!,
|
|
91
|
+
onAccept: () => console.log("accepted"),
|
|
92
|
+
onReject: () => console.log("rejected"),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Programmatic confirm — returns Promise<boolean>
|
|
96
|
+
const ok = await confirmModal({
|
|
97
|
+
title: "Delete item?",
|
|
98
|
+
message: "This cannot be undone.",
|
|
99
|
+
acceptLabel: "Delete",
|
|
100
|
+
variant: "danger", // "default" | "danger"
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Toast
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<!-- Place once in body -->
|
|
108
|
+
<div id="app-toast" class="app-toast" role="status" aria-live="polite"></div>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { showToast } from "@goblin-systems/goblin-design-system";
|
|
113
|
+
showToast("Saved.", "success"); // "success" | "error" | "info"
|
|
114
|
+
showToast("Oops.", "error", 4000); // custom duration ms
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Navigation (dropdown nav bar)
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<nav id="my-nav" class="nav-bar">
|
|
121
|
+
<div class="nav-item">
|
|
122
|
+
<button class="nav-trigger">File <i data-lucide="chevron-down"></i></button>
|
|
123
|
+
<div class="nav-dropdown">
|
|
124
|
+
<button class="nav-option" data-nav-id="new">New</button>
|
|
125
|
+
<div class="nav-divider"></div>
|
|
126
|
+
<!-- Submenu: use div, NOT button, for the parent -->
|
|
127
|
+
<div class="nav-option nav-option--has-sub">
|
|
128
|
+
<span class="nav-option-icon"><i data-lucide="upload"></i></span>
|
|
129
|
+
<span class="nav-option-label">Export As</span>
|
|
130
|
+
<span class="nav-option-arrow"><i data-lucide="chevron-right"></i></span>
|
|
131
|
+
<div class="nav-submenu">
|
|
132
|
+
<button class="nav-option" data-nav-id="export-png">PNG</button>
|
|
133
|
+
<button class="nav-option" data-nav-id="export-svg">SVG</button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
<!-- Disabled option -->
|
|
137
|
+
<button class="nav-option nav-option--disabled" data-nav-id="save">Save</button>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</nav>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Important:** `.nav-option--has-sub` must be a `<div>`, not a `<button>` — browsers disallow nested buttons, which would break submenu rendering.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { bindNavigation } from "@goblin-systems/goblin-design-system";
|
|
147
|
+
const nav = bindNavigation({
|
|
148
|
+
root: document.getElementById("my-nav") ?? document,
|
|
149
|
+
onSelect: (id) => console.log("selected:", id),
|
|
150
|
+
});
|
|
151
|
+
// nav.closeAll(), nav.openItem(el), nav.closeItem(el)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Search field
|
|
155
|
+
|
|
156
|
+
```html
|
|
157
|
+
<div class="search-field" style="width:280px">
|
|
158
|
+
<span class="search-field-icon"><i data-lucide="search"></i></span>
|
|
159
|
+
<input id="my-search" type="text" placeholder="Search…" />
|
|
160
|
+
<div class="search-suggestions"></div> <!-- omit if no autocomplete needed -->
|
|
161
|
+
</div>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { bindSearch } from "@goblin-systems/goblin-design-system";
|
|
166
|
+
const search = bindSearch({
|
|
167
|
+
input: document.getElementById("my-search") as HTMLInputElement,
|
|
168
|
+
debounce: 200,
|
|
169
|
+
onSearch: (query) => {
|
|
170
|
+
const results = myData.filter(x => x.includes(query));
|
|
171
|
+
search.setSuggestions(results); // populates the dropdown
|
|
172
|
+
},
|
|
173
|
+
onSelect: (value) => console.log("picked:", value),
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Range slider (double handle)
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<div id="my-range" class="range-slider">
|
|
181
|
+
<div class="range-track">
|
|
182
|
+
<div class="range-fill"></div>
|
|
183
|
+
<!-- For inverted range, also add: -->
|
|
184
|
+
<!-- <div class="range-fill-end"></div> -->
|
|
185
|
+
<div class="range-thumb" data-thumb="lo"></div>
|
|
186
|
+
<div class="range-thumb" data-thumb="hi"></div>
|
|
187
|
+
</div>
|
|
188
|
+
<div class="range-labels"> <!-- optional -->
|
|
189
|
+
<span class="range-label-lo"></span>
|
|
190
|
+
<span class="range-label-hi"></span>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { bindRange } from "@goblin-systems/goblin-design-system";
|
|
197
|
+
const range = bindRange({
|
|
198
|
+
el: document.getElementById("my-range")!,
|
|
199
|
+
min: 0, max: 100, step: 1,
|
|
200
|
+
value: [20, 80],
|
|
201
|
+
inverted: false, // true = fill outside the handles (exclusion zone)
|
|
202
|
+
onChange: (lo, hi) => console.log(lo, hi),
|
|
203
|
+
});
|
|
204
|
+
range.setValue(30, 70);
|
|
205
|
+
range.getValue(); // [30, 70]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Single range
|
|
209
|
+
|
|
210
|
+
Native `<input type="range">` is fully styled. To animate the track fill, update a CSS variable:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
function syncFill(input: HTMLInputElement) {
|
|
214
|
+
const pct = ((+input.value - +input.min) / (+input.max - +input.min)) * 100;
|
|
215
|
+
input.style.setProperty("--_pct", `${pct}%`);
|
|
216
|
+
}
|
|
217
|
+
const input = document.querySelector("input[type=range]") as HTMLInputElement;
|
|
218
|
+
syncFill(input);
|
|
219
|
+
input.addEventListener("input", () => syncFill(input));
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Split pane
|
|
223
|
+
|
|
224
|
+
```html
|
|
225
|
+
<div id="workspace" class="split-workspace">
|
|
226
|
+
<div class="split-pane split-pane-left">…</div>
|
|
227
|
+
<div id="left-resizer" class="split-resizer"></div>
|
|
228
|
+
<div class="split-pane split-pane-main">…</div>
|
|
229
|
+
<div id="right-resizer" class="split-resizer"></div>
|
|
230
|
+
<div class="split-pane split-pane-right">…</div>
|
|
231
|
+
</div>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
import { bindSplitPaneResize } from "@goblin-systems/goblin-design-system";
|
|
236
|
+
bindSplitPaneResize({
|
|
237
|
+
workspace: document.getElementById("workspace")!,
|
|
238
|
+
leftResizer: document.getElementById("left-resizer")!,
|
|
239
|
+
rightResizer: document.getElementById("right-resizer")!,
|
|
240
|
+
});
|
|
241
|
+
// Collapse panels by toggling classes:
|
|
242
|
+
workspace.classList.toggle("left-collapsed");
|
|
243
|
+
workspace.classList.toggle("right-collapsed");
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Scroll panel
|
|
247
|
+
|
|
248
|
+
CSS-only. Constrains height and applies custom scrollbar:
|
|
249
|
+
|
|
250
|
+
```html
|
|
251
|
+
<div class="scroll-panel scroll-panel-lg">
|
|
252
|
+
<!-- any content -->
|
|
253
|
+
</div>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Size modifiers: `scroll-panel-sm` (200px) · `scroll-panel-md` (360px) · `scroll-panel-lg` (520px) · `scroll-panel-xl` (680px)
|
|
257
|
+
|
|
258
|
+
Custom height: `style="--scroll-panel-height: 400px"`
|
|
259
|
+
|
|
260
|
+
### Status indicator
|
|
261
|
+
|
|
262
|
+
```html
|
|
263
|
+
<span class="status-indicator connected">
|
|
264
|
+
<span></span>
|
|
265
|
+
<span>Connected</span>
|
|
266
|
+
</span>
|
|
267
|
+
<!-- States: connected | untested | disconnected | error -->
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Badges
|
|
271
|
+
|
|
272
|
+
```html
|
|
273
|
+
<span class="badge default">Stable</span>
|
|
274
|
+
<span class="badge beta">Beta</span>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Form elements
|
|
278
|
+
|
|
279
|
+
All styled automatically from `base.css`:
|
|
280
|
+
|
|
281
|
+
```html
|
|
282
|
+
<input type="text" placeholder="…" />
|
|
283
|
+
<input type="password" />
|
|
284
|
+
<input type="number" />
|
|
285
|
+
<select><option>…</option></select>
|
|
286
|
+
<input type="checkbox" />
|
|
287
|
+
<input type="radio" name="g" />
|
|
288
|
+
<input type="range" min="0" max="100" value="50" />
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Group label + input:
|
|
292
|
+
|
|
293
|
+
```html
|
|
294
|
+
<div class="field-block">
|
|
295
|
+
<label>Field label</label>
|
|
296
|
+
<input type="text" />
|
|
297
|
+
</div>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Layout helpers
|
|
301
|
+
|
|
302
|
+
```html
|
|
303
|
+
<div class="settings-section"> <!-- padded card section -->
|
|
304
|
+
<h2>Section title</h2>
|
|
305
|
+
…
|
|
306
|
+
</div>
|
|
307
|
+
<div class="demo-row"> <!-- flex row with gap -->…</div>
|
|
308
|
+
<div class="demo-grid"> <!-- 2-col responsive grid -->…</div>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Icons
|
|
314
|
+
|
|
315
|
+
1,900+ Lucide icons available. Use `data-lucide` attribute, then call `applyIcons()`:
|
|
316
|
+
|
|
317
|
+
```html
|
|
318
|
+
<i data-lucide="search"></i>
|
|
319
|
+
<i data-lucide="chevron-right"></i>
|
|
320
|
+
<i data-lucide="trash-2"></i>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
import { applyIcons, createIcon } from "@goblin-systems/goblin-design-system";
|
|
325
|
+
applyIcons(); // replace all <i data-lucide> in DOM
|
|
326
|
+
const svg = createIcon("search"); // create single SVGElement programmatically
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Tauri window controls (optional)
|
|
332
|
+
|
|
333
|
+
Only needed in Tauri apps:
|
|
334
|
+
|
|
335
|
+
```html
|
|
336
|
+
<button id="window-minimize-btn" class="icon-btn"><i data-lucide="minus"></i></button>
|
|
337
|
+
<button id="window-maximize-btn" class="icon-btn"><i data-lucide="maximize-2"></i></button>
|
|
338
|
+
<button id="window-close-btn" class="icon-btn"><i data-lucide="x"></i></button>
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
import { setupWindowControls, setupContextMenuGuard } from "@goblin-systems/goblin-design-system";
|
|
343
|
+
setupWindowControls(); // wires the three buttons above
|
|
344
|
+
setupContextMenuGuard(); // disables right-click context menu
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Design tokens
|
|
350
|
+
|
|
351
|
+
Override in your own `:root` after importing the stylesheet.
|
|
352
|
+
|
|
353
|
+
```css
|
|
354
|
+
:root {
|
|
355
|
+
/* Surfaces */
|
|
356
|
+
--bg: #0d1324; --bg-section: #16213e;
|
|
357
|
+
--bg-input: #0f1729; --bg-deep: #0b1020;
|
|
358
|
+
/* Borders */
|
|
359
|
+
--border: #2a2a4a; --border-focus: #6c63ff;
|
|
360
|
+
/* Text */
|
|
361
|
+
--text: #e0e0e0; --text-muted: #8888aa;
|
|
362
|
+
--text-heading: #fff; --text-subtle: #b8b8d4;
|
|
363
|
+
/* Semantic */
|
|
364
|
+
--accent: #6c63ff; --accent-hover: #7c73ff;
|
|
365
|
+
--success: #4ade80; --warning: #fbbf24; --error: #f87171;
|
|
366
|
+
/* Typography */
|
|
367
|
+
--font: "Segoe UI", -apple-system, sans-serif;
|
|
368
|
+
--font-mono: "Cascadia Code", "Fira Code", monospace;
|
|
369
|
+
/* Spacing: --space-1 (4px) … --space-10 (32px) */
|
|
370
|
+
/* Animation: --duration-fast / --duration-base / --duration-slow */
|
|
371
|
+
}
|
|
372
|
+
```
|