@dstackai/sqircle 0.1.0 → 0.1.1
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 +154 -16
- package/dist/SquircleEditor.d.ts +5 -2
- package/dist/assets/ConstructorPage-BZYA63EF.js +2 -0
- package/dist/assets/ConstructorPage-BthtTZhb.css +1 -0
- package/dist/assets/PageShell-CjHSnST7.js +1 -0
- package/dist/assets/demo-C2Ri3G4a.js +1 -0
- package/dist/assets/index-CYKmiCSm.js +1 -0
- package/dist/assets/pages-DQf07DWd.js +9 -0
- package/dist/assets/pages-hENaA-mA.css +1 -0
- package/dist/codeExport.d.ts +4 -2
- package/dist/constructor.html +15 -0
- package/dist/demo.html +15 -0
- package/dist/index.html +15 -0
- package/dist/react.html +15 -0
- package/dist/sqircle.js +763 -310
- package/dist/sqircle.js.map +1 -1
- package/dist/static/styles.css +883 -0
- package/dist/style.css +1 -1
- package/docs/README.md +12 -7
- package/docs/design/colors.md +12 -12
- package/docs/design/geometry.md +14 -13
- package/docs/design/rendering.md +18 -19
- package/docs/design/single-squircle-states.md +47 -109
- package/docs/examples/README.md +33 -0
- package/docs/react/README.md +101 -62
- package/docs/react/editor.md +159 -0
- package/package.json +3 -2
package/dist/style.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.squircle-scene{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;background:0 0;width:100%;height:auto;display:block;overflow:visible}.squircle-scene.sq-theme-dark{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}.sq-layer{cursor:pointer}.sq-layer.is-selected{outline:none}.sq-variant{transition:opacity var(--sq-transition-ms,.22s) ease}.sq-hover{opacity:0;pointer-events:none}.sq-layer.sq-has-hover:hover>.sq-base{opacity:0}.sq-layer.sq-has-hover:hover>.sq-hover{opacity:1}.sq-face{pointer-events:all;shape-rendering:geometricprecision;stroke-linejoin:round}.sq-hidden{fill:none;pointer-events:none;stroke-dasharray:8 8;stroke-linecap:round;stroke-linejoin:round}.sq-inlay{fill:none;pointer-events:none;shape-rendering:geometricprecision;stroke-dasharray:9 8;stroke-linejoin:round}.sq-label{clip-rule:evenodd;fill-rule:evenodd;paint-order:stroke;pointer-events:none;shape-rendering:geometricprecision;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;transition:fill .25s,stroke .25s,stroke-width .25s,opacity .25s}:root{color:#111821;background:0 0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}body{background:0 0;margin:0}button,input,select{font:inherit}.squircle-editor{--editor-text:#111821;--editor-muted:#697484;--editor-soft:#f6f9fc;--editor-panel:#ffffffdb;--editor-panel-soft:#ffffffb8;--editor-panel-muted:#ffffff75;--editor-line:#dfe7f1;--editor-line-strong:#dbe5ef;--editor-control-bg:#fff;--editor-control-text:#17202b;--editor-active-text:#073c68;--editor-accent:#09f;--editor-accent-soft:#0099ff24;--editor-danger:#9c2340;--editor-code-bg:#ffffffb8;--editor-code-text:#263140;--editor-stage-sheen:#b8e7ff2e;color:var(--editor-text);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;grid-template-rows:auto 1fr;width:min(100%,1320px);min-height:100vh;margin:0 auto;padding:clamp(14px,3vw,32px);display:grid}.squircle-editor-dark{--editor-text:#f4f8ff;--editor-muted:#aab7c8;--editor-soft:#101925;--editor-panel:#0d141ff0;--editor-panel-soft:#151f2ddb;--editor-panel-muted:#0c131db8;--editor-line:#25364a;--editor-line-strong:#33485f;--editor-control-bg:#121d2b;--editor-control-text:#eef6ff;--editor-active-text:#e5f6ff;--editor-accent:#42b4ff;--editor-accent-soft:#42b4ff2b;--editor-danger:#ff8ca3;--editor-code-bg:#090e16d6;--editor-code-text:#d8e8ff;--editor-stage-sheen:#42b4ff17;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}.squircle-editor-topbar{justify-content:space-between;align-items:end;gap:18px;padding-bottom:14px;display:flex}.squircle-editor-topbar h1{letter-spacing:0;margin:0;font-size:clamp(28px,5vw,48px)}.squircle-editor-topbar p{color:var(--editor-muted);margin:4px 0 0;font-size:14px;font-weight:650}.topbar-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;display:flex}.squircle-editor button{border:1px solid var(--editor-line-strong);background:var(--editor-control-bg);min-height:34px;color:var(--editor-control-text);cursor:pointer;border-radius:6px;font-weight:800}.squircle-editor button:hover{border-color:color-mix(in srgb, var(--editor-accent) 52%, transparent);color:var(--editor-active-text)}.theme-switch{border:1px solid var(--editor-line);background:var(--editor-panel-soft);border-radius:8px;gap:2px;padding:3px;display:inline-flex}.theme-switch button{min-width:68px;min-height:28px;color:var(--editor-muted);background:0 0;border-color:#0000}.theme-switch button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 48%, var(--editor-line));color:var(--editor-active-text);background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 92%, var(--editor-accent)), var(--editor-panel-soft));box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 16%, transparent), 0 1px 5px #0f141a1f}.squircle-editor-workspace{border:1px solid var(--editor-line);background:linear-gradient(145deg, var(--editor-stage-sheen), transparent 48%), var(--editor-panel);border-radius:8px;grid-template-columns:minmax(220px,270px) minmax(420px,1fr) minmax(280px,350px);min-height:0;display:grid;overflow:hidden}.side-panel{min-width:0;padding:14px;overflow:auto}.side-panel:first-child{border-right:1px solid var(--editor-line)}.inspector-panel{border-left:1px solid var(--editor-line)}.panel-heading{justify-content:space-between;align-items:center;gap:10px;margin-bottom:12px;display:flex}.panel-heading h2{margin:0;font-size:15px}.panel-heading span{color:var(--editor-muted);font-size:12px;font-weight:850}.squircle-editor-layer-list{gap:8px;display:grid}.squircle-editor-layer-row{grid-template-columns:1fr auto;align-items:stretch;gap:6px;display:grid}.layer-select{text-align:left;justify-items:start;min-width:0;padding:10px;display:grid}.layer-select strong{font-size:13px}.layer-select span{color:var(--editor-muted);font-size:11px;font-weight:760}.layer-select.is-active{border-color:color-mix(in srgb, var(--editor-accent) 68%, var(--editor-line));background:linear-gradient(90deg, var(--editor-accent-soft), var(--editor-panel-soft) 48%), var(--editor-control-bg);box-shadow:0 0 0 2px color-mix(in srgb, var(--editor-accent) 12%, transparent);color:var(--editor-active-text);border-left-width:4px}.icon-text{color:var(--editor-muted);padding:0 10px;font-size:12px}.icon-text.danger{color:var(--editor-danger)}.squircle-editor-preview{grid-template-rows:minmax(260px,1fr) minmax(120px,220px);gap:12px;min-width:0;min-height:0;padding:18px;display:grid}.squircle-editor-preview .squircle-scene{place-self:center;max-width:620px}.code-panel{border:1px solid var(--editor-line);background:var(--editor-code-bg);border-radius:6px;grid-template-rows:auto 1fr;min-width:0;display:grid;overflow:hidden}.code-panel-header{border-bottom:1px solid var(--editor-line);justify-content:space-between;align-items:center;gap:12px;padding:9px 10px;display:flex}.code-panel-header h2{color:var(--editor-control-text);letter-spacing:0;margin:0;font-size:13px}.code-panel-header p{color:var(--editor-muted);margin:1px 0 0;font-size:11px;font-weight:760}.copy-code-button{place-items:center;width:34px;min-width:34px;padding:0;display:inline-grid;position:relative}.copy-code-button .copy-icon{fill:none;stroke:currentColor;stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:round;width:17px;height:17px}.copy-status{border:1px solid var(--editor-line);background:var(--editor-control-bg);color:var(--editor-active-text);opacity:0;pointer-events:none;border-radius:5px;padding:3px 6px;font-size:11px;font-weight:850;transition:opacity .16s,transform .16s;position:absolute;bottom:calc(100% + 5px);right:0;transform:translateY(2px)}.copy-status:not(:empty){opacity:1;transform:translateY(0)}.code-output{min-width:0;color:var(--editor-code-text);margin:0;padding:12px;font-size:11px;line-height:1.45;overflow:auto}.code-output code{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace}.editor-section{border-top:1px solid var(--editor-line);gap:10px;padding:12px 0;display:grid}.editor-section:first-of-type{border-top:0;padding-top:0}.editor-section h3{color:var(--editor-muted);letter-spacing:0;text-transform:uppercase;margin:0;font-size:12px}.field{color:var(--editor-muted);gap:5px;font-size:12px;font-weight:800;display:grid}.field select,.field input[type=text],.field input[type=range]{width:100%}.field select,.field input[type=text]{border:1px solid var(--editor-line-strong);background:var(--editor-control-bg);min-height:34px;color:var(--editor-control-text);border-radius:6px;padding:0 8px}.toggle-field{color:var(--editor-control-text);align-items:center;gap:8px;font-size:13px;font-weight:800;display:flex}.toggle-field input{width:16px;height:16px}.empty-note{color:var(--editor-muted);margin:0;font-size:13px;font-weight:700}@media (width<=1060px){.squircle-editor-workspace{grid-template-columns:1fr}.side-panel:first-child,.inspector-panel{border:0;border-bottom:1px solid var(--editor-line)}}@media (width<=700px){.squircle-editor-topbar{flex-direction:column;align-items:stretch}.topbar-actions{justify-content:stretch}.topbar-actions button{flex:1}.squircle-editor-preview{padding:10px}}
|
|
1
|
+
.squircle-scene{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;background:0 0;width:100%;height:auto;display:block;overflow:visible}.squircle-scene.sq-theme-dark{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}.sq-layer{cursor:pointer}.sq-layer.is-selected{outline:none}.sq-variant{transition:opacity var(--sq-transition-ms,.22s) ease}.sq-hover{opacity:0;pointer-events:none}.sq-layer.sq-has-hover:hover>.sq-base{opacity:0}.sq-layer.sq-has-hover:hover>.sq-hover{opacity:1}.sq-face{pointer-events:all;shape-rendering:geometricprecision;stroke-linejoin:round}.sq-hidden{fill:none;pointer-events:none;stroke-dasharray:8 8;stroke-linecap:round;stroke-linejoin:round}.sq-inlay{fill:none;pointer-events:none;shape-rendering:geometricprecision;stroke-dasharray:9 8;stroke-linejoin:round}.sq-label{clip-rule:evenodd;fill-rule:evenodd;paint-order:stroke;pointer-events:none;shape-rendering:geometricprecision;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;transition:fill .25s,stroke .25s,stroke-width .25s,opacity .25s}:root{color:#111821;background:0 0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}body{background:0 0;margin:0}button,input,select{font:inherit}.squircle-editor{--editor-text:#111821;--editor-muted:#697484;--editor-faint:#8b98aa;--editor-soft:#f6f9fc;--editor-panel:#ffffffdb;--editor-panel-soft:#ffffffb8;--editor-panel-muted:#ffffff75;--editor-line:#dfe7f1;--editor-line-strong:#dbe5ef;--editor-control-bg:#fff;--editor-control-text:#17202b;--editor-active-text:#073c68;--editor-accent:#09f;--editor-accent-2:#7c58f7;--editor-accent-soft:#0099ff24;--editor-danger:#9c2340;--editor-code-bg:#ffffffb8;--editor-code-text:#263140;--editor-stage-sheen:#b8e7ff2e;color:var(--editor-text);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;grid-template-rows:auto minmax(0,1fr);width:min(100%,1320px);min-height:100vh;margin:0 auto;padding:clamp(14px,3vw,32px);display:grid}.squircle-editor-dark{--editor-text:#f4f8ff;--editor-muted:#aab7c8;--editor-faint:#7f8da2;--editor-soft:#101925;--editor-panel:#0d141ff0;--editor-panel-soft:#151f2ddb;--editor-panel-muted:#0c131db8;--editor-line:#25364a;--editor-line-strong:#33485f;--editor-control-bg:#121d2b;--editor-control-text:#eef6ff;--editor-active-text:#e5f6ff;--editor-accent:#42b4ff;--editor-accent-2:#a88cff;--editor-accent-soft:#42b4ff2b;--editor-danger:#ff8ca3;--editor-code-bg:#090e16d6;--editor-code-text:#d8e8ff;--editor-stage-sheen:#42b4ff17;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}.squircle-editor-topbar{justify-content:space-between;align-items:end;gap:18px;padding-bottom:14px;display:flex}.squircle-editor-topbar h1{letter-spacing:0;margin:0;font-size:clamp(28px,5vw,48px)}.squircle-editor-topbar p{color:var(--editor-muted);margin:4px 0 0;font-size:14px;font-weight:650}.topbar-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;display:flex}.squircle-editor button{border:1px solid var(--editor-line-strong);background:var(--editor-control-bg);min-height:34px;color:var(--editor-control-text);cursor:pointer;border-radius:6px;font-weight:800}.squircle-editor button:hover{border-color:color-mix(in srgb, var(--editor-accent) 52%, transparent);color:var(--editor-active-text)}.squircle-editor button svg{fill:none;stroke:currentColor;stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:round;width:17px;height:17px}.icon-button{place-items:center;width:34px;min-width:34px;padding:0;display:inline-grid}.primary-action{border-color:color-mix(in srgb, var(--editor-accent) 62%, var(--editor-line-strong));background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 90%, var(--editor-accent)), var(--editor-control-bg));color:var(--editor-active-text);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 16%, transparent)}.danger{color:var(--editor-danger)}.theme-switch{border:1px solid var(--editor-line);background:var(--editor-panel-soft);border-radius:8px;gap:2px;padding:3px;display:inline-flex}.theme-switch button{min-width:68px;min-height:28px;color:var(--editor-muted);background:0 0;border-color:#0000}.theme-switch button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 48%, var(--editor-line));color:var(--editor-active-text);background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 92%, var(--editor-accent)), var(--editor-panel-soft));box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 16%, transparent), 0 1px 5px #0f141a1f}.squircle-editor-workspace{border:1px solid var(--editor-line);background:linear-gradient(145deg, var(--editor-stage-sheen), transparent 48%), var(--editor-panel);border-radius:8px;grid-template-columns:minmax(250px,300px) minmax(520px,1fr) minmax(300px,380px);min-height:0;display:grid;overflow:hidden}@media (width>=1061px){.squircle-editor{height:100vh;overflow:hidden}}.side-panel{min-width:0;padding:14px;overflow:auto}.layers-panel{align-content:start;gap:14px;display:grid}.side-panel:first-child{border-right:1px solid var(--editor-line)}.inspector-panel{border-left:1px solid var(--editor-line)}.panel-heading{justify-content:space-between;align-items:center;gap:10px;display:flex}.panel-heading h2{margin:0;font-size:15px}.panel-heading span{color:var(--editor-muted);margin-top:2px;font-size:12px;font-weight:850;display:block}.panel-actions{justify-content:flex-end;gap:6px;display:flex}.squircle-editor-layer-list{gap:9px;display:grid}.squircle-editor-layer-row{background:0 0;border:1px solid #0000;border-radius:8px;grid-template-columns:minmax(0,1fr) auto;align-items:stretch;gap:7px;padding:3px;transition:border-color .17s,background .17s,box-shadow .17s;display:grid}.layer-select{text-align:left;background:0 0;border-color:#0000;justify-items:start;gap:8px;min-width:0;padding:10px;display:grid}.layer-select strong{font-size:13px}.layer-card-topline,.layer-card-meta{align-items:center;width:100%;min-width:0;display:flex}.layer-card-topline{gap:7px}.layer-card-meta{justify-content:space-between;gap:8px}.layer-number{background:var(--editor-panel-soft);width:25px;min-width:25px;height:22px;color:var(--editor-faint);border-radius:5px;justify-content:center;align-items:center;font-size:11px;font-weight:900;display:inline-flex}.material-pill{border:1px solid var(--editor-line);color:var(--editor-muted);text-transform:uppercase;border-radius:999px;margin-left:auto;padding:3px 6px;font-size:10px;font-weight:900}.material-pill-solid{border-color:color-mix(in srgb, var(--editor-accent) 40%, var(--editor-line));color:var(--editor-active-text)}.material-pill-transparent{border-color:color-mix(in srgb, var(--editor-accent-2) 32%, var(--editor-line));color:color-mix(in srgb, var(--editor-active-text) 72%, var(--editor-accent-2))}.layer-palette-chip,.layer-feature-tags{min-width:0;color:var(--editor-muted);align-items:center;font-size:11px;font-weight:820;display:inline-flex}.layer-palette-chip{gap:6px}.layer-feature-tags{flex-wrap:wrap;justify-content:flex-end;gap:4px}.layer-feature-tags span{border:1px solid var(--editor-line);background:var(--editor-panel-muted);border-radius:999px;padding:2px 5px;font-size:10px}.layer-select span{color:var(--editor-muted)}.squircle-editor-layer-row.is-active{border-color:color-mix(in srgb, var(--editor-accent) 68%, var(--editor-line));background:linear-gradient(90deg, var(--editor-accent-soft), transparent 65%), var(--editor-panel-soft);box-shadow:0 0 0 2px color-mix(in srgb, var(--editor-accent) 13%, transparent), inset 4px 0 0 color-mix(in srgb, var(--editor-accent) 76%, var(--editor-accent-2));color:var(--editor-active-text)}.squircle-editor-layer-row.is-active .layer-number{background:color-mix(in srgb, var(--editor-accent) 18%, var(--editor-control-bg));color:var(--editor-active-text)}.layer-visibility{min-height:100%;color:var(--editor-muted);align-self:stretch}.layer-visibility[aria-pressed=false]{color:var(--editor-faint);opacity:.66}.squircle-editor-preview{grid-template-rows:auto minmax(320px,1fr) auto;gap:12px;min-width:0;min-height:0;padding:18px;display:grid}.preview-toolbar,.preview-actions{justify-content:space-between;align-items:center;gap:10px;display:flex}.preview-toolbar h2{margin:0;font-size:14px}.preview-toolbar span{color:var(--editor-muted);margin-top:2px;font-size:12px;font-weight:820;display:block}.preview-actions{justify-content:flex-end}.code-toggle-button{align-items:center;gap:7px;padding:0 10px;display:inline-flex}.code-toggle-button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 56%, var(--editor-line));background:var(--editor-accent-soft);color:var(--editor-active-text)}.preview-stage{border:1px solid color-mix(in srgb, var(--editor-line) 74%, transparent);background:linear-gradient(180deg, color-mix(in srgb, var(--editor-panel-soft) 72%, transparent), transparent 56%), color-mix(in srgb, var(--editor-panel-muted) 84%, transparent);border-radius:8px;place-items:center;min-height:0;display:grid;overflow:hidden}.preview-stage .squircle-scene{place-self:center;max-width:660px;padding:16px}.code-panel{border:1px solid var(--editor-line);background:var(--editor-code-bg);border-radius:6px;grid-template-rows:auto;min-width:0;display:grid;overflow:hidden}.code-panel.is-open{grid-template-rows:auto minmax(120px,220px)}.code-panel-header{border-bottom:1px solid var(--editor-line);justify-content:space-between;align-items:center;gap:12px;padding:9px 10px;display:flex}.code-panel-header h2{color:var(--editor-control-text);letter-spacing:0;margin:0;font-size:13px}.code-panel-header p{color:var(--editor-muted);margin:1px 0 0;font-size:11px;font-weight:760}.copy-code-button{place-items:center;width:34px;min-width:34px;padding:0;display:inline-grid;position:relative}.copy-status{border:1px solid var(--editor-line);background:var(--editor-control-bg);color:var(--editor-active-text);opacity:0;pointer-events:none;border-radius:5px;padding:3px 6px;font-size:11px;font-weight:850;transition:opacity .16s,transform .16s;position:absolute;bottom:calc(100% + 5px);right:0;transform:translateY(2px)}.copy-status:not(:empty){opacity:1;transform:translateY(0)}.code-output{min-width:0;color:var(--editor-code-text);margin:0;padding:12px;font-size:11px;line-height:1.45;overflow:auto}.code-output code{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace}.editor-section{border-top:1px solid var(--editor-line);gap:10px;padding:12px 0;display:grid}.editor-section:first-of-type{border-top:0;padding-top:0}.editor-section h3{color:var(--editor-muted);letter-spacing:0;text-transform:uppercase;margin:0;font-size:12px}.inspector-heading{border-bottom:1px solid var(--editor-line);justify-content:space-between;align-items:start;gap:12px;padding-bottom:12px;display:flex}.inspector-heading h2{margin:1px 0 0;font-size:18px}.inspector-heading p{color:var(--editor-muted);margin:4px 0 0;font-size:12px;font-weight:760}.inspector-kicker{color:var(--editor-faint);text-transform:uppercase;font-size:11px;font-weight:900}.state-switch{border:1px solid var(--editor-line);background:var(--editor-panel-soft);border-radius:8px;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px;margin:12px 0 2px;padding:4px;display:grid}.state-switch button{min-height:34px;color:var(--editor-muted);background:0 0;border-color:#0000;position:relative}.state-switch button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 58%, var(--editor-line));background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 90%, var(--editor-accent)), var(--editor-control-bg));color:var(--editor-active-text);box-shadow:0 1px 5px #0f141a1a}.state-dot{background:var(--editor-accent);border-radius:50%;width:6px;height:6px;position:absolute;top:7px;right:9px}.editor-section-details{gap:0}.editor-section-details summary{cursor:pointer;justify-content:space-between;align-items:center;list-style:none;display:flex}.editor-section-details summary::-webkit-details-marker{display:none}.editor-section-details summary:after{content:"+";color:var(--editor-muted);font-size:16px;font-weight:850}.editor-section-details[open] summary:after{content:"-"}.editor-section-body{gap:10px;padding-top:10px;display:grid}.field{color:var(--editor-muted);gap:5px;font-size:12px;font-weight:800;display:grid}.variant-controls{gap:11px;display:grid}.feature-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:7px;display:grid}.feature-switch{text-align:left;justify-content:space-between;align-items:center;gap:8px;min-width:0;min-height:42px;padding:0 10px;display:flex}.feature-switch[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 62%, var(--editor-line));background:linear-gradient(90deg, var(--editor-accent-soft), var(--editor-control-bg) 72%), var(--editor-control-bg);color:var(--editor-active-text);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 12%, transparent)}.feature-switch-label{font-size:13px;font-weight:900}.feature-switch-state{color:var(--editor-muted);text-transform:uppercase;font-size:11px;font-weight:900}.nested-fields{border:1px solid color-mix(in srgb, var(--editor-line) 82%, transparent);background:var(--editor-panel-muted);border-radius:8px;gap:9px;padding:10px;display:grid}.field input[type=text],.field input[type=range]{width:100%}.field input[type=text]{border:1px solid var(--editor-line-strong);background:var(--editor-control-bg);min-height:34px;color:var(--editor-control-text);border-radius:6px;padding:0 8px}.segmented-field{border:1px solid var(--editor-line);background:var(--editor-panel-soft);border-radius:8px;grid-template-columns:repeat(auto-fit,minmax(68px,1fr));gap:3px;padding:3px;display:grid}.segmented-field button{min-width:0;min-height:30px;color:var(--editor-muted);background:0 0;border-color:#0000;padding:0 7px;font-size:12px}.segmented-field button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 54%, var(--editor-line));background:linear-gradient(180deg, color-mix(in srgb, var(--editor-control-bg) 90%, var(--editor-accent)), var(--editor-control-bg));color:var(--editor-active-text);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--editor-accent) 14%, transparent), 0 1px 4px #0f141a1a}.palette-grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;display:grid}.palette-grid button{min-width:0;min-height:32px;color:var(--editor-muted);grid-template-columns:auto 1fr;align-items:center;gap:5px;padding:0 6px;font-size:12px;display:grid}.palette-grid button[aria-pressed=true]{border-color:color-mix(in srgb, var(--editor-accent) 68%, var(--editor-line));background:linear-gradient(90deg, var(--editor-accent-soft), var(--editor-control-bg) 62%), var(--editor-control-bg);color:var(--editor-active-text);box-shadow:0 0 0 2px color-mix(in srgb, var(--editor-accent) 12%, transparent)}.palette-swatch{border:1px solid var(--editor-line);border-radius:4px;width:16px;height:16px}.forced-token{border:1px dashed var(--editor-line-strong);background:var(--editor-panel-muted);min-height:32px;color:var(--editor-muted);border-radius:6px;align-items:center;padding:0 9px;font-size:12px;font-weight:850;display:flex}.empty-note{color:var(--editor-muted);align-content:start;gap:12px;margin:0;font-size:13px;font-weight:700;display:grid}.empty-note h2{color:var(--editor-text);margin:0;font-size:16px}.empty-note .primary-action{width:max-content;padding:0 12px}@media (width<=1060px){.squircle-editor-workspace{grid-template-columns:1fr}.side-panel:first-child,.inspector-panel{border:0;border-bottom:1px solid var(--editor-line)}}@media (width<=700px){.squircle-editor{max-width:100%;overflow-x:hidden}.squircle-editor-topbar{flex-direction:column;align-items:stretch}.topbar-title{min-width:0}.squircle-editor-topbar p{overflow-wrap:anywhere}.topbar-actions{justify-content:stretch}.topbar-actions button{flex:1}.squircle-editor-workspace,.side-panel,.squircle-editor-preview,.preview-stage,.code-panel{width:100%;min-width:0;max-width:100%}.squircle-editor-layer-row{grid-template-columns:minmax(0,1fr);padding-right:76px;position:relative}.layer-visibility{z-index:2;height:34px;min-height:34px;position:absolute;top:auto;bottom:9px;right:46px}.layer-select{overflow:hidden}.layer-card-meta{flex-direction:column;align-items:flex-start}.preview-toolbar{flex-direction:column;align-items:stretch}.preview-actions{justify-content:stretch}.code-toggle-button{flex:1;justify-content:center}.squircle-editor-preview{padding:10px}.preview-stage{min-height:320px}.preview-stage .squircle-scene{width:min(92%,360px);max-width:360px;padding:8px}.palette-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.feature-grid{grid-template-columns:1fr}}
|
|
2
2
|
/*$vite$:1*/
|
package/docs/README.md
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
# Documentation Index
|
|
2
2
|
|
|
3
|
-
This repo is primarily about the reusable Squircle React component in `src/squircle`.
|
|
3
|
+
This repo is primarily about the reusable Squircle React component in `src/squircle`. The root HTML files are React-backed examples that mount `src/pages` entrypoints; legacy static snapshots are kept only in the ignored `legacy-static/` archive.
|
|
4
4
|
|
|
5
5
|
## Start Here
|
|
6
6
|
|
|
7
7
|
| Need | Read |
|
|
8
8
|
| --- | --- |
|
|
9
|
-
| Use the React renderer
|
|
9
|
+
| Use the main React renderer | [React Component Guide](./react/README.md) |
|
|
10
|
+
| Use or maintain the constructor editor | [Editor Guide](./react/editor.md) |
|
|
10
11
|
| Change geometry, gradients, labels, wireframes, or colors | [Design Documentation](./design/README.md) |
|
|
11
|
-
| Maintain the
|
|
12
|
+
| Maintain the local React examples | [React Example Pages](./examples/README.md) |
|
|
13
|
+
| Compare against old static snapshots | [Legacy Static Snapshots](./static/README.md) |
|
|
12
14
|
| Check work before handoff | [Verification Checklist](./verification.md) |
|
|
13
15
|
|
|
14
16
|
## Agent Routing
|
|
15
17
|
|
|
16
|
-
- Building
|
|
18
|
+
- Building a renderer integration or reusable scene artifact: use `src/squircle` and [react/README.md](./react/README.md).
|
|
19
|
+
- Building or changing the constructor/editor: read [react/editor.md](./react/editor.md).
|
|
17
20
|
- Adjusting the squircle's look: read [design/geometry.md](./design/geometry.md), [design/rendering.md](./design/rendering.md), and [design/colors.md](./design/colors.md).
|
|
18
|
-
- Updating `index.html`, `demo.html`, or `
|
|
21
|
+
- Updating `index.html`, `demo.html`, `constructor.html`, or `react.html`: keep the HTML shell thin, edit the matching React entrypoint in `src/pages`, then verify against [examples/README.md](./examples/README.md).
|
|
19
22
|
- Adding palettes or annotations: update both the React constants and the design docs so future agents do not invent one-off colors.
|
|
20
23
|
|
|
21
24
|
## Important Boundaries
|
|
22
25
|
|
|
23
|
-
- React component docs describe
|
|
26
|
+
- React component docs describe the main renderer TypeScript API and reusable scene configuration.
|
|
27
|
+
- Editor docs describe the optional constructor UI and code-export workflow.
|
|
24
28
|
- Design docs describe visual truth: math, gradients, label paths, wireframe paint, and state constructors.
|
|
25
|
-
-
|
|
29
|
+
- Example docs describe the current React page contracts.
|
|
30
|
+
- Static docs describe ignored legacy snapshots only; they are not the source of truth.
|
|
26
31
|
- Verification docs describe what must still be true after edits.
|
package/docs/design/colors.md
CHANGED
|
@@ -76,41 +76,41 @@ Variant classes may also define fallback ghost colors, but those must remain vis
|
|
|
76
76
|
|
|
77
77
|
## Hover Palettes
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
React examples and generated code can use any palette id as a layer's hover `paletteId`. The base variant keeps its normal palette, and the hover variant receives its own `paletteId`. This means hover color changes use the same documented gradients, label contrast, and wireframe stroke colors as normal rendering.
|
|
80
80
|
|
|
81
|
-
Do not implement hover color with filters, opacity hacks, or untracked ad hoc colors. Hover color is a normal palette swap. `
|
|
81
|
+
Do not implement hover color with filters, opacity hacks, or untracked ad hoc colors. Hover color is a normal palette swap. Hover `paletteId` may be the same id as the base `paletteId`; that is useful when hover should change only material while preserving color. If the resolved hover variant matches the base variant, `SquircleScene` should not render or crossfade a hover copy.
|
|
82
82
|
|
|
83
83
|
## Annotation Auto And Overrides
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
React layers with `textColor: "contrast"` and `dashColor: "contrast"` use the palette's automatic annotation colors. Solid and transparent top-plane annotations use the palette `labelFill`. This applies to both filled text and solid dashed inlays, so default text + dash states keep one automatic contrast color.
|
|
86
86
|
|
|
87
87
|
- Darker top gradients may use a near-white label. Current example: `15 Alpha` uses `#f7fbff`.
|
|
88
88
|
- Lighter top gradients should use dark in-family annotation colors.
|
|
89
89
|
- Do not add a second outline copy for automatic contrast.
|
|
90
90
|
|
|
91
|
-
Wireframe text labels ignore
|
|
91
|
+
Wireframe text labels ignore fixed label paint and use gradient wire paint:
|
|
92
92
|
|
|
93
93
|
```css
|
|
94
94
|
fill: none !important;
|
|
95
|
-
stroke:
|
|
95
|
+
stroke: url("#...text-wire...");
|
|
96
96
|
stroke-width: var(--label-wire-width);
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
Wireframe inlays use the top-face gradient.
|
|
100
100
|
|
|
101
|
-
In React, `textStyle: "wireframe"` outlines the same live SVG text element used by filled mode. On solid/transparent material, outline paint comes from `textColor`; `contrast` resolves to
|
|
101
|
+
In React, `textStyle: "wireframe"` outlines the same live SVG text element used by filled mode. On solid/transparent material, outline paint comes from `textColor`; `contrast` resolves to `labelFill`, matching filled text and solid dash. On wireframe material, outline paint comes from label-local `textWire` gradients; do not sample the full-face top ramp there, and do not replace the text with single-stroke lettering.
|
|
102
102
|
|
|
103
|
-
`
|
|
103
|
+
`SquircleVariantConfig` supports explicit annotation overrides:
|
|
104
104
|
|
|
105
105
|
| Field | Values | Paint |
|
|
106
106
|
| --- | --- | --- |
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
107
|
+
| `textColor` | `contrast`, `white`, `black` | Filled or outlined text paint for solid/transparent material; `contrast` appears as `Auto` in the UI |
|
|
108
|
+
| `textStyle` | `solid`, `wireframe` | Filled or outlined text label |
|
|
109
109
|
| `dashColor` | `contrast`, `white`, `black` | Dash stroke for solid/transparent material; `contrast` appears as `Auto` in the UI |
|
|
110
110
|
|
|
111
|
-
Use `Auto` when the annotation should follow the palette. The exported value is `contrast`. Use `white` or `black` only when the user deliberately wants fixed annotation paint.
|
|
111
|
+
Use `Auto` when the annotation should follow the palette. The exported value is `contrast`. Use `white` or `black` only when the user deliberately wants fixed annotation paint. On wireframe material, dash always uses the top gradient; text uses the text surface gradient at full opacity when `textStyle` is `solid` and the text wire gradient when `textStyle` is `wireframe`.
|
|
112
112
|
|
|
113
|
-
Legacy configs with `gpuStyle: "transparent"` normalize to `solid`; there is no third text paint control.
|
|
113
|
+
Deprecated `gpuColor` and `gpuStyle` aliases are accepted only for older snippets. Legacy configs with `gpuStyle: "transparent"` normalize to `solid`; there is no third text paint control.
|
|
114
114
|
|
|
115
115
|
## Edge Colors
|
|
116
116
|
|
package/docs/design/geometry.md
CHANGED
|
@@ -89,25 +89,26 @@ Rotate the sampled array if needed so the selected front-facing indices are emit
|
|
|
89
89
|
|
|
90
90
|
The complementary back-bottom edge is emitted as `ghost-hidden` and is only shown in wireframe mode.
|
|
91
91
|
|
|
92
|
-
## Generated
|
|
92
|
+
## Generated React Geometry
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
`src/squircle/geometry.ts` returns the geometry consumed by `SquircleScene`:
|
|
95
95
|
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
96
|
+
- `topPoints`: lit top-face polygon.
|
|
97
|
+
- `wallPoints`: one continuous front side-wall polygon.
|
|
98
|
+
- `hiddenPoints`: back/bottom edge used only in wireframe mode.
|
|
99
|
+
- `inlayPoints`: top-plane dashed squircle polygon.
|
|
100
|
+
- `labelTransform`: `matrix(cosA, sinA, -cosA, sinA, cx, cy - h)` for live SVG text.
|
|
101
|
+
- `topBounds` and `sideBounds`: gradient coordinate boxes.
|
|
100
102
|
|
|
101
103
|
## Regeneration Checklist
|
|
102
104
|
|
|
103
|
-
When changing geometry,
|
|
105
|
+
When changing geometry, update the generator and keep these in sync:
|
|
104
106
|
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
- Gradient bboxes documented in [rendering.md](./rendering.md)
|
|
107
|
+
- `DEFAULT_GEOMETRY` in `src/squircle/geometry.ts`.
|
|
108
|
+
- `SquircleScene` gradient definitions in `src/squircle/SquircleScene.tsx`.
|
|
109
|
+
- Example dimensions in `src/pages`.
|
|
110
|
+
- Docs in [rendering.md](./rendering.md) and [single-squircle-states.md](./single-squircle-states.md).
|
|
111
|
+
- Legacy snapshots only if you intentionally refresh `legacy-static/`.
|
|
111
112
|
|
|
112
113
|
For small visual changes:
|
|
113
114
|
|
package/docs/design/rendering.md
CHANGED
|
@@ -42,16 +42,15 @@ The top face is drawn after the side wall, so it hides the back half of the extr
|
|
|
42
42
|
|
|
43
43
|
The middle-layer dashed squircle is generated from the same superellipse at `0.6 * a` and projected at `z = h`, so it lies on the top plane. The static copies are:
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
- `#top-inlay-wire` for wireframe rendering
|
|
45
|
+
`SquircleScene` renders the dashed inlay as the same top-plane polygon in every material. Paint changes by material:
|
|
47
46
|
|
|
48
|
-
By default, solid and transparent inlays use
|
|
47
|
+
By default, solid and transparent inlays use the palette label color, the same contrast token as filled text. Wireframe inlays use the top-face gradient, matching the prism wires.
|
|
49
48
|
|
|
50
|
-
`
|
|
49
|
+
`dashColor: "contrast"` keeps the material default. `dashColor: "white"` and `dashColor: "black"` intentionally use fixed stroke colors on solid/transparent material. Wireframe material ignores fixed dash color and uses the wire gradient.
|
|
51
50
|
|
|
52
51
|
## Label Source
|
|
53
52
|
|
|
54
|
-
React renders top-plane text as one live SVG `<text>` element, so component configs can pass arbitrary strings such as `text: "GPU"`, `text: "CUDA"`, or `text: "AI"`.
|
|
53
|
+
React renders top-plane text as one live SVG `<text>` element, so component configs can pass arbitrary strings such as `text: "GPU"`, `text: "CUDA"`, or `text: "AI"`. `GPU` is only the example string used by the local demos.
|
|
55
54
|
|
|
56
55
|
Current label parameters:
|
|
57
56
|
|
|
@@ -62,7 +61,7 @@ Current label parameters:
|
|
|
62
61
|
- Centered bbox: `x = -63.0293..63.0293`, `y = -22.9473..22.9473`
|
|
63
62
|
- Label wire stroke: `1.1`
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
The old static snapshots used an outlined glyph path generated with `opentype.js@2.0.0`. Do not use that path technique in new component work; keep live text so arbitrary strings render correctly.
|
|
66
65
|
|
|
67
66
|
```js
|
|
68
67
|
const font = opentype.parse(fs.readFileSync("/System/Library/Fonts/Supplemental/Arial.ttf").buffer);
|
|
@@ -72,11 +71,11 @@ const tx = -((box.x1 + box.x2) / 2);
|
|
|
72
71
|
const ty = -((box.y1 + box.y2) / 2);
|
|
73
72
|
```
|
|
74
73
|
|
|
75
|
-
|
|
74
|
+
If a legacy snapshot ever needs refreshing, apply `tx` and `ty` to every path command coordinate before writing the static `d` attribute, and keep `fill-rule="evenodd"` plus `clip-rule="evenodd"` so counters remain true holes.
|
|
76
75
|
|
|
77
76
|
## Label Transform And Paint
|
|
78
77
|
|
|
79
|
-
Every
|
|
78
|
+
Every React label is a single `<text>` element transformed onto the top plane with:
|
|
80
79
|
|
|
81
80
|
```text
|
|
82
81
|
matrix(cosA, sinA, -cosA, sinA, cx, cy - h)
|
|
@@ -90,27 +89,27 @@ matrix(0.94, 0.342, -0.94, 0.342, 400, 145.6)
|
|
|
90
89
|
|
|
91
90
|
The transform never changes between solid and wireframe modes; only paint changes. React and constructor defaults intentionally mirror the original generated GPU path: `textSize: 62`, `textFontFamily: "Arial, Helvetica, sans-serif"`, and `textFontWeight: 400`.
|
|
92
91
|
|
|
93
|
-
Solid
|
|
92
|
+
Solid text style:
|
|
94
93
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
94
|
+
- The label uses palette label paint when `textColor` is `contrast`.
|
|
95
|
+
- Solid/transparent dash uses the same palette label paint by default.
|
|
97
96
|
- `15 Alpha` uses `#f7fbff` because its top gradient is dark.
|
|
98
97
|
- Lighter variants use dark in-family label fills.
|
|
99
98
|
|
|
100
|
-
Wireframe
|
|
99
|
+
Wireframe text style:
|
|
101
100
|
|
|
102
101
|
- `textStyle: "wireframe"` uses the same live text element as filled mode.
|
|
103
|
-
- It sets `fill:none
|
|
104
|
-
- On solid/transparent material,
|
|
105
|
-
- On wireframe material,
|
|
106
|
-
-
|
|
102
|
+
- It sets `fill: none` and uses a stroke.
|
|
103
|
+
- On solid/transparent material, outline paint comes from `textColor`; `contrast` resolves to the palette label paint, matching filled text and solid dash.
|
|
104
|
+
- On wireframe material, outline paint resolves to the label-local `textWire` gradient.
|
|
105
|
+
- Wireframe dash uses the top-face gradient so dashed inlays match prism wires.
|
|
107
106
|
- It must remain an outline around the font figures, not a single-stroke lettering replacement.
|
|
108
107
|
- Keep the outline stroke thin relative to text size. The default `labelWire` is `1.1` at `textSize: 62`, safely below the ratio where counters start merging.
|
|
109
108
|
|
|
110
|
-
Do not sample the full-face
|
|
109
|
+
Do not sample the full-face top ramp directly for the text outline on wireframe material; it is too large for the label geometry and makes the color read wrong. Use one text element for all text styles. Use `textColor` for solid/transparent outline paint and the label-local wire gradient for wireframe-material outline paint.
|
|
111
110
|
|
|
112
|
-
|
|
111
|
+
Deprecated `gpuColor` aliases are accepted only for old snippets and map to React `textColor`. They override label paint only on solid/transparent material. On wireframe material, `textStyle: "solid"` renders a fully opaque filled label using the text surface gradient, and `textStyle: "wireframe"` renders an outlined label using the text wire gradient.
|
|
113
112
|
|
|
114
|
-
Do not introduce `textStyle: "transparent"` as a third paint control. Legacy
|
|
113
|
+
Do not introduce `textStyle: "transparent"` as a third paint control. Legacy configs with `gpuStyle: "transparent"` should normalize to `solid`.
|
|
115
114
|
|
|
116
115
|
Do not build filled letters from `<rect>`, `<line>`, or separate stem/bowl paths. Do not add a second label copy and do not replace the text with monoline lettering for outline mode.
|
|
@@ -1,121 +1,59 @@
|
|
|
1
1
|
# Single-Squircle State Constructors
|
|
2
2
|
|
|
3
|
-
This file
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
This file describes reusable one-layer constructors for `SquircleScene`. Use these recipes when another agent wants one squircle outside a full multi-layer composition.
|
|
4
|
+
|
|
5
|
+
## Base Layer Shape
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import type { SquircleLayerConfig } from "@dstackai/sqircle";
|
|
9
|
+
|
|
10
|
+
const layer: SquircleLayerConfig = {
|
|
11
|
+
id: "single",
|
|
12
|
+
base: {
|
|
13
|
+
material: "solid",
|
|
14
|
+
paletteId: "15",
|
|
15
|
+
text: "GPU",
|
|
16
|
+
textStyle: "solid",
|
|
17
|
+
dash: true
|
|
18
|
+
}
|
|
19
|
+
};
|
|
13
20
|
```
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- `--top-fill`
|
|
18
|
-
- `--side-fill`
|
|
19
|
-
- `--label-fill`
|
|
20
|
-
- `--top-edge`
|
|
21
|
-
- `--side-edge`
|
|
22
|
-
- `--label-wire-width`
|
|
23
|
-
- `--face-edge-width`
|
|
24
|
-
- `--face-edge-opacity`
|
|
25
|
-
|
|
26
|
-
The global transparent/wireframe switch does not affect this drawer. The drawer always shows all constructor states at once.
|
|
27
|
-
|
|
28
|
-
## Building Blocks
|
|
29
|
-
|
|
30
|
-
| Part | SVG use | CSS class | Purpose |
|
|
31
|
-
| --- | --- | --- | --- |
|
|
32
|
-
| Solid prism | `<use href="#prism-active" />` | `.single-active` | Filled, selected-looking prism with top and side gradients |
|
|
33
|
-
| Transparent prism | `<use href="#prism-ghost" />` | `.single-ghost` inside `.single-transparent` | Translucent filled prism |
|
|
34
|
-
| Wireframe prism | `<use href="#prism-ghost" />` | `.single-ghost` inside `.single-wire` | Gradient outline with transparent faces |
|
|
35
|
-
| Solid/transparent dash | `<use href="#top-inlay" />` | `.single-inlay` | Dashed top-plane squircle using `--label-fill` |
|
|
36
|
-
| Wireframe dash | `<use href="#top-inlay-wire" />` | `.single-wire-inlay.wire-inlay` | Gradient dashed top-plane squircle |
|
|
37
|
-
| Text label | `<use href="#label-gpu" />` | `.single-label.plane-label` | Compound path label on the top plane; the current static glyph spells `GPU` |
|
|
38
|
-
|
|
39
|
-
The label always uses this transform:
|
|
40
|
-
|
|
41
|
-
```html
|
|
42
|
-
transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)"
|
|
43
|
-
```
|
|
22
|
+
`GPU` is only example text. Any string is valid, including `"{}"` or `"CUDA"`.
|
|
44
23
|
|
|
45
24
|
## Constructor Matrix
|
|
46
25
|
|
|
47
|
-
| State |
|
|
48
|
-
| --- | --- |
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Transparent with text and dash:
|
|
74
|
-
|
|
75
|
-
```html
|
|
76
|
-
<svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="Transparent single squircle with text and dashed inlay">
|
|
77
|
-
<use class="single-ghost" href="#prism-ghost" />
|
|
78
|
-
<use class="single-inlay" href="#top-inlay" />
|
|
79
|
-
<use class="single-label plane-label" href="#label-gpu" transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)" />
|
|
80
|
-
</svg>
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Wireframe with text and dash:
|
|
84
|
-
|
|
85
|
-
```html
|
|
86
|
-
<svg class="single-squircle" viewBox="-6 -6 812 314" aria-label="Wireframe single squircle with text and dashed inlay">
|
|
87
|
-
<use class="single-ghost" href="#prism-ghost" />
|
|
88
|
-
<use class="single-wire-inlay wire-inlay" href="#top-inlay-wire" />
|
|
89
|
-
<use class="single-label plane-label" href="#label-gpu" transform="matrix(0.94,0.342,-0.94,0.342,400,145.6)" />
|
|
90
|
-
</svg>
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
The text + dash states are reusable variants that combine the top-plane text path with a dashed inlay.
|
|
94
|
-
|
|
95
|
-
In the static single-state drawer, solid and transparent text + dash states use `--label-fill` for both the filled text path and dashed inlay. Wireframe text + dash states use `--top-fill` for both the outlined text path and dashed inlay. `constructor.html` can override GPU color, GPU style, and dash color per layer because it is the GPU example; React generated code maps those controls to `textColor`, `textStyle`, and `dashColor`.
|
|
26
|
+
| State | Config |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| Solid | `{ material: "solid" }` |
|
|
29
|
+
| Solid Text | `{ material: "solid", text: "GPU", textStyle: "solid" }` |
|
|
30
|
+
| Solid Text Outline | `{ material: "solid", text: "GPU", textStyle: "wireframe" }` |
|
|
31
|
+
| Solid Dash | `{ material: "solid", dash: true }` |
|
|
32
|
+
| Solid Text + Dash | `{ material: "solid", text: "GPU", textStyle: "solid", dash: true }` |
|
|
33
|
+
| Transparent | `{ material: "transparent" }` |
|
|
34
|
+
| Transparent Text | `{ material: "transparent", text: "GPU", textStyle: "solid" }` |
|
|
35
|
+
| Transparent Text Outline + Dash | `{ material: "transparent", text: "GPU", textStyle: "wireframe", dash: true }` |
|
|
36
|
+
| Wireframe | `{ material: "wireframe" }` |
|
|
37
|
+
| Wireframe Text Filled | `{ material: "wireframe", text: "GPU", textStyle: "solid" }` |
|
|
38
|
+
| Wireframe Text Outline | `{ material: "wireframe", text: "GPU", textStyle: "wireframe" }` |
|
|
39
|
+
| Wireframe Text Outline + Dash | `{ material: "wireframe", text: "GPU", textStyle: "wireframe", dash: true }` |
|
|
40
|
+
|
|
41
|
+
The local `index.html` example renders these through `createSingleStatePresets()` in `src/pages/exampleData.ts`.
|
|
42
|
+
|
|
43
|
+
## Paint Rules
|
|
44
|
+
|
|
45
|
+
- Solid and transparent text + dash states use palette contrast paint by default.
|
|
46
|
+
- Solid/transparent `textColor` and `dashColor` may be `contrast`, `white`, or `black`.
|
|
47
|
+
- Wireframe dash always uses the wire gradient.
|
|
48
|
+
- Wireframe `textStyle: "wireframe"` uses the label-local text wire gradient.
|
|
49
|
+
- Wireframe `textStyle: "solid"` uses the text surface gradient at full opacity.
|
|
96
50
|
|
|
97
51
|
## Ordering Rules
|
|
98
52
|
|
|
99
|
-
|
|
100
|
-
- Draw the inlay after the prism.
|
|
101
|
-
- Draw the label last.
|
|
102
|
-
- Use `#top-inlay` for solid/transparent states.
|
|
103
|
-
- Use `#top-inlay-wire` for wireframe states.
|
|
104
|
-
- Do not use two label copies. The same `.plane-label` object changes paint through CSS.
|
|
53
|
+
`SquircleScene` draws a variant in this order:
|
|
105
54
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
```css
|
|
111
|
-
#variant-15:checked ~ .hero-card,
|
|
112
|
-
#variant-15:checked ~ .single-drawer {
|
|
113
|
-
--top-fill: url("#top-15");
|
|
114
|
-
--side-fill: url("#side-15");
|
|
115
|
-
--label-fill: #f7fbff;
|
|
116
|
-
--top-edge: #7c5fd0;
|
|
117
|
-
--side-edge: #2d1466;
|
|
118
|
-
}
|
|
119
|
-
```
|
|
55
|
+
1. Prism side/top or wireframe prism.
|
|
56
|
+
2. Dashed inlay, when enabled.
|
|
57
|
+
3. Live SVG text, when enabled.
|
|
120
58
|
|
|
121
|
-
|
|
59
|
+
Do not create two text copies for filled and outline states. The same live `<text>` object changes only paint.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# React Example Pages
|
|
2
|
+
|
|
3
|
+
The root HTML files are Vite shells. They should stay small and only mount React entrypoints from `src/pages`.
|
|
4
|
+
|
|
5
|
+
| HTML | React entrypoint | Purpose |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| `index.html` | `src/pages/IndexPage.tsx` | Hero scene plus selectable single-squircle state drawer |
|
|
8
|
+
| `demo.html` | `src/pages/DemoPage.tsx` | Selectable generated gallery of 3-layer compositions |
|
|
9
|
+
| `constructor.html` | `src/pages/ConstructorPage.tsx` | Full constructor UI using `SquircleEditor` |
|
|
10
|
+
| `react.html` | `src/pages/ConstructorPage.tsx` | Compatibility alias for the constructor |
|
|
11
|
+
|
|
12
|
+
## Source Files
|
|
13
|
+
|
|
14
|
+
- `src/pages/exampleData.ts`: shared preset and seed-layer constructors.
|
|
15
|
+
- `src/pages/PageShell.tsx`: shared page chrome and theme switch.
|
|
16
|
+
- `src/pages/pages.css`: example-page layout styles.
|
|
17
|
+
|
|
18
|
+
The examples must consume `SquircleScene`, `SquircleEditor`, palettes, and helpers from `src/squircle`. Do not paste generated SVG polygons or duplicate renderer logic in page files.
|
|
19
|
+
|
|
20
|
+
## Behavior
|
|
21
|
+
|
|
22
|
+
- `index.html` renders a main three-layer scene and a collapsed/openable drawer of single-squircle states. Palette buttons recolor the examples through React state.
|
|
23
|
+
- `demo.html` renders 96 generated composition presets. Clicking a card changes the main hero composition. Each layer hover is a state/color swap only.
|
|
24
|
+
- `constructor.html` renders `SquircleEditor` with three default plain wireframe layers. The Code panel exports React code using `@dstackai/sqircle` as the import path.
|
|
25
|
+
- `react.html` intentionally mirrors `constructor.html` for older links.
|
|
26
|
+
|
|
27
|
+
## Rules
|
|
28
|
+
|
|
29
|
+
- Keep root HTML files as shells with one `#root` and one module script.
|
|
30
|
+
- Keep examples transparent-background friendly.
|
|
31
|
+
- Keep hover effects as opacity crossfades between base and hover variants. Do not add movement, shadows, scale, filters, or gap changes.
|
|
32
|
+
- Keep all squircle geometry, palette, stroke, annotation, and theme behavior in the reusable component layer.
|
|
33
|
+
- If a legacy static behavior is still valuable, migrate it into `src/pages` or `src/squircle`; do not edit ignored snapshots as source.
|