@gemboss/ui 0.36.1 → 0.40.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/CHANGELOG.md +441 -0
- package/README.md +617 -14
- package/catalog/editor.html +45 -25
- package/catalog/index.html +5522 -1448
- package/gemboss-ui.css +3 -3
- package/package.json +11 -2
- package/parts/components.css +2 -2
- package/parts/core.css +1 -1
- package/parts/shell.css +2 -2
- package/src/assistant/chat-markdown.ts +7 -4
- package/src/assistant.css +1 -1
- package/src/auth-field.css +1 -0
- package/src/auth-field.tsx +74 -0
- package/src/auth-sso-plain.js +1 -0
- package/src/auth.css +1 -1
- package/src/auth.tsx +64 -1
- package/src/brand-gem.tsx +19 -0
- package/src/brand.tsx +4 -7
- package/src/chat-plain.js +2 -2
- package/src/editor/controls.tsx +148 -11
- package/src/editor/frame.tsx +8 -4
- package/src/editor/index.ts +8 -4
- package/src/editor/panel.tsx +44 -14
- package/src/editor/repeater.tsx +322 -0
- package/src/editor/sortable.tsx +50 -24
- package/src/editor/swatch.ts +20 -0
- package/src/editor/tiles.tsx +51 -0
- package/src/editor-plain.js +1 -1
- package/src/editor-repeater.css +1 -0
- package/src/editor-tiles.css +1 -0
- package/src/editor.css +1 -1
- package/src/gemboss-field.css +1 -1
- package/src/icons.tsx +11 -0
- package/src/index.ts +14 -2
- package/src/kit-plain.js +1 -1
- package/src/service-switcher-plain.js +1 -1
- package/src/service-switcher.css +1 -1
- package/src/service-switcher.tsx +119 -35
- package/src/services.ts +4 -2
- package/src/shell.css +1 -1
- package/src/site.css +1 -0
- package/src/site.tsx +50 -0
- package/src/team-chat-data.ts +79 -0
- package/src/team-chat.css +1 -0
- package/src/team-chat.tsx +825 -0
- package/src/toggle-bar.tsx +60 -0
- package/src/ui.css +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(i){"use strict";var g=i.gembossUI||(i.gembossUI={}),d="https://admin.gemboss.ai",l={not_member:"Your GemBoss account does not have access here. Use the password.",invalid:"That sign-in did not go through. Try again, or use the password.",off:"Signing in with GemBoss is not available here yet. Use the password."},f='<svg viewBox="0 0 32 32" width="18" height="18" aria-hidden="true" focusable="false"><rect width="32" height="32" rx="7"/><g transform="translate(16,16) scale(0.24) translate(-50,-62)"><polygon points="26,14 74,14 66,50 34,50" fill="#8a93ff"/><path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="#f4f4f2" stroke-width="5" stroke-linejoin="round"/><path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="#f4f4f2" stroke-opacity="0.72" stroke-width="3" stroke-linejoin="round"/></g></svg>';function b(){var s=document.querySelectorAll("a[data-gb-sso]");if(!s.length)return!1;var t=new URLSearchParams(location.search),h=t.get("sso"),c=!t.has("sso")&&t.get("password")!=="1"&&/(?:^|;\s*)gb_sso=1(?:;|$)/.test(document.cookie),o=null;return s.forEach(function(e){var m=(e.getAttribute("data-issuer")||d)+"/api/auth/sso/authorize?service="+encodeURIComponent(e.getAttribute("data-gb-sso"))+"&next="+encodeURIComponent(e.getAttribute("data-next")||"/");e.href=m,o=o||e,e.querySelector("svg")||(e.innerHTML=f+'<span class="gb-auth-sso-label">Continue with GemBoss</span>',e.querySelector("rect").style.fill="var(--gb-mark-bg, #0b0b0c)");var r=e.closest(".gb-auth-sso")||e.parentNode;if(h&&!r.querySelector(".gb-auth-sso-why")){var n=document.createElement("p");n.className="gb-auth-sso-why",n.setAttribute("role","status"),n.textContent=l[h]||l.invalid,e.after(n)}if(!r.querySelector(".gb-auth-or")){var a=document.createElement("p");a.className="gb-auth-or",a.textContent="or use the password",r.append(a)}e.addEventListener("click",function(){u(e)})}),c&&(u(o),location.replace(o.href)),c}function u(s){var t=s.querySelector(".gb-auth-sso-label");t&&(t.textContent="Continuing with GemBoss\u2026")}g.initContinueWithGemBoss=b})(typeof globalThis<"u"?globalThis:this);
|
package/src/auth.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.gb-auth{--gb-surface: #ffffff;--gb-ink: var(--gemboss-text-strong, #1b1c1e);--gb-muted: #6d7175;--gb-line: #e1e3e5;--gb-field-line: #c9cccf;--gb-field-bg: #ffffff;--gb-field-hover: #fafafa;--gb-placeholder: #868b94;--gb-accent: #5661f5;--gb-btn: linear-gradient(120deg, #1b1c1e 0%, #000000 100%);--gb-btn-ink: #ffffff;--gb-wordmark: linear-gradient(120deg, #1b1c1e 0%, #000000 100%);--gb-hover: #f7f7f5;--gb-hover-line: #b9bbc0;--gb-err-bg: #fee2e2;--gb-err-ink: #b91c1c;--gb-err-line: transparent;--gb-card-shadow: 0 24px 64px rgba(15, 23, 42, .08);--gb-mark-bg: #0b0b0c;--gb-facet: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 120'%3E%3Cg fill='none' stroke='%23ffffff' stroke-width='0.45' stroke-linejoin='round'%3E%3Cpath d='M26,14 74,14 96,50 50,110 4,50Z'/%3E%3Cpath d='M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110'/%3E%3C/g%3E%3C/svg%3E");--gb-page: radial-gradient(1200px 600px at 20% 10%, rgba(27, 28, 30, .05), transparent 60%), radial-gradient(1000px 500px at 80% 90%, rgba(86, 97, 245, .06), transparent 60%), #fafafb;--gb-page-flat: #ffffff;--gb-panel-bg: radial-gradient(900px 520px at 78% 12%, rgba(86, 97, 245, .26), transparent 62%), radial-gradient(700px 480px at 12% 96%, rgba(86, 97, 245, .12), transparent 60%), #0b0b0c;--gb-panel-ink: #f4f4f2;--gb-panel-muted: rgba(244, 244, 242, .72);--gb-panel-eyebrow: #8a93ff;--gb-panel-facet-o: .05;--gb-panel-grid-bg: linear-gradient(160deg, #1b1c22 0%, #0b0b0c 72%);--gb-panel-grid-line: rgba(138, 147, 255, .1);--gb-panel-grid-size: 34px;--gb-panel-grid-fade: radial-gradient(90% 80% at 30% 20%, #000, transparent 75%);color-scheme:light dark;position:fixed;top:var(--vvt, 0px);left:0;right:0;height:100vh;height:var(--vvh, 100dvh);container:gb-auth / size;overflow-y:auto;background:var(--gb-page);color:var(--gb-ink);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif}.gb-auth-split{display:grid;grid-template-columns:1fr;min-height:100%}.gb-auth-stage{box-sizing:border-box;display:flex;min-height:100%;padding:clamp(12px,3cqh,24px) clamp(16px,4vw,24px)}.gb-auth-panel-in{position:relative;box-sizing:border-box;width:100%;max-width:520px;padding:48px clamp(32px,5vw,64px)}.gb-auth-eyebrow{margin:0 0 20px;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:12px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--gb-panel-eyebrow)}.gb-auth-panel-h{margin:0 0 16px;font-size:clamp(28px,3.2vw,40px);line-height:1.15;letter-spacing:-.02em;font-weight:700;color:var(--gb-panel-ink)}.gb-auth-panel-p{margin:0;font-size:15px;line-height:1.6;color:var(--gb-panel-muted)}.gb-auth-panel{display:none}@container gb-auth (min-width: 900px) and (min-height: 520px){.gb-auth-split{grid-template-columns:minmax(420px,1fr) minmax(0,1.05fr)}.gb-auth-split .gb-auth-stage{background:var(--gb-surface)}.gb-auth-split .gb-auth-card{border:none;box-shadow:none;max-width:400px}.gb-auth-panel{display:flex;align-items:center;position:relative;overflow:hidden;background:var(--gb-panel-bg);color:var(--gb-panel-ink)}.gb-auth-panel:before{content:"";position:absolute;inset:-45% -38% auto auto;width:132%;aspect-ratio:1;background-image:var(--gb-facet);background-size:contain;background-repeat:no-repeat;opacity:var(--gb-panel-facet-o);pointer-events:none}.gb-auth-split .gb-auth-panel--grid{background:var(--gb-panel-grid-bg)}.gb-auth-panel.gb-auth-panel--grid:before{inset:0;width:auto;aspect-ratio:auto;background-image:linear-gradient(var(--gb-panel-grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--gb-panel-grid-line) 1px,transparent 1px);background-size:var(--gb-panel-grid-size) var(--gb-panel-grid-size);background-repeat:repeat;opacity:1;-webkit-mask-image:var(--gb-panel-grid-fade);mask-image:var(--gb-panel-grid-fade)}.gb-auth-panel.gb-auth-panel--none:before{content:none}}.gb-auth-card{width:100%;max-width:380px;margin:auto;background:var(--gb-surface);border:1px solid var(--gb-line);border-radius:14px;padding:clamp(18px,3.5cqh,28px) 28px;box-shadow:var(--gb-card-shadow)}.gb-auth-header{text-align:center;margin-bottom:clamp(12px,3cqh,24px)}.gb-auth-mark{display:inline-flex;margin-bottom:clamp(6px,calc(6px + (100cqh - 480px)*.075),12px)}.gb-auth-mark svg,.gb-auth-mark img{width:clamp(32px,calc(32px + (100cqh - 480px)*.3),56px);height:clamp(32px,calc(32px + (100cqh - 480px)*.3),56px)}@container gb-auth (max-height: 479.98px){.gb-auth-mark{display:none}}.gb-auth-header h1{font-size:clamp(19px,2.75cqh,22px);font-weight:700;margin:0 0 clamp(0px,calc((100cqh - 560px)*.1),4px);background:var(--gb-wordmark);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.gb-auth-header p{margin:0;font-size:13px;color:var(--gb-muted)}@container gb-auth (max-height: 599.98px){.gb-auth-header p{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}}.gb-auth-form{display:flex;flex-direction:column;gap:clamp(8px,1.5cqh,12px)}.gb-auth-form label{display:flex;flex-direction:column;gap:clamp(4px,.75cqh,6px);font-size:12px;font-weight:500;color:var(--gb-ink)}.gb-auth-input{box-sizing:border-box;height:32px;min-height:44px;min-width:0;padding:0 12px;font:inherit;font-size:13px;line-height:20px;border:1px solid;border-radius:8px}@media (max-width: 47.9975em){.gb-auth-input{font-size:16px}}@media (pointer: coarse){.gb-auth-input{height:auto}}.gb-auth-form input{width:100%;box-sizing:border-box;background-color:var(--gb-field-bg);border-color:var(--gb-field-line);color:var(--gb-ink)}.gb-auth-form input::placeholder{color:var(--gb-placeholder)}.gb-auth-form input:hover:not(:disabled){background-color:var(--gb-field-hover);border-color:var(--gb-hover-line)}.gb-auth-form input:focus-visible{outline:2px solid var(--gb-ink);outline-offset:1px;border-color:var(--gb-ink)}.gb-auth-field{display:flex;flex-direction:column;gap:clamp(4px,.75cqh,6px);font-size:12px;font-weight:500;color:var(--gb-ink)}.gb-auth-pw{position:relative;display:flex}.gb-auth-pw input{width:100%;padding-right:48px}.gb-auth-pw-toggle{min-height:44px;min-width:44px;position:absolute;right:0;top:0;bottom:0;display:inline-flex;align-items:center;justify-content:center;padding:0;margin:0;background:none;border:none;border-radius:8px;cursor:pointer;color:var(--gb-muted);transition:none}.gb-auth-pw-toggle:hover{color:var(--gb-ink)}.gb-auth-pw-toggle:focus-visible{outline:2px solid var(--gemboss-primary, #1b1c1e);outline-offset:-2px}.gb-auth-error{background:var(--gb-err-bg);border:1px solid var(--gb-err-line);color:var(--gb-err-ink);padding:8px 10px;font-size:13px;border-radius:6px}.gb-auth-form>.gb-auth-submit{margin-top:clamp(2px,.75cqh,6px);min-height:44px;font-size:14px;font-weight:600;border:none;border-radius:8px;background:var(--gb-btn);color:var(--gb-btn-ink);cursor:pointer;font-family:inherit;transition:opacity .15s ease,transform .15s ease}.gb-auth-form>.gb-auth-submit:hover:not(:disabled){transform:translateY(-1px)}.gb-auth-form>.gb-auth-submit:disabled{opacity:.6;cursor:not-allowed}.gb-auth-foot{margin:clamp(10px,2cqh,16px) 0 0;font-size:13px;color:var(--gb-muted);text-align:center;line-height:1.5}.gb-auth-foot a{display:inline-block;min-height:24px;line-height:24px;padding:0 2px;color:var(--gb-accent);font-weight:600;text-decoration:none}.gb-auth-foot a:hover,.gb-auth-foot a:focus-visible{text-decoration:underline}.gb-auth-card .gb-auth-help{margin:clamp(8px,1.75cqh,14px) 0 0;font-size:12px;color:var(--gb-muted);text-align:center;line-height:1.5}.gb-auth-card .gb-auth-help a{display:inline-block;min-height:24px;line-height:24px;color:var(--gb-accent);font-weight:600;text-decoration:none}.gb-auth-card .gb-auth-help a:hover,.gb-auth-card .gb-auth-help a:focus-visible{text-decoration:underline}@media (max-width: 480px){.gb-auth{background:var(--gb-page-flat)}.gb-auth-stage{padding:max(clamp(10px,3cqh,20px),env(safe-area-inset-top)) max(16px,env(safe-area-inset-right)) max(clamp(10px,3cqh,20px),env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left))}.gb-auth-card{border:none;border-radius:0;box-shadow:none;padding:0}}@container gb-auth (max-height: 399.98px){.gb-auth-header{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}}@container gb-auth (max-height: 359.98px){.gb-auth-card .gb-auth-help,.gb-auth-foot{display:none}.gb-auth-stage{padding-top:max(8px,env(safe-area-inset-top));padding-bottom:max(8px,env(safe-area-inset-bottom))}}@container gb-auth (max-height: 359.98px) and (min-width: 481px){.gb-auth-card{padding-top:14px;padding-bottom:14px}}@media (prefers-color-scheme: dark){:root:not([data-theme=light]) .gb-auth{--gb-surface: #141517;--gb-ink: #f4f4f3;--gb-muted: #969c97;--gb-line: rgba(255, 255, 255, .11);--gb-field-line: rgba(255, 255, 255, .17);--gb-field-bg: #1b1c1f;--gb-field-hover: #212226;--gb-placeholder: #7f838c;--gb-accent: #8a93ff;--gb-btn: linear-gradient(120deg, #f4f4f3 0%, #dfe0e4 100%);--gb-btn-ink: #0b0b0c;--gb-wordmark: linear-gradient(120deg, #f4f4f3 0%, #c9cbd2 100%);--gb-hover: rgba(255, 255, 255, .06);--gb-hover-line: rgba(255, 255, 255, .28);--gb-err-bg: rgba(215, 44, 13, .16);--gb-err-ink: #ff9d88;--gb-err-line: rgba(245, 120, 95, .4);--gb-card-shadow: 0 24px 64px rgba(0, 0, 0, .55);--gb-mark-bg: #202127;--gb-page: radial-gradient(1200px 600px at 20% 10%, rgba(255, 255, 255, .05), transparent 60%), radial-gradient(1000px 500px at 80% 90%, rgba(138, 147, 255, .1), transparent 60%), #0b0b0c;--gb-page-flat: #0b0b0c}}:root[data-theme=dark] .gb-auth{--gb-surface: #141517;--gb-ink: #f4f4f3;--gb-muted: #969c97;--gb-line: rgba(255, 255, 255, .11);--gb-field-line: rgba(255, 255, 255, .17);--gb-field-bg: #1b1c1f;--gb-field-hover: #212226;--gb-placeholder: #7f838c;--gb-accent: #8a93ff;--gb-btn: linear-gradient(120deg, #f4f4f3 0%, #dfe0e4 100%);--gb-btn-ink: #0b0b0c;--gb-wordmark: linear-gradient(120deg, #f4f4f3 0%, #c9cbd2 100%);--gb-hover: rgba(255, 255, 255, .06);--gb-hover-line: rgba(255, 255, 255, .28);--gb-err-bg: rgba(215, 44, 13, .16);--gb-err-ink: #ff9d88;--gb-err-line: rgba(245, 120, 95, .4);--gb-card-shadow: 0 24px 64px rgba(0, 0, 0, .55);--gb-mark-bg: #202127;--gb-page: radial-gradient(1200px 600px at 20% 10%, rgba(255, 255, 255, .05), transparent 60%), radial-gradient(1000px 500px at 80% 90%, rgba(138, 147, 255, .1), transparent 60%), #0b0b0c;--gb-page-flat: #0b0b0c}
|
|
1
|
+
.gb-auth{--gb-surface: #ffffff;--gb-ink: var(--gemboss-text-strong, #1b1c1e);--gb-muted: #6d7175;--gb-line: #e1e3e5;--gb-field-line: #c9cccf;--gb-field-bg: #ffffff;--gb-field-hover: #fafafa;--gb-placeholder: #868b94;--gb-accent: #5661f5;--gb-btn: linear-gradient(120deg, #1b1c1e 0%, #000000 100%);--gb-btn-ink: #ffffff;--gb-wordmark: linear-gradient(120deg, #1b1c1e 0%, #000000 100%);--gb-hover: #f7f7f5;--gb-hover-line: #b9bbc0;--gb-err-bg: #fee2e2;--gb-err-ink: #b91c1c;--gb-err-line: transparent;--gb-card-shadow: 0 24px 64px rgba(15, 23, 42, .08);--gb-mark-bg: #0b0b0c;--gb-facet: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 120'%3E%3Cg fill='none' stroke='%23ffffff' stroke-width='0.45' stroke-linejoin='round'%3E%3Cpath d='M26,14 74,14 96,50 50,110 4,50Z'/%3E%3Cpath d='M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110'/%3E%3C/g%3E%3C/svg%3E");--gb-page: radial-gradient(1200px 600px at 20% 10%, rgba(27, 28, 30, .05), transparent 60%), radial-gradient(1000px 500px at 80% 90%, rgba(86, 97, 245, .06), transparent 60%), #fafafb;--gb-page-flat: #ffffff;--gb-panel-bg: radial-gradient(900px 520px at 78% 12%, rgba(86, 97, 245, .26), transparent 62%), radial-gradient(700px 480px at 12% 96%, rgba(86, 97, 245, .12), transparent 60%), #0b0b0c;--gb-panel-ink: #f4f4f2;--gb-panel-muted: rgba(244, 244, 242, .72);--gb-panel-eyebrow: #8a93ff;--gb-panel-facet-o: .05;--gb-panel-grid-bg: linear-gradient(160deg, #1b1c22 0%, #0b0b0c 72%);--gb-panel-grid-line: rgba(138, 147, 255, .1);--gb-panel-grid-size: 34px;--gb-panel-grid-fade: radial-gradient(90% 80% at 30% 20%, #000, transparent 75%);color-scheme:light dark;position:fixed;top:var(--vvt, 0px);left:0;right:0;height:100vh;height:var(--vvh, 100dvh);container:gb-auth / size;overflow-y:auto;background:var(--gb-page);color:var(--gb-ink);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif}.gb-auth-split{display:grid;grid-template-columns:1fr;min-height:100%}.gb-auth-stage{box-sizing:border-box;display:flex;min-height:100%;padding:clamp(12px,3cqh,24px) clamp(16px,4vw,24px)}.gb-auth-panel-in{position:relative;box-sizing:border-box;width:100%;max-width:520px;padding:48px clamp(32px,5vw,64px)}.gb-auth-eyebrow{margin:0 0 20px;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:12px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--gb-panel-eyebrow)}.gb-auth-panel-h{margin:0 0 16px;font-size:clamp(28px,3.2vw,40px);line-height:1.15;letter-spacing:-.02em;font-weight:700;color:var(--gb-panel-ink)}.gb-auth-panel-p{margin:0;font-size:15px;line-height:1.6;color:var(--gb-panel-muted)}.gb-auth-panel{display:none}@container gb-auth (min-width: 900px) and (min-height: 520px){.gb-auth-split{grid-template-columns:minmax(420px,1fr) minmax(0,1.05fr)}.gb-auth-split .gb-auth-stage{background:var(--gb-surface)}.gb-auth-split .gb-auth-card{border:none;box-shadow:none;max-width:400px}.gb-auth-panel{display:flex;align-items:center;position:relative;overflow:hidden;background:var(--gb-panel-bg);color:var(--gb-panel-ink)}.gb-auth-panel:before{content:"";position:absolute;inset:-45% -38% auto auto;width:132%;aspect-ratio:1;background-image:var(--gb-facet);background-size:contain;background-repeat:no-repeat;opacity:var(--gb-panel-facet-o);pointer-events:none}.gb-auth-split .gb-auth-panel--grid{background:var(--gb-panel-grid-bg)}.gb-auth-panel.gb-auth-panel--grid:before{inset:0;width:auto;aspect-ratio:auto;background-image:linear-gradient(var(--gb-panel-grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--gb-panel-grid-line) 1px,transparent 1px);background-size:var(--gb-panel-grid-size) var(--gb-panel-grid-size);background-repeat:repeat;opacity:1;-webkit-mask-image:var(--gb-panel-grid-fade);mask-image:var(--gb-panel-grid-fade)}.gb-auth-panel.gb-auth-panel--none:before{content:none}}.gb-auth-card{width:100%;max-width:380px;margin:auto;background:var(--gb-surface);border:1px solid var(--gb-line);border-radius:14px;padding:clamp(18px,3.5cqh,28px) 28px;box-shadow:var(--gb-card-shadow)}.gb-auth-header{text-align:center;margin-bottom:clamp(12px,3cqh,24px)}.gb-auth-mark{display:inline-flex;margin-bottom:clamp(6px,calc(6px + (100cqh - 480px)*.075),12px)}.gb-auth-mark svg,.gb-auth-mark img{width:clamp(32px,calc(32px + (100cqh - 480px)*.3),56px);height:clamp(32px,calc(32px + (100cqh - 480px)*.3),56px)}@container gb-auth (max-height: 479.98px){.gb-auth-mark{display:none}}.gb-auth-header h1{font-size:clamp(19px,2.75cqh,22px);font-weight:700;margin:0 0 clamp(0px,calc((100cqh - 560px)*.1),4px);background:var(--gb-wordmark);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.gb-auth-header p{margin:0;font-size:13px;color:var(--gb-muted)}@container gb-auth (max-height: 599.98px){.gb-auth-header p{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}}.gb-auth-form{display:flex;flex-direction:column;gap:clamp(8px,1.5cqh,12px)}.gb-auth-form label{display:flex;flex-direction:column;gap:clamp(4px,.75cqh,6px);font-size:12px;font-weight:500;color:var(--gb-ink)}.gb-auth-input{box-sizing:border-box;height:32px;min-height:44px;min-width:0;padding:0 12px;font:inherit;font-size:13px;line-height:20px;border:1px solid;border-radius:8px}@media (max-width: 47.9975em){.gb-auth-input{font-size:16px}}@media (pointer: coarse){.gb-auth-input{height:auto}}.gb-auth-form input{width:100%;box-sizing:border-box;background-color:var(--gb-field-bg);border-color:var(--gb-field-line);color:var(--gb-ink)}.gb-auth-form input::placeholder{color:var(--gb-placeholder)}.gb-auth-form input:hover:not(:disabled){background-color:var(--gb-field-hover);border-color:var(--gb-hover-line)}.gb-auth-form input:focus-visible{outline:2px solid var(--gb-ink);outline-offset:1px;border-color:var(--gb-ink)}.gb-auth-field{display:flex;flex-direction:column;gap:clamp(4px,.75cqh,6px);font-size:12px;font-weight:500;color:var(--gb-ink)}.gb-auth-pw{position:relative;display:flex}.gb-auth-pw input{width:100%;padding-right:48px}.gb-auth-pw-toggle{min-height:44px;min-width:44px;position:absolute;right:0;top:0;bottom:0;display:inline-flex;align-items:center;justify-content:center;padding:0;margin:0;background:none;border:none;border-radius:8px;cursor:pointer;color:var(--gb-muted);transition:none}.gb-auth-pw-toggle:hover{color:var(--gb-ink)}.gb-auth-pw-toggle:focus-visible{outline:2px solid var(--gemboss-primary, #1b1c1e);outline-offset:-2px}.gb-auth-error{background:var(--gb-err-bg);border:1px solid var(--gb-err-line);color:var(--gb-err-ink);padding:8px 10px;font-size:13px;border-radius:6px}.gb-auth-form>.gb-auth-submit{margin-top:clamp(2px,.75cqh,6px);min-height:44px;font-size:14px;font-weight:600;border:none;border-radius:8px;background:var(--gb-btn);color:var(--gb-btn-ink);cursor:pointer;font-family:inherit;transition:opacity .15s ease,transform .15s ease}.gb-auth-form>.gb-auth-submit:hover:not(:disabled){transform:translateY(-1px)}.gb-auth-form>.gb-auth-submit:disabled{opacity:.6;cursor:not-allowed}.gb-auth-sso-btn{display:flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:0 16px;border:1px solid var(--gb-field-line);border-radius:8px;background:var(--gb-surface);color:var(--gb-ink);font-size:14px;font-weight:600;text-decoration:none;transition:background .15s,border-color .15s}.gb-auth-sso-btn:hover{background:var(--gb-hover);border-color:var(--gb-hover-line)}.gb-auth-sso-btn:focus-visible{outline:2px solid var(--gb-ink);outline-offset:1px}.gb-auth-sso-why{margin:8px 0 0;font-size:12px;line-height:1.5;color:var(--gb-muted);text-align:center}.gb-auth-sso .gb-auth-or{display:flex;align-items:center;gap:12px;margin:clamp(8px,1.75cqh,14px) 0;color:var(--gb-muted);font-size:12px}.gb-auth-sso .gb-auth-or:before,.gb-auth-sso .gb-auth-or:after{content:"";flex:1;height:1px;background:var(--gb-line)}.gb-auth-foot{margin:clamp(10px,2cqh,16px) 0 0;font-size:13px;color:var(--gb-muted);text-align:center;line-height:1.5}.gb-auth-foot a{display:inline-block;min-height:24px;line-height:24px;padding:0 2px;color:var(--gb-accent);font-weight:600;text-decoration:none}.gb-auth-foot a:hover,.gb-auth-foot a:focus-visible{text-decoration:underline}.gb-auth-card .gb-auth-help{margin:clamp(8px,1.75cqh,14px) 0 0;font-size:12px;color:var(--gb-muted);text-align:center;line-height:1.5}.gb-auth-card .gb-auth-help a{display:inline-block;min-height:24px;line-height:24px;color:var(--gb-accent);font-weight:600;text-decoration:none}.gb-auth-card .gb-auth-help a:hover,.gb-auth-card .gb-auth-help a:focus-visible{text-decoration:underline}@media (max-width: 480px){.gb-auth{background:var(--gb-page-flat)}.gb-auth-stage{padding:max(clamp(10px,3cqh,20px),env(safe-area-inset-top)) max(16px,env(safe-area-inset-right)) max(clamp(10px,3cqh,20px),env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left))}.gb-auth-card{border:none;border-radius:0;box-shadow:none;padding:0}}@container gb-auth (max-height: 399.98px){.gb-auth-header{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}}@container gb-auth (max-height: 359.98px){.gb-auth-card .gb-auth-help,.gb-auth-foot{display:none}.gb-auth-stage{padding-top:max(8px,env(safe-area-inset-top));padding-bottom:max(8px,env(safe-area-inset-bottom))}}@container gb-auth (max-height: 359.98px) and (min-width: 481px){.gb-auth-card{padding-top:14px;padding-bottom:14px}}@media (prefers-color-scheme: dark){:root:not([data-theme=light]) .gb-auth{--gb-surface: #141517;--gb-ink: #f4f4f3;--gb-muted: #969c97;--gb-line: rgba(255, 255, 255, .11);--gb-field-line: rgba(255, 255, 255, .17);--gb-field-bg: #1b1c1f;--gb-field-hover: #212226;--gb-placeholder: #7f838c;--gb-accent: #8a93ff;--gb-btn: linear-gradient(120deg, #f4f4f3 0%, #dfe0e4 100%);--gb-btn-ink: #0b0b0c;--gb-wordmark: linear-gradient(120deg, #f4f4f3 0%, #c9cbd2 100%);--gb-hover: rgba(255, 255, 255, .06);--gb-hover-line: rgba(255, 255, 255, .28);--gb-err-bg: rgba(215, 44, 13, .16);--gb-err-ink: #ff9d88;--gb-err-line: rgba(245, 120, 95, .4);--gb-card-shadow: 0 24px 64px rgba(0, 0, 0, .55);--gb-mark-bg: #202127;--gb-page: radial-gradient(1200px 600px at 20% 10%, rgba(255, 255, 255, .05), transparent 60%), radial-gradient(1000px 500px at 80% 90%, rgba(138, 147, 255, .1), transparent 60%), #0b0b0c;--gb-page-flat: #0b0b0c}}:root[data-theme=dark] .gb-auth{--gb-surface: #141517;--gb-ink: #f4f4f3;--gb-muted: #969c97;--gb-line: rgba(255, 255, 255, .11);--gb-field-line: rgba(255, 255, 255, .17);--gb-field-bg: #1b1c1f;--gb-field-hover: #212226;--gb-placeholder: #7f838c;--gb-accent: #8a93ff;--gb-btn: linear-gradient(120deg, #f4f4f3 0%, #dfe0e4 100%);--gb-btn-ink: #0b0b0c;--gb-wordmark: linear-gradient(120deg, #f4f4f3 0%, #c9cbd2 100%);--gb-hover: rgba(255, 255, 255, .06);--gb-hover-line: rgba(255, 255, 255, .28);--gb-err-bg: rgba(215, 44, 13, .16);--gb-err-ink: #ff9d88;--gb-err-line: rgba(245, 120, 95, .4);--gb-card-shadow: 0 24px 64px rgba(0, 0, 0, .55);--gb-mark-bg: #202127;--gb-page: radial-gradient(1200px 600px at 20% 10%, rgba(255, 255, 255, .05), transparent 60%), radial-gradient(1000px 500px at 80% 90%, rgba(138, 147, 255, .1), transparent 60%), #0b0b0c;--gb-page-flat: #0b0b0c}
|
package/src/auth.tsx
CHANGED
|
@@ -37,7 +37,7 @@ const BACKDROP: Record<AuthBackdrop, string> = { facet: "", grid: "gb-auth-panel
|
|
|
37
37
|
* The whole screen. `children` is everything inside the card below the heading: the form, then any
|
|
38
38
|
* lines under it.
|
|
39
39
|
*/
|
|
40
|
-
export function AuthLayout({ brand, tagline, mark, panel, backdrop = "facet", className, children }: {
|
|
40
|
+
export function AuthLayout({ brand, tagline, mark, panel, backdrop = "facet", art, className, children }: {
|
|
41
41
|
/** The page heading (h1) in the card. */
|
|
42
42
|
brand: React.ReactNode;
|
|
43
43
|
/** One line under the heading; the first thing shed on a short screen. */
|
|
@@ -48,6 +48,9 @@ export function AuthLayout({ brand, tagline, mark, panel, backdrop = "facet", cl
|
|
|
48
48
|
panel?: { eyebrow?: React.ReactNode; heading: React.ReactNode; body?: React.ReactNode };
|
|
49
49
|
/** The panel's ground pattern; see `AuthBackdrop`. Only drawn where the panel is (900 wide AND 520 tall). */
|
|
50
50
|
backdrop?: AuthBackdrop;
|
|
51
|
+
/** Drawn on the panel, under its words (16/10/2026): `AuthPageField`, or anything else that is decoration. Pair it
|
|
52
|
+
* with `backdrop="none"`. Only drawn where the panel is. */
|
|
53
|
+
art?: React.ReactNode;
|
|
51
54
|
className?: string;
|
|
52
55
|
children?: React.ReactNode;
|
|
53
56
|
}) {
|
|
@@ -105,6 +108,7 @@ export function AuthLayout({ brand, tagline, mark, panel, backdrop = "facet", cl
|
|
|
105
108
|
them. Nothing in it is focusable — it is decoration beside the form, not a second path. */}
|
|
106
109
|
{panel ? (
|
|
107
110
|
<aside className={cx("gb-auth-panel", BACKDROP[backdrop])}>
|
|
111
|
+
{art}
|
|
108
112
|
<div className="gb-auth-panel-in">
|
|
109
113
|
{panel.eyebrow ? <p className="gb-auth-eyebrow">{panel.eyebrow}</p> : null}
|
|
110
114
|
<h2 className="gb-auth-panel-h">{panel.heading}</h2>
|
|
@@ -117,6 +121,65 @@ export function AuthLayout({ brand, tagline, mark, panel, backdrop = "facet", cl
|
|
|
117
121
|
);
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
/**
|
|
125
|
+
* "Continue with GemBoss" (16/10/2026): the internal tools' second door, above their password form (Chris, 16/10/2026: "có
|
|
126
|
+
* cả continue với gemboss hoặc nhập mật khẩu"). Protocol: gemboss docs/fleet-sso.md. It links to GemBoss's sign-in
|
|
127
|
+
* door for this `service`; GemBoss sends a person who is signed in there and on the internal list straight back, with a
|
|
128
|
+
* one-use token the tool redeems at its own route. Below it, "or" and the form the tool always had.
|
|
129
|
+
*
|
|
130
|
+
* Once a browser has come in through GemBoss the tool sets `gb_sso=1`, and from then on this page continues by itself,
|
|
131
|
+
* so a person still signed in to GemBoss never sees a form. It does not when the tool sent a reason back (`?sso=…`:
|
|
132
|
+
* that sign-in did not work, so the page stays and says why) or `?password=1` (the person asked for the form).
|
|
133
|
+
*/
|
|
134
|
+
export const GEMBOSS_ISSUER = "https://admin.gemboss.ai";
|
|
135
|
+
const SSO_REASONS: Record<string, string> = {
|
|
136
|
+
not_member: "Your GemBoss account does not have access here. Use the password.",
|
|
137
|
+
invalid: "That sign-in did not go through. Try again, or use the password.",
|
|
138
|
+
off: "Signing in with GemBoss is not available here yet. Use the password.",
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/** The GemBoss door for one tool, with the path on the tool to come back to. */
|
|
142
|
+
export function gemBossSignInUrl(service: string, next = "/", issuer = GEMBOSS_ISSUER): string {
|
|
143
|
+
return `${issuer}/api/auth/sso/authorize?service=${encodeURIComponent(service)}&next=${encodeURIComponent(next)}`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Whether this visit should go to GemBoss without a click: the browser came in that way before, and nothing says stay. */
|
|
147
|
+
export function continuesByItself(cookie: string, search: string): boolean {
|
|
148
|
+
const q = new URLSearchParams(search);
|
|
149
|
+
if (q.has("sso") || q.get("password") === "1") return false;
|
|
150
|
+
return /(?:^|;\s*)gb_sso=1(?:;|$)/.test(cookie);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function AuthContinueWithGemBoss({ service, next = "/", issuer = GEMBOSS_ISSUER }: {
|
|
154
|
+
/** This tool's id in GemBoss's list of tools that redeem (FLEET_SSO_SERVICES). */
|
|
155
|
+
service: string;
|
|
156
|
+
/** Where on this tool to land afterwards: a path. */
|
|
157
|
+
next?: string;
|
|
158
|
+
issuer?: string;
|
|
159
|
+
}) {
|
|
160
|
+
const href = gemBossSignInUrl(service, next, issuer);
|
|
161
|
+
const [going, setGoing] = useState(false);
|
|
162
|
+
const [reason, setReason] = useState<string | null>(null);
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
const r = new URLSearchParams(window.location.search).get("sso");
|
|
165
|
+
if (r) setReason(SSO_REASONS[r] ?? SSO_REASONS.invalid);
|
|
166
|
+
if (continuesByItself(document.cookie, window.location.search)) {
|
|
167
|
+
setGoing(true);
|
|
168
|
+
window.location.replace(href);
|
|
169
|
+
}
|
|
170
|
+
}, [href]);
|
|
171
|
+
return (
|
|
172
|
+
<div className="gb-auth-sso">
|
|
173
|
+
<a className="gb-auth-sso-btn" href={href} onClick={() => setGoing(true)}>
|
|
174
|
+
<GemMark size={18} tile="var(--gb-mark-bg)" />
|
|
175
|
+
{going ? "Continuing with GemBoss…" : "Continue with GemBoss"}
|
|
176
|
+
</a>
|
|
177
|
+
{reason ? <p className="gb-auth-sso-why" role="status">{reason}</p> : null}
|
|
178
|
+
<p className="gb-auth-or">or use the password</p>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
120
183
|
export function AuthForm({ className, children, ...rest }: React.FormHTMLAttributes<HTMLFormElement>) {
|
|
121
184
|
return <form className={cx("gb-auth-form", className)} {...rest}>{children}</form>;
|
|
122
185
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The lockup's gem: the logo file's paths and stroke weights at the wordmark's rendered size (a 32×40 box drawn 26px
|
|
3
|
+
* tall). Its own module (0.40.0) so `BrandLockup` (topbar, app size) and `SiteBrand` (public site size) draw the same
|
|
4
|
+
* gem, and `SiteBrand` does not import brand.tsx: that module brings the ServiceSwitcher, which a public page must not
|
|
5
|
+
* carry. No stylesheet: `.gb-brand-gem` is in the core (app-shell.css).
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
|
|
9
|
+
export function BrandGem() {
|
|
10
|
+
return (
|
|
11
|
+
<svg className="gb-brand-gem" viewBox="0 0 32 40" width="20.8" height="26" aria-hidden="true" focusable="false">
|
|
12
|
+
<g transform="translate(0,0.8) scale(0.31)">
|
|
13
|
+
<polygon points="26,14 74,14 66,50 34,50" fill="#5661F5" fillOpacity="0.92" />
|
|
14
|
+
<path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" strokeWidth="8.75" strokeLinejoin="round" />
|
|
15
|
+
<path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" strokeWidth="5.45" strokeLinejoin="round" />
|
|
16
|
+
</g>
|
|
17
|
+
</svg>
|
|
18
|
+
);
|
|
19
|
+
}
|
package/src/brand.tsx
CHANGED
|
@@ -13,9 +13,12 @@
|
|
|
13
13
|
* `switcher` (0.36.0): the ServiceSwitcher button right after the logo, which opens every other Gem service. The logo
|
|
14
14
|
* still goes to `href`, as without it. Without the prop the lockup renders exactly as before. The import below brings
|
|
15
15
|
* service-switcher.css with this module whether or not a surface passes it (README "ServiceSwitcher").
|
|
16
|
+
*
|
|
17
|
+
* The gem lives in brand-gem.tsx (0.40.0), shared with `SiteBrand` (the public site's lockup, site.tsx).
|
|
16
18
|
*/
|
|
17
19
|
import React from "react";
|
|
18
20
|
import { ServiceSwitcher, type ServiceSwitcherProps } from "./service-switcher";
|
|
21
|
+
import { BrandGem } from "./brand-gem";
|
|
19
22
|
|
|
20
23
|
type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>;
|
|
21
24
|
|
|
@@ -23,13 +26,7 @@ export function BrandLockup({ name, href = "/", label, className, switcher, ...r
|
|
|
23
26
|
{ name: string; label?: string; switcher?: Omit<ServiceSwitcherProps, "className"> } & AnchorProps) {
|
|
24
27
|
const link = (
|
|
25
28
|
<a className={className ? `gb-brand ${className}` : "gb-brand"} href={href} aria-label={label ?? `${name} home`} {...rest}>
|
|
26
|
-
<
|
|
27
|
-
<g transform="translate(0,0.8) scale(0.31)">
|
|
28
|
-
<polygon points="26,14 74,14 66,50 34,50" fill="#5661F5" fillOpacity="0.92" />
|
|
29
|
-
<path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" strokeWidth="8.75" strokeLinejoin="round" />
|
|
30
|
-
<path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" strokeWidth="5.45" strokeLinejoin="round" />
|
|
31
|
-
</g>
|
|
32
|
-
</svg>
|
|
29
|
+
<BrandGem />
|
|
33
30
|
<span className="gb-brand-name">{name}</span>
|
|
34
31
|
</a>
|
|
35
32
|
);
|
package/src/chat-plain.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
(function(O){"use strict";var
|
|
1
|
+
(function(O){"use strict";var w=O.gembossUI;if(!w){O.console&&O.console.error("@gemboss/ui chat-plain.js: load kit-plain.js first (it defines gembossUI)");return}if(w.chat)return;var b={thread:"Conversation",message:"Message",send:"Send",stop:"Stop",attach:"Attach a file",copy:"Copy",copied:"Copied",copyCommand:"Copy command",originalError:"Original error",stopped:"Stopped",steps:"What it is doing",thought:"Thought",thinking:"Thinking",working:"Working",showLess:"Show less",showAll:"Show all {n} lines",openImage:"Open image"},A="http://www.w3.org/2000/svg",k=function(e){return typeof Node<"u"&&e instanceof Node};function l(e,n,r){var t=document.createElement(e);if(n)for(var a in n){var i=n[a];i==null||i===!1||(a==="text"?t.textContent=String(i):a==="className"?t.className=i:t.setAttribute(a,i===!0?"":String(i)))}return R(t,r),t}function R(e,n){return n==null||n===!1?e:Array.isArray(n)?(n.forEach(function(r){R(e,r)}),e):(e.appendChild(k(n)?n:document.createTextNode(String(n))),e)}function M(e,n,r){var t=document.createElementNS(A,"svg");if(t.setAttribute("viewBox",e),t.setAttribute("aria-hidden","true"),t.setAttribute("focusable","false"),n.forEach(function(i){var s=document.createElementNS(A,i.tag||"path");for(var o in i)o!=="tag"&&s.setAttribute(o,i[o]);t.appendChild(s)}),r)for(var a in r)t.setAttribute(a,r[a]);return t}var ae=[{host:"admin.gemboss.ai",path:"/api/gemboss/market-image/"},{host:"gemmedia-api-production.up.railway.app",path:"/"},{host:"media.gemboss.ai",path:"/v1/theme-media/assets/"},{host:"gemmedia.ops.gemboss.ai",path:"/v1/theme-media/assets/"},{host:"gemmedia.gemboss.ai",path:"/v1/theme-media/assets/"},{host:"cdn.shopify.com",path:"/"}],q=/[\u0000-\u001f\u007f]/g;function C(e){var n=String(e).replace(q,"").trim();return n?n.charAt(0)==="/"?n.charAt(1)==="/"||n.charAt(1)==="\\"?null:{href:n,internal:!0}:/^(https?:\/\/|mailto:)/i.test(n)?{href:n,internal:!1}:null:null}var D="https://same-origin.invalid",ie=/^\/[^/]+(\/[^/]+)*\/$/;function H(e,n){var r=String(e).replace(q,"").trim(),t;if(r.charAt(0)==="/"){var a=(n||[]).filter(function(i){return!i.host&&ie.test(i.path)});if(!a.length||r.charAt(1)==="/"||r.indexOf("\\")>=0||/%(2f|5c|25)|;/i.test(r))return null;try{t=new URL(r,D)}catch{return null}return t.origin!==D||/\.\.|%2e/i.test(t.pathname)?null:a.some(function(i){return t.pathname.indexOf(i.path)===0})?t.href.slice(D.length):null}try{t=new URL(r)}catch{return null}return t.protocol!=="https:"||t.username||t.password||t.port?null:ae.concat(n||[]).some(function(i){return i.host===t.hostname&&t.pathname.indexOf(i.path)===0})?t.href:null}var T=function(e,n){return e.split(n).length-1};function P(e,n){var r=e.length,t,a,i,s,o=T(e,"("),d=T(e,")"),f=T(e,"["),c=T(e,"]");for(n=n!==!1;n&&r>0;)t=e.charAt(r-1),/[.,:;!?_~'"]/.test(t)?r-=1:t===")"&&o<d?(r-=1,d-=1):t==="]"&&f<c?(r-=1,c-=1):n=!1;if(s=e.slice(0,r),a=C(s),!a||a.internal)return null;try{i=new URL(a.href)}catch{return null}return!/^https?:$/.test(i.protocol)||!i.hostname?null:{href:a.href,rest:e.slice(r)}}var le=/(`([^`]+)`)|(\*\*([^*]+)\*\*)|(\*([^*\n]+)\*)|(!\[([^\]]*)\]\(([^)\s]+)\))|(\[([^\]]+)\]\(([^)\s]+)\))|(<(https?:\/\/[^\s<>]+)>)|(https?:\/\/[^\s<>`"*\u2000-\u206f\u3000-\u9fff\uff00-\uffef]+)/gi;function F(e,n){n=n||{};for(var r=[],t=function(m){if(m){var v=r[r.length-1];v&&v.t==="text"?v.text+=m:r.push({t:"text",text:m})}},a=new RegExp(le.source,"gi"),i=0,s;(s=a.exec(e))!==null;){if(t(e.slice(i,s.index)),s[2]!=null)r.push({t:"code",text:s[2]});else if(s[4]!=null)r.push({t:"bold",text:s[4]});else if(s[6]!=null)r.push({t:"italic",text:s[6]});else if(s[7]!=null){var o=H(s[9],n.imageHosts),d=s[8].trim();if(o)r.push({t:"image",alt:d,src:o});else{var f=C(s[9]);f&&!f.internal?r.push({t:"link",label:d||"image",href:f.href,internal:!1}):t(d)}}else if(s[10]!=null){var c=s[12],p=n.ownLink&&n.ownLink(c)?{href:c,internal:!0,own:!0}:C(c);p?r.push({t:"link",label:s[11],href:p.href,internal:p.internal}):t(s[11])}else{var h=s[15]!=null&&s.index>0&&/[a-z\d]/i.test(e.charAt(s.index-1)),g=h?null:s[14]!=null?P(s[14],!1):P(s[15]);g?(r.push({t:"link",label:g.href,href:g.href,internal:!1}),t(g.rest)):t(s[0])}i=s.index+s[0].length}return t(e.slice(i)),r}var $=function(e){return/^\s*\|.*\|\s*$/.test(e)},se=function(e){return/^\s*\|?[\s:-]*-[-\s:|]*\|?\s*$/.test(e)&&e.indexOf("-")>=0},U=function(e){return e.trim().replace(/^\||\|$/g,"").split("|").map(function(n){return n.trim()})};function K(e){for(var n=String(e??"").split(`
|
|
2
2
|
`),r=[],t=0,a=[],i=function(){a.length&&r.push({t:"para",lines:a.slice()}),a.length=0};t<n.length;){var s=n[t],o;if(o=/^\s*```([\w#+.-]*)\s*$/.exec(s)){i();var d=[];for(t+=1;t<n.length&&!/^\s*```\s*$/.test(n[t]);)d.push(n[t]),t+=1;t+=1,r.push({t:"code",lang:o[1]||"",body:d.join(`
|
|
3
3
|
`)});continue}if(o=/^(#{1,6})\s+(.*)$/.exec(s)){i(),o[2].trim()&&r.push({t:"heading",level:Math.min(o[1].length,3),text:o[2].trim()}),t+=1;continue}if(/^\s*(---|\*\*\*|___)\s*$/.test(s)){i(),r.push({t:"rule"}),t+=1;continue}if($(s)&&t+1<n.length&&se(n[t+1])){i();var f=U(s),c=[];for(t+=2;t<n.length&&$(n[t]);)c.push(U(n[t])),t+=1;r.push({t:"table",head:f,rows:c});continue}if(o=/^\s*([-*+]|\d+[.)])\s+(.*)$/.exec(s)){i();for(var p=/\d/.test(o[1]),h=p?parseInt(o[1],10):void 0,g=[];t<n.length;){var m=/^\s*([-*+]|\d+[.)])\s+(.*)$/.exec(n[t]);if(!m||/\d/.test(m[1])!==p)break;g.push(m[2]),t+=1}var v={t:"list",ordered:p,items:g};h!==void 0&&h!==1&&(v.start=h),r.push(v);continue}if(/^\s*>\s?/.test(s)){i();for(var y=[];t<n.length&&/^\s*>\s?/.test(n[t]);)y.push(n[t].replace(/^\s*>\s?/,"")),t+=1;r.push({t:"quote",lines:y});continue}if(s.trim()===""){i(),t+=1;continue}a.push(s),t+=1}return i(),r}var j=12;function x(e,n,r){return F(e,n).map(function(t){if(t.t==="text")return document.createTextNode(t.text);if(t.t==="code")return l("code",{className:"gemboss-md-code",text:t.text});if(t.t==="bold")return l("strong",0,x(t.text,n,r));if(t.t==="italic")return l("em",0,x(t.text,n,r));if(t.t==="link"){if(n.ownLink&&n.ownLink(t.href)&&n.renderLink)return n.renderLink(t.href,t.label)||document.createTextNode(t.label);var a=l("a",{className:"gemboss-ec-md-link",href:t.href,target:t.internal?null:"_blank",rel:t.internal?null:"noopener noreferrer",text:t.label});return t.internal&&n.onNavigate&&a.addEventListener("click",function(i){i.metaKey||i.ctrlKey||i.shiftKey||(i.preventDefault(),n.onNavigate(t.href))}),a}return l("a",{className:"gemboss-md-imglink",href:t.src,target:"_blank",rel:"noopener noreferrer","aria-label":t.alt?null:r.openImage},l("img",{className:"gemboss-md-img",src:t.src,alt:t.alt,loading:"lazy",referrerpolicy:"no-referrer"}))})}function z(e,n,r){var t=[];return e.forEach(function(a,i){t=t.concat(x(a,n,r)),i<e.length-1&&t.push(l("br"))}),t}function oe(e,n){var r=e.body.split(`
|
|
4
4
|
`),t=r.length>j,a=!1,i=l("code",{text:t?r.slice(0,j).join(`
|
|
5
5
|
`):e.body}),s=l("div",{className:"gemboss-md-codeblock"},[e.lang?l("span",{className:"gemboss-md-codelang",text:e.lang}):null,l("pre",null,i)]);if(t){var o=l("button",{type:"button",className:"gemboss-md-codemore","aria-expanded":"false",text:n.showAll.replace("{n}",String(r.length))});o.addEventListener("click",function(){a=!a,i.textContent=a?e.body:r.slice(0,j).join(`
|
|
6
|
-
`),o.textContent=a?n.showLess:n.showAll.replace("{n}",String(r.length)),o.setAttribute("aria-expanded",String(a))}),s.appendChild(o)}return s}var ue={1:"h3",2:"h4",3:"h5"};function G(e,n,r){n=n||{};var t=Object.assign({},b,r||n.labels||{}),a=document.createDocumentFragment();return K(e).forEach(function(i){var s;i.t==="heading"?s=l(ue[i.level],{className:"gemboss-md-h gemboss-md-h"+i.level},x(i.text,n,t)):i.t==="code"?s=oe(i,t):i.t==="table"?s=l("div",{className:"gemboss-md-tablewrap"},l("table",{className:"gemboss-md-table"},[l("thead",null,l("tr",null,i.head.map(function(o){return l("th",null,x(o,n,t))}))),l("tbody",null,i.rows.map(function(o){return l("tr",null,i.head.map(function(d,f){return l("td",null,x(o[f]||"",n,t))}))}))])):i.t==="list"?s=l(i.ordered?"ol":"ul",{className:"gemboss-md-list",start:i.ordered&&i.start!==void 0?i.start:null},i.items.map(function(o){return l("li",null,x(o,n,t))})):i.t==="quote"?s=l("blockquote",{className:"gemboss-md-quote"},z(i.lines,n,t)):i.t==="rule"?s=l("hr",{className:"gemboss-md-rule"}):s=l("p",{className:"gemboss-md-p"},z(i.lines,n,t)),a.appendChild(s)}),a}function ce(){for(var e=12,n=1.7,r=.5,t=9.6,a=[],i=function(g){return Math.round(g*100)/100},s=function(g,m){return i(g)+" "+i(m)},o=0;o<8;o++){var d=o*Math.PI/4,f=Math.cos(d),c=Math.sin(d),p=-c,h=f;a.push(["M"+s(e+p*n,e+h*n),"L"+s(e+f*t+p*r,e+c*t+h*r),"Q"+s(e+f*(t+.6),e+c*(t+.6))+" "+s(e+f*t-p*r,e+c*t-h*r),"L"+s(e-p*n,e-h*n),"Z"].join(""))}return a.join(" ")}var V={claude:{path:ce(),label:"Claude Code"},codex:{path:"M12 2.6 20.1 7.3V16.7L12 21.4 3.9 16.7V7.3ZM12 6.9 7.6 9.45v5.1L12 17.1l4.4-2.55v-5.1Z",label:"Codex"}};function Y(e,n){var r=Object.prototype.hasOwnProperty.call(V,e)?V[e]:null;if(!r)return null;n=n||{};var t=document.createElementNS(A,"svg");if(t.setAttribute("class",n.className||"gemboss-engine-mark"),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","currentColor"),n.decorative)t.setAttribute("aria-hidden","true"),t.setAttribute("focusable","false");else{t.setAttribute("role","img"),t.setAttribute("aria-label",n.title||r.label);var a=document.createElementNS(A,"title");a.textContent=n.title||r.label,t.appendChild(a)}var i=document.createElementNS(A,"path");return i.setAttribute("d",r.path),i.setAttribute("fill-rule","nonzero"),t.appendChild(i),t}function Z(e,n){var r=Object.assign({},b,n||{});return!e||!e.length?null:l("ul",{className:"gemboss-narr","aria-label":r.steps},e.map(function(t){var a=t.state||"ok",i;if(t.kind==="thinking")return i=[l("span",{className:"gemboss-narr-dot","aria-hidden":"true",text:"\u2733"}),a==="running"||!t.detail?l("span",{className:"gemboss-narr-text gemboss-narr-thinking-live"},t.label||r.thinking):l("details",{className:"gemboss-narr-thinking"},[l("summary",null,t.label||r.thought),l("div",{className:"gemboss-narr-thinking-body",text:t.detail})])],l("li",{className:"gemboss-narr-row thinking "+a},i);if(t.kind==="steer")return l("li",{className:"gemboss-narr-row steer "+a},[l("span",{className:"gemboss-narr-dot","aria-hidden":"true",text:"\u276F"}),l("span",{className:"gemboss-narr-text gemboss-narr-steer"},t.label)]);if(t.kind==="trace")return l("li",{className:"gemboss-narr-row trace "+a,title:t.error||null},[l("span",{className:"gemboss-narr-dot","aria-hidden":"true",text:a==="failed"?"\u2715":"\u23FA"}),t.detail?l("details",{className:"gemboss-narr-trace"},[l("summary",null,l("code",null,t.label)),l("div",{className:"gemboss-narr-trace-body"},l("code",{text:t.detail}))]):l("code",{className:"gemboss-narr-text gemboss-narr-trace-call"},t.label)]);var s=t.engine?Y(t.engine):null;return l("li",{className:"gemboss-narr-row "+a,title:t.error||null},[s?l("span",{className:"gemboss-narr-dot gemboss-narr-dot-engine"},s):l("span",{className:"gemboss-narr-dot","aria-hidden":"true",text:a==="ok"?"\u2713":a==="failed"?"\u2715":"\xB7"}),l("span",{className:"gemboss-narr-text"},[t.label,t.target?l("span",{className:"gb-chat-target"},[" ",t.target]):null])])}))}function J(e,n){e=e||{};var r=Object.assign({},b,n||{}),t=e.verbs&&e.verbs.length?e.verbs:[r.working],a=l("span",{className:"gemboss-ec-pending-verb","aria-hidden":"true",text:t[0]+"\u2026"}),i=l("span",{className:"gemboss-ec-pending-meta","aria-hidden":"true",text:"0s"}),s=l("div",{className:"gemboss-ec-pending",role:"status"},[l("span",{className:"gemboss-assistant-sronly",text:e.label||t[0]}),l("span",{className:"gemboss-ec-pending-star","aria-hidden":"true",text:"\u2733"}),e.compact?null:a,i]),o=e.since||Date.now(),d=!1,f=-1,c=function(){var h=Math.max(0,Math.floor((Date.now()-o)/1e3));i.textContent=(e.tokens>0?e.tokens+" token"+(e.tokens===1?"":"s")+" \xB7 ":"")+h+"s";var g=t.length>1?Math.floor(h/2)%t.length:0;if(g!==f&&!e.compact){f=g;var m=l("span",{className:"gemboss-ec-pending-verb","aria-hidden":"true",text:t[f]+"\u2026"});a.replaceWith(m),a=m}};c();var p=setInterval(function(){if(s.isConnected)d=!0;else if(d||Date.now()-o>6e5){clearInterval(p);return}c()},250);return s}function I(e,n,r){r=r||{};var t=document.createDocumentFragment();return!e||!e.length||(r.title&&t.appendChild(l("p",{className:"gb-chat-label"},r.title)),t.appendChild(l("div",{className:"gemboss-ec-starters"},e.map(function(a){var i=l("button",{type:"button",className:"gemboss-ec-starter gb-chat-starter"},[a.hint?l("span",{className:"gb-chat-starter-hint",text:a.hint}):null,a.label]);return i.addEventListener("click",function(){n(a)}),i})))),t}function de(e){var n=function(){try{var r=l("textarea",{readonly:!0});r.value=e,r.style.position="fixed",r.style.opacity="0",document.body.appendChild(r),r.select();var t=document.execCommand("copy");return r.remove(),t}catch{return!1}};try{if(navigator.clipboard&&navigator.clipboard.writeText)return navigator.clipboard.writeText(e).then(function(){return!0},n)}catch{}return Promise.resolve(n())}function Q(e,n,r){var t=l("button",{type:"button",className:"ui-btn ui-btn--ghost gb-chat-copy",text:n}),a=0;return t.addEventListener("click",function(){de(e).then(function(i){i&&(t.textContent=r,clearTimeout(a),a=setTimeout(function(){t.textContent=n},1500))})}),t}function X(e,n,r){var t=Object.assign({},b,r||{}),a=(n||[]).filter(function(i){return i!=null&&i!==!1&&i!==""});return!e&&!a.length?null:l("div",{className:"gb-chat-meta"},[e?Q(e,t.copy,t.copied):null].concat(a.map(function(i){return l("span",null,i)})))}function ee(e,n,r){var t=Object.assign({},b,r||{});return n?l("div",{className:"ui-notice ui-notice--danger gb-chat-error",role:"alert"},[l("div",null,n.text),n.command?l("code",{className:"gb-chat-cmd",text:n.command}):null,n.command?l("div",null,Q(n.command,t.copyCommand,t.copied)):null,l("details",{className:"gb-chat-original"},[l("summary",{text:t.originalError}),l("span",null,e)])]):l("p",{className:"ui-notice ui-notice--danger gb-chat-error",role:"alert"},e)}function _(e){return l("p",{className:"gb-chat-note"},e)}var fe={warn:"gb-engine-notice gb-engine-notice--warn",down:"gb-engine-notice gb-engine-notice--down"},he={warn:"gb-engine-dot gb-engine-dot--warn",down:"gb-engine-dot gb-engine-dot--down"};function te(e,n,r,t){if(N.bridgeNotice)return N.bridgeNotice({text:k(e)?void 0:e,node:k(e)?e:void 0,tone:n,action:r,onOpenSetup:t});var a=n==="warn"?"warn":"down",i=l("p",{className:fe[a],role:"status"},[l("span",{className:he[a],"aria-hidden":"true"}),l("span",{className:"gb-engine-notice-text"},e)]);if(t){var s=l("button",{type:"button",className:"gb-engine-notice-link",text:r||"Open setup"});s.addEventListener("click",t),i.appendChild(s)}return i}function B(e,n){return l("div",{className:"gemboss-ec-bubble"},[e,n?l("span",{className:"gemboss-ec-cursor","aria-hidden":"true"}):null])}function ne(e,n){n=n||{};var r=Object.assign({},b,n.labels||{});if(e.role==="note")return e.tone==="warn"||e.tone==="down"?te(e.text,e.tone,e.action&&e.action.label,e.action&&e.action.onClick):_(e.text);if(e.role==="user")return l("div",{className:"gemboss-ec-msg user",lang:e.lang||null},B(e.text));var t=!!e.text;return l("div",{className:"gemboss-ec-msg assistant",lang:e.lang||null},[e.steps&&e.steps.length?Z(e.steps,r):null,t?B(e.markdown===!1?e.text:G(e.text,n,r),e.pending):e.pending?J({verbs:n.pendingVerbs,since:n.since},r):null,n.renderExtra?n.renderExtra(e):null,e.warn?l("p",{className:"ui-notice ui-notice--warning gb-chat-notice"},e.warn):null,e.error?e.bridge?te(e.error,"down",null,n.onOpenSetup):ee(e.error,e.fix,r):null,e.stopped?_(r.stopped):null,!e.pending&&t?X(e.text,e.meta,r):null])}function ge(e){try{return JSON.stringify(e,function(n,r){return typeof r=="function"?"fn":k(r)?"node:"+(r.textContent||""):r})}catch{return String(Math.random())}}function pe(e,n){var r=e.querySelectorAll("details"),t=n.querySelectorAll("details"),a;for(a=0;a<r.length&&a<t.length;a++)r[a].open&&(t[a].open=!0);for(r=e.querySelectorAll(".gemboss-md-codemore"),t=n.querySelectorAll(".gemboss-md-codemore"),a=0;a<r.length&&a<t.length;a++)r[a].getAttribute("aria-expanded")==="true"&&t[a].click()}function me(e,n,r){r=r||{};var t=Object.assign({},b,r.labels||{}),a=e.__gbChat||(e.__gbChat={els:new Map,since:new Map}),i=[],s=new Map;if(!n.length&&(r.intro||r.starters&&r.starters.length)){var o=function(d){r.onStarter&&r.onStarter(d)};i.push(l("div",{className:"gemboss-ec-msg assistant"},[r.intro?B(r.intro):null,r.starters&&r.starters.length?I(r.starters,o,{title:r.startersTitle}):null,r.moreStarters&&r.moreStarters.length?I(r.moreStarters,o,{title:r.moreStartersTitle}):null]))}return n.forEach(function(d,f){var c=d.id!==void 0&&d.id!==null?"id:"+d.id:"at:"+f,p=ge(d),h=a.els.get(c);d.pending&&!d.text&&!a.since.has(c)&&a.since.set(c,Date.now());var g;h&&h.look===p?g=h.el:(g=ne(d,Object.assign({},r,{labels:t,since:a.since.get(c)})),h&&pe(h.el,g)),s.set(c,{el:g,look:p}),i.push(g)}),a.els=s,a.since.forEach(function(d,f){s.has(f)||a.since.delete(f)}),e.replaceChildren.apply(e,i),e}function ve(e,n){n=n||{};var r=Object.assign({},b,n.labels||{});e.classList.add("gemboss-ec-scroll"),e.setAttribute("role","log"),e.hasAttribute("aria-label")||e.setAttribute("aria-label",n.label||r.thread);var t=!0,a=0,i=0,s=0,o=!1,d=null,f=function(){e.scrollTop=e.scrollHeight,d=e.scrollTop},c=function(){!t||a||(a=requestAnimationFrame(function(){a=0,t&&f()}))},p=function(){i=Date.now(),o=!0,t=!1},h=function(){o=!1},g=function(){var u=e.scrollHeight-e.clientHeight-e.scrollTop,w=d!==null&&e.scrollTop===d;d=null,u>24&&!w?t=!1:u<=4&&(!o||Date.now()-i>400)&&(t=!0)},m=function(u){u.deltaY<0?p():u.deltaY>0&&h()},v=function(u){s=u.touches[0]?u.touches[0].clientY:0},y=function(u){var w=u.touches[0]?u.touches[0].clientY:0;w>s+2?p():w<s-2&&h()},L=function(u){u.key==="ArrowUp"||u.key==="PageUp"||u.key==="Home"?p():(u.key==="ArrowDown"||u.key==="PageDown"||u.key==="End")&&h()};e.addEventListener("scroll",g,{passive:!0}),e.addEventListener("wheel",m,{passive:!0}),e.addEventListener("touchstart",v,{passive:!0}),e.addEventListener("touchmove",y,{passive:!0}),e.addEventListener("keydown",L),e.addEventListener("load",c,!0);var E=new MutationObserver(c);E.observe(e,{childList:!0,subtree:!0,characterData:!0});var S=typeof ResizeObserver>"u"?null:new ResizeObserver(c);return S&&S.observe(e),f(),{el:e,setBusy:function(u){u?e.setAttribute("aria-busy","true"):e.removeAttribute("aria-busy")},toEnd:function(){t=!0,f()},destroy:function(){cancelAnimationFrame(a),e.removeEventListener("scroll",g),e.removeEventListener("wheel",m),e.removeEventListener("touchstart",v),e.removeEventListener("touchmove",y),e.removeEventListener("keydown",L),e.removeEventListener("load",c,!0),E.disconnect(),S&&S.disconnect()}}}function be(){return M("0 0 24 24",[{d:"M21.4 11.1l-8.5 8.5a5.5 5.5 0 01-7.8-7.8l8.5-8.5a3.7 3.7 0 015.2 5.2l-8.5 8.5a1.8 1.8 0 01-2.6-2.6l7.8-7.8",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"}])}function ye(e){e=e||{};var n=e.label||b.attach,r=l("input",{type:"file",hidden:!0,accept:e.accept||null,multiple:e.multiple||null,tabindex:"-1"}),t=l("button",{type:"button",className:"gemboss-ec-attach-btn","aria-label":n,title:e.title||n,disabled:e.disabled||null},be());t.addEventListener("click",function(){r.click()}),r.addEventListener("change",function(){var i=Array.prototype.slice.call(r.files||[]);r.value="",i.length&&e.onFiles&&e.onFiles(i)});var a=document.createDocumentFragment();return a.appendChild(t),a.appendChild(r),a}var W=["full","compact","icon","tight"];function we(e){var n=e.querySelector(".gemboss-ec-toolbar-lead"),r=e.querySelector(".gemboss-ec-toolbar");if(n&&n.scrollWidth>n.clientWidth+1||r&&r.scrollWidth>r.clientWidth+1)return!0;var t,a=e.querySelectorAll(".gemboss-ec-chiplabel, .gb-engine-chiplabel");for(t=0;t<a.length;t++)if(a[t].offsetParent!==null&&a[t].scrollWidth>a[t].clientWidth+1)return!0;for(a=e.querySelectorAll(".gemboss-ec-chipctl, .gb-engine-chip"),t=0;t<a.length;t++)if(a[t].offsetParent!==null&&a[t].tagName!=="SELECT"&&a[t].scrollWidth>a[t].clientWidth+1)return!0;return!1}function Ne(e,n){e&&!k(e)&&(n=e,e=null),n=n||{};var r=Object.assign({},b,n.labels||{}),t=!1,a=l("textarea",{rows:n.rows||1,maxlength:n.maxLength||null,"aria-label":n.label||r.message,placeholder:n.placeholder||null}),i=l("div",{className:"gemboss-ec-toolbar-lead"},n.leading||null),s=n.tools?l("div",{className:"gemboss-ec-toolbar-tools"},n.tools):null,o=l("button",{type:"submit",disabled:!0,"aria-label":r.send,title:r.send},M("0 0 20 20",[{d:"M10.53 4.47a.75.75 0 0 0-1.06 0l-4.5 4.5a.75.75 0 0 0 1.06 1.06l3.22-3.22v8.69a.75.75 0 0 0 1.5 0v-8.69l3.22 3.22a.75.75 0 1 0 1.06-1.06l-4.5-4.5Z"}])),d=l("button",{type:"button",className:"gemboss-ec-stop","aria-label":r.stop,title:r.stop},M("0 0 12 12",[{tag:"rect",x:"1",y:"1",width:"10",height:"10",rx:"2"}])),f=l("div",{className:"gemboss-ec-toolbar"},[i,s,n.status||null,o]),c=l("div",{className:"gemboss-ec-input","data-density":"full"},[n.top||null,a,f]);e&&e.appendChild(c);var p=function(){return!a.disabled&&a.value.trim()!==""&&(!t||n.sendWhileBusy||!n.onStop)},h=function(){var u=t&&!!n.onStop,w=u?d:o,re=u?o:d;re.parentNode&&re.replaceWith(w),o.disabled=!p()},g=function(){p()&&(n.onSend&&n.onSend(a.value),n.thread&&n.thread.toEnd&&n.thread.toEnd(),n.keepValue||(a.value="",m(),h()))},m=function(){if(!(a.offsetParent===null||a.scrollHeight===0)){var u=parseFloat(getComputedStyle(a).maxHeight)||84;a.style.height="auto";var w=a.scrollHeight+(a.offsetHeight-a.clientHeight);a.style.height=Math.min(w,u)+"px",a.style.overflowY=w>u?"auto":"hidden"}};a.addEventListener("input",function(){m(),h(),n.onChange&&n.onChange(a.value)}),a.addEventListener("keydown",function(u){n.onKeyDown&&n.onKeyDown(u),!u.defaultPrevented&&u.key==="Enter"&&!u.shiftKey&&!u.isComposing&&u.keyCode!==229&&(u.preventDefault(),g())}),o.addEventListener("click",function(u){u.preventDefault(),g()}),d.addEventListener("click",function(){n.onStop&&n.onStop(),a.focus()});var v=!1,y=function(){if(!(v||c.offsetParent===null)){v=!0;for(var u=0;u<W.length&&(c.setAttribute("data-density",W[u]),!(u===W.length-1||!we(c)));u++);v=!1,n.onDensity&&n.onDensity(c.getAttribute("data-density"))}},L=-1,E=typeof ResizeObserver>"u"?null:new ResizeObserver(function(){c.clientWidth!==L&&(L=c.clientWidth,y(),m())});E&&E.observe(c);var S=new MutationObserver(function(){v||y()});return S.observe(i,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["data-label","title","aria-label"]}),document.fonts&&document.fonts.ready&&document.fonts.ready.then(y),requestAnimationFrame(function(){y(),m()}),{el:c,textarea:a,value:function(){return a.value},setValue:function(u){a.value=u==null?"":String(u),m(),h()},setBusy:function(u){t=!!u,h()},setDisabled:function(u){a.disabled=!!u,h()},focus:function(){a.focus()},measure:y,destroy:function(){E&&E.disconnect(),S.disconnect(),c.remove()}}}var xe="The bridge on your machine is not responding: it was stopped, or the machine just slept. The panel is checking again; rerun the install command if it does not come back.",Ee="The bridge on your machine rejected this browser's pairing code (it was installed with another code). Pair again in Setup.";function Se(e,n){return e&&typeof e=="object"&&(e.atBridge=!0,n!==void 0&&(e.status=n)),e}function Ae(e,n){return n=n||{},e&&e.name==="AbortError"?{error:null,bridge:!1,stopped:!0}:e&&e.atBridge&&e.status===401?{error:n.rejected||Ee,bridge:!0,stopped:!1}:e&&e.atBridge&&(e instanceof TypeError||e.name==="TimeoutError")?{error:n.gone||xe,bridge:!0,stopped:!1}:{error:e&&e.message||String(e),bridge:!1,stopped:!1}}N.engineMark||(N.engineMark=Y),N.chat={markdown:G,parseBlocks:K,parseInline:F,linkTarget:C,imageSource:H,message:ne,render:me,thread:ve,steps:Z,pending:J,starters:I,meta:X,error:ee,note:_,composer:Ne,attach:ye,labels:b,atBridge:Se,turnError:Ae}})(typeof globalThis<"u"?globalThis:this);
|
|
6
|
+
`),o.textContent=a?n.showLess:n.showAll.replace("{n}",String(r.length)),o.setAttribute("aria-expanded",String(a))}),s.appendChild(o)}return s}var ue={1:"h3",2:"h4",3:"h5"};function G(e,n,r){n=n||{};var t=Object.assign({},b,r||n.labels||{}),a=document.createDocumentFragment();return K(e).forEach(function(i){var s;i.t==="heading"?s=l(ue[i.level],{className:"gemboss-md-h gemboss-md-h"+i.level},x(i.text,n,t)):i.t==="code"?s=oe(i,t):i.t==="table"?s=l("div",{className:"gemboss-md-tablewrap"},l("table",{className:"gemboss-md-table"},[l("thead",null,l("tr",null,i.head.map(function(o){return l("th",null,x(o,n,t))}))),l("tbody",null,i.rows.map(function(o){return l("tr",null,i.head.map(function(d,f){return l("td",null,x(o[f]||"",n,t))}))}))])):i.t==="list"?s=l(i.ordered?"ol":"ul",{className:"gemboss-md-list",start:i.ordered&&i.start!==void 0?i.start:null},i.items.map(function(o){return l("li",null,x(o,n,t))})):i.t==="quote"?s=l("blockquote",{className:"gemboss-md-quote"},z(i.lines,n,t)):i.t==="rule"?s=l("hr",{className:"gemboss-md-rule"}):s=l("p",{className:"gemboss-md-p"},z(i.lines,n,t)),a.appendChild(s)}),a}function ce(){for(var e=12,n=1.7,r=.5,t=9.6,a=[],i=function(g){return Math.round(g*100)/100},s=function(g,m){return i(g)+" "+i(m)},o=0;o<8;o++){var d=o*Math.PI/4,f=Math.cos(d),c=Math.sin(d),p=-c,h=f;a.push(["M"+s(e+p*n,e+h*n),"L"+s(e+f*t+p*r,e+c*t+h*r),"Q"+s(e+f*(t+.6),e+c*(t+.6))+" "+s(e+f*t-p*r,e+c*t-h*r),"L"+s(e-p*n,e-h*n),"Z"].join(""))}return a.join(" ")}var V={claude:{path:ce(),label:"Claude Code"},codex:{path:"M12 2.6 20.1 7.3V16.7L12 21.4 3.9 16.7V7.3ZM12 6.9 7.6 9.45v5.1L12 17.1l4.4-2.55v-5.1Z",label:"Codex"}};function Y(e,n){var r=Object.prototype.hasOwnProperty.call(V,e)?V[e]:null;if(!r)return null;n=n||{};var t=document.createElementNS(A,"svg");if(t.setAttribute("class",n.className||"gemboss-engine-mark"),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","currentColor"),n.decorative)t.setAttribute("aria-hidden","true"),t.setAttribute("focusable","false");else{t.setAttribute("role","img"),t.setAttribute("aria-label",n.title||r.label);var a=document.createElementNS(A,"title");a.textContent=n.title||r.label,t.appendChild(a)}var i=document.createElementNS(A,"path");return i.setAttribute("d",r.path),i.setAttribute("fill-rule","nonzero"),t.appendChild(i),t}function Z(e,n){var r=Object.assign({},b,n||{});return!e||!e.length?null:l("ul",{className:"gemboss-narr","aria-label":r.steps},e.map(function(t){var a=t.state||"ok",i;if(t.kind==="thinking")return i=[l("span",{className:"gemboss-narr-dot","aria-hidden":"true",text:"\u2733"}),a==="running"||!t.detail?l("span",{className:"gemboss-narr-text gemboss-narr-thinking-live"},t.label||r.thinking):l("details",{className:"gemboss-narr-thinking"},[l("summary",null,t.label||r.thought),l("div",{className:"gemboss-narr-thinking-body",text:t.detail})])],l("li",{className:"gemboss-narr-row thinking "+a},i);if(t.kind==="steer")return l("li",{className:"gemboss-narr-row steer "+a},[l("span",{className:"gemboss-narr-dot","aria-hidden":"true",text:"\u276F"}),l("span",{className:"gemboss-narr-text gemboss-narr-steer"},t.label)]);if(t.kind==="trace")return l("li",{className:"gemboss-narr-row trace "+a,title:t.error||null},[l("span",{className:"gemboss-narr-dot","aria-hidden":"true",text:a==="failed"?"\u2715":"\u23FA"}),t.detail?l("details",{className:"gemboss-narr-trace"},[l("summary",null,l("code",null,t.label)),l("div",{className:"gemboss-narr-trace-body"},l("code",{text:t.detail}))]):l("code",{className:"gemboss-narr-text gemboss-narr-trace-call"},t.label)]);var s=t.engine?Y(t.engine):null;return l("li",{className:"gemboss-narr-row "+a,title:t.error||null},[s?l("span",{className:"gemboss-narr-dot gemboss-narr-dot-engine"},s):l("span",{className:"gemboss-narr-dot","aria-hidden":"true",text:a==="ok"?"\u2713":a==="failed"?"\u2715":"\xB7"}),l("span",{className:"gemboss-narr-text"},[t.label,t.target?l("span",{className:"gb-chat-target"},[" ",t.target]):null])])}))}function J(e,n){e=e||{};var r=Object.assign({},b,n||{}),t=e.verbs&&e.verbs.length?e.verbs:[r.working],a=l("span",{className:"gemboss-ec-pending-verb","aria-hidden":"true",text:t[0]+"\u2026"}),i=l("span",{className:"gemboss-ec-pending-meta","aria-hidden":"true",text:"0s"}),s=l("div",{className:"gemboss-ec-pending",role:"status"},[l("span",{className:"gemboss-assistant-sronly",text:e.label||t[0]}),l("span",{className:"gemboss-ec-pending-star","aria-hidden":"true",text:"\u2733"}),e.compact?null:a,i]),o=e.since||Date.now(),d=!1,f=-1,c=function(){var h=Math.max(0,Math.floor((Date.now()-o)/1e3));i.textContent=(e.tokens>0?e.tokens+" token"+(e.tokens===1?"":"s")+" \xB7 ":"")+h+"s";var g=t.length>1?Math.floor(h/2)%t.length:0;if(g!==f&&!e.compact){f=g;var m=l("span",{className:"gemboss-ec-pending-verb","aria-hidden":"true",text:t[f]+"\u2026"});a.replaceWith(m),a=m}};c();var p=setInterval(function(){if(s.isConnected)d=!0;else if(d||Date.now()-o>6e5){clearInterval(p);return}c()},250);return s}function I(e,n,r){r=r||{};var t=document.createDocumentFragment();return!e||!e.length||(r.title&&t.appendChild(l("p",{className:"gb-chat-label"},r.title)),t.appendChild(l("div",{className:"gemboss-ec-starters"},e.map(function(a){var i=l("button",{type:"button",className:"gemboss-ec-starter gb-chat-starter"},[a.hint?l("span",{className:"gb-chat-starter-hint",text:a.hint}):null,a.label]);return i.addEventListener("click",function(){n(a)}),i})))),t}function de(e){var n=function(){try{var r=l("textarea",{readonly:!0});r.value=e,r.style.position="fixed",r.style.opacity="0",document.body.appendChild(r),r.select();var t=document.execCommand("copy");return r.remove(),t}catch{return!1}};try{if(navigator.clipboard&&navigator.clipboard.writeText)return navigator.clipboard.writeText(e).then(function(){return!0},n)}catch{}return Promise.resolve(n())}function Q(e,n,r){var t=l("button",{type:"button",className:"ui-btn ui-btn--ghost gb-chat-copy",text:n}),a=0;return t.addEventListener("click",function(){de(e).then(function(i){i&&(t.textContent=r,clearTimeout(a),a=setTimeout(function(){t.textContent=n},1500))})}),t}function X(e,n,r){var t=Object.assign({},b,r||{}),a=(n||[]).filter(function(i){return i!=null&&i!==!1&&i!==""});return!e&&!a.length?null:l("div",{className:"gb-chat-meta"},[e?Q(e,t.copy,t.copied):null].concat(a.map(function(i){return l("span",null,i)})))}function ee(e,n,r){var t=Object.assign({},b,r||{});return n?l("div",{className:"ui-notice ui-notice--danger gb-chat-error",role:"alert"},[l("div",null,n.text),n.command?l("code",{className:"gb-chat-cmd",text:n.command}):null,n.command?l("div",null,Q(n.command,t.copyCommand,t.copied)):null,l("details",{className:"gb-chat-original"},[l("summary",{text:t.originalError}),l("span",null,e)])]):l("p",{className:"ui-notice ui-notice--danger gb-chat-error",role:"alert"},e)}function _(e){return l("p",{className:"gb-chat-note"},e)}var fe={warn:"gb-engine-notice gb-engine-notice--warn",down:"gb-engine-notice gb-engine-notice--down"},he={warn:"gb-engine-dot gb-engine-dot--warn",down:"gb-engine-dot gb-engine-dot--down"};function te(e,n,r,t){if(w.bridgeNotice)return w.bridgeNotice({text:k(e)?void 0:e,node:k(e)?e:void 0,tone:n,action:r,onOpenSetup:t});var a=n==="warn"?"warn":"down",i=l("p",{className:fe[a],role:"status"},[l("span",{className:he[a],"aria-hidden":"true"}),l("span",{className:"gb-engine-notice-text"},e)]);if(t){var s=l("button",{type:"button",className:"gb-engine-notice-link",text:r||"Open setup"});s.addEventListener("click",t),i.appendChild(s)}return i}function B(e,n){return l("div",{className:"gemboss-ec-bubble"},[e,n?l("span",{className:"gemboss-ec-cursor","aria-hidden":"true"}):null])}function ne(e,n){n=n||{};var r=Object.assign({},b,n.labels||{});if(e.role==="note")return e.tone==="warn"||e.tone==="down"?te(e.text,e.tone,e.action&&e.action.label,e.action&&e.action.onClick):_(e.text);if(e.role==="user")return l("div",{className:"gemboss-ec-msg user",lang:e.lang||null},B(e.text));var t=!!e.text;return l("div",{className:"gemboss-ec-msg assistant",lang:e.lang||null},[e.steps&&e.steps.length?Z(e.steps,r):null,t?B(e.markdown===!1?e.text:G(e.text,n,r),e.pending):e.pending?J({verbs:n.pendingVerbs,since:n.since},r):null,n.renderExtra?n.renderExtra(e):null,e.warn?l("p",{className:"ui-notice ui-notice--warning gb-chat-notice"},e.warn):null,e.error?e.bridge?te(e.error,"down",null,n.onOpenSetup):ee(e.error,e.fix,r):null,e.stopped?_(r.stopped):null,!e.pending&&t?X(e.text,e.meta,r):null])}function ge(e){try{return JSON.stringify(e,function(n,r){return typeof r=="function"?"fn":k(r)?"node:"+(r.textContent||""):r})}catch{return String(Math.random())}}function pe(e,n){var r=e.querySelectorAll("details"),t=n.querySelectorAll("details"),a;for(a=0;a<r.length&&a<t.length;a++)r[a].open&&(t[a].open=!0);for(r=e.querySelectorAll(".gemboss-md-codemore"),t=n.querySelectorAll(".gemboss-md-codemore"),a=0;a<r.length&&a<t.length;a++)r[a].getAttribute("aria-expanded")==="true"&&t[a].click()}function me(e,n,r){r=r||{};var t=Object.assign({},b,r.labels||{}),a=e.__gbChat||(e.__gbChat={els:new Map,since:new Map}),i=[],s=new Map;if(!n.length&&(r.intro||r.starters&&r.starters.length)){var o=function(d){r.onStarter&&r.onStarter(d)};i.push(l("div",{className:"gemboss-ec-msg assistant"},[r.intro?B(r.intro):null,r.starters&&r.starters.length?I(r.starters,o,{title:r.startersTitle}):null,r.moreStarters&&r.moreStarters.length?I(r.moreStarters,o,{title:r.moreStartersTitle}):null]))}return n.forEach(function(d,f){var c=d.id!==void 0&&d.id!==null?"id:"+d.id:"at:"+f,p=ge(d),h=a.els.get(c);d.pending&&!d.text&&!a.since.has(c)&&a.since.set(c,Date.now());var g;h&&h.look===p?g=h.el:(g=ne(d,Object.assign({},r,{labels:t,since:a.since.get(c)})),h&&pe(h.el,g)),s.set(c,{el:g,look:p}),i.push(g)}),a.els=s,a.since.forEach(function(d,f){s.has(f)||a.since.delete(f)}),e.replaceChildren.apply(e,i),e}function ve(e,n){n=n||{};var r=Object.assign({},b,n.labels||{});e.classList.add("gemboss-ec-scroll"),e.setAttribute("role","log"),e.hasAttribute("aria-label")||e.setAttribute("aria-label",n.label||r.thread);var t=!0,a=0,i=0,s=0,o=!1,d=null,f=function(){e.scrollTop=e.scrollHeight,d=e.scrollTop},c=function(){!t||a||(a=requestAnimationFrame(function(){a=0,t&&f()}))},p=function(){i=Date.now(),o=!0,t=!1},h=function(){o=!1},g=function(){var u=e.scrollHeight-e.clientHeight-e.scrollTop,N=d!==null&&e.scrollTop===d;d=null,u>24&&!N?t=!1:u<=4&&(!o||Date.now()-i>400)&&(t=!0)},m=function(u){u.deltaY<0?p():u.deltaY>0&&h()},v=function(u){s=u.touches[0]?u.touches[0].clientY:0},y=function(u){var N=u.touches[0]?u.touches[0].clientY:0;N>s+2?p():N<s-2&&h()},L=function(u){u.key==="ArrowUp"||u.key==="PageUp"||u.key==="Home"?p():(u.key==="ArrowDown"||u.key==="PageDown"||u.key==="End")&&h()};e.addEventListener("scroll",g,{passive:!0}),e.addEventListener("wheel",m,{passive:!0}),e.addEventListener("touchstart",v,{passive:!0}),e.addEventListener("touchmove",y,{passive:!0}),e.addEventListener("keydown",L),e.addEventListener("load",c,!0);var E=new MutationObserver(c);E.observe(e,{childList:!0,subtree:!0,characterData:!0});var S=typeof ResizeObserver>"u"?null:new ResizeObserver(c);return S&&S.observe(e),f(),{el:e,setBusy:function(u){u?e.setAttribute("aria-busy","true"):e.removeAttribute("aria-busy")},toEnd:function(){t=!0,f()},destroy:function(){cancelAnimationFrame(a),e.removeEventListener("scroll",g),e.removeEventListener("wheel",m),e.removeEventListener("touchstart",v),e.removeEventListener("touchmove",y),e.removeEventListener("keydown",L),e.removeEventListener("load",c,!0),E.disconnect(),S&&S.disconnect()}}}function be(){return M("0 0 24 24",[{d:"M21.4 11.1l-8.5 8.5a5.5 5.5 0 01-7.8-7.8l8.5-8.5a3.7 3.7 0 015.2 5.2l-8.5 8.5a1.8 1.8 0 01-2.6-2.6l7.8-7.8",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"}])}function ye(e){e=e||{};var n=e.label||b.attach,r=l("input",{type:"file",hidden:!0,accept:e.accept||null,multiple:e.multiple||null,tabindex:"-1"}),t=l("button",{type:"button",className:"gemboss-ec-attach-btn","aria-label":n,title:e.title||n,disabled:e.disabled||null},be());t.addEventListener("click",function(){r.click()}),r.addEventListener("change",function(){var i=Array.prototype.slice.call(r.files||[]);r.value="",i.length&&e.onFiles&&e.onFiles(i)});var a=document.createDocumentFragment();return a.appendChild(t),a.appendChild(r),a}var W=["full","compact","icon","tight"];function Ne(e){var n=e.querySelector(".gemboss-ec-toolbar-lead"),r=e.querySelector(".gemboss-ec-toolbar");if(n&&n.scrollWidth>n.clientWidth+1||r&&r.scrollWidth>r.clientWidth+1)return!0;var t,a=e.querySelectorAll(".gemboss-ec-chiplabel, .gb-engine-chiplabel");for(t=0;t<a.length;t++)if(a[t].offsetParent!==null&&a[t].scrollWidth>a[t].clientWidth+1)return!0;for(a=e.querySelectorAll(".gemboss-ec-chipctl, .gb-engine-chip"),t=0;t<a.length;t++)if(a[t].offsetParent!==null&&a[t].tagName!=="SELECT"&&a[t].scrollWidth>a[t].clientWidth+1)return!0;return!1}function we(e,n){e&&!k(e)&&(n=e,e=null),n=n||{};var r=Object.assign({},b,n.labels||{}),t=!1,a=l("textarea",{rows:n.rows||1,maxlength:n.maxLength||null,"aria-label":n.label||r.message,placeholder:n.placeholder||null}),i=l("div",{className:"gemboss-ec-toolbar-lead"},n.leading||null),s=n.tools?l("div",{className:"gemboss-ec-toolbar-tools"},n.tools):null,o=l("button",{type:"submit",disabled:!0,"aria-label":r.send,title:r.send},M("0 0 20 20",[{d:"M10.53 4.47a.75.75 0 0 0-1.06 0l-4.5 4.5a.75.75 0 0 0 1.06 1.06l3.22-3.22v8.69a.75.75 0 0 0 1.5 0v-8.69l3.22 3.22a.75.75 0 1 0 1.06-1.06l-4.5-4.5Z"}])),d=l("button",{type:"button",className:"gemboss-ec-stop","aria-label":r.stop,title:r.stop},M("0 0 12 12",[{tag:"rect",x:"1",y:"1",width:"10",height:"10",rx:"2"}])),f=l("div",{className:"gemboss-ec-toolbar"},[i,s,n.status||null,o]),c=l("div",{className:"gemboss-ec-input","data-density":"full"},[n.top||null,a,f]);e&&e.appendChild(c);var p=function(){return!a.disabled&&a.value.trim()!==""&&(!t||n.sendWhileBusy||!n.onStop)},h=function(){var u=t&&!!n.onStop,N=u?d:o,re=u?o:d;re.parentNode&&re.replaceWith(N),o.disabled=!p()},g=function(){p()&&(n.onSend&&n.onSend(a.value),n.thread&&n.thread.toEnd&&n.thread.toEnd(),n.keepValue||(a.value="",m(),h()))},m=function(){if(!(a.offsetParent===null||a.scrollHeight===0)){var u=parseFloat(getComputedStyle(a).maxHeight)||84;a.style.height="auto";var N=a.scrollHeight+(a.offsetHeight-a.clientHeight);a.style.height=Math.min(N,u)+"px",a.style.overflowY=N>u?"auto":"hidden"}};a.addEventListener("input",function(){m(),h(),n.onChange&&n.onChange(a.value)}),a.addEventListener("keydown",function(u){n.onKeyDown&&n.onKeyDown(u),!u.defaultPrevented&&u.key==="Enter"&&!u.shiftKey&&!u.isComposing&&u.keyCode!==229&&(u.preventDefault(),g())}),o.addEventListener("click",function(u){u.preventDefault(),g()}),d.addEventListener("click",function(){n.onStop&&n.onStop(),a.focus()});var v=!1,y=function(){if(!(v||c.offsetParent===null)){v=!0;for(var u=0;u<W.length&&(c.setAttribute("data-density",W[u]),!(u===W.length-1||!Ne(c)));u++);v=!1,n.onDensity&&n.onDensity(c.getAttribute("data-density"))}},L=-1,E=typeof ResizeObserver>"u"?null:new ResizeObserver(function(){c.clientWidth!==L&&(L=c.clientWidth,y(),m())});E&&E.observe(c);var S=new MutationObserver(function(){v||y()});return S.observe(i,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["data-label","title","aria-label"]}),document.fonts&&document.fonts.ready&&document.fonts.ready.then(y),requestAnimationFrame(function(){y(),m()}),{el:c,textarea:a,value:function(){return a.value},setValue:function(u){a.value=u==null?"":String(u),m(),h()},setBusy:function(u){t=!!u,h()},setDisabled:function(u){a.disabled=!!u,h()},focus:function(){a.focus()},measure:y,destroy:function(){E&&E.disconnect(),S.disconnect(),c.remove()}}}var xe="The bridge on your machine is not responding: it was stopped, or the machine just slept. The panel is checking again; rerun the install command if it does not come back.",Ee="The bridge on your machine rejected this browser's pairing code (it was installed with another code). Pair again in Setup.";function Se(e,n){return e&&typeof e=="object"&&(e.atBridge=!0,n!==void 0&&(e.status=n)),e}function Ae(e,n){return n=n||{},e&&e.name==="AbortError"?{error:null,bridge:!1,stopped:!0}:e&&e.atBridge&&e.status===401?{error:n.rejected||Ee,bridge:!0,stopped:!1}:e&&e.atBridge&&(e instanceof TypeError||e.name==="TimeoutError")?{error:n.gone||xe,bridge:!0,stopped:!1}:{error:e&&e.message||String(e),bridge:!1,stopped:!1}}w.engineMark||(w.engineMark=Y),w.chat={markdown:G,parseBlocks:K,parseInline:F,linkTarget:C,imageSource:H,message:ne,render:me,thread:ve,steps:Z,pending:J,starters:I,meta:X,error:ee,note:_,composer:we,attach:ye,labels:b,atBridge:Se,turnError:Ae}})(typeof globalThis<"u"?globalThis:this);
|
package/src/editor/controls.tsx
CHANGED
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
// · the edge box hands each keystroke to the caller (`onChange(side, raw)`) and says when to commit (`onCommit`, on
|
|
12
12
|
// blur): the caller decides what an empty edge means, and the admin has three answers to that on purpose.
|
|
13
13
|
import "../editor.css"; // its own stylesheet travels with it (0.23.0): a surface that never renders it never loads it
|
|
14
|
-
import { useEffect, useState } from "react";
|
|
14
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
15
15
|
import type React from "react";
|
|
16
16
|
import { cssUrl } from "./css-url";
|
|
17
|
-
import { swatchState } from "./swatch";
|
|
17
|
+
import { swatchState, swatchWhy } from "./swatch";
|
|
18
18
|
import { Segmented } from "../segmented";
|
|
19
19
|
|
|
20
20
|
const blurOnEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
@@ -27,6 +27,8 @@ export interface SegOption<T extends string = string> {
|
|
|
27
27
|
value: T;
|
|
28
28
|
/** Default: the value itself. */
|
|
29
29
|
label?: React.ReactNode;
|
|
30
|
+
/** The button's own name, for a label that is a glyph (an arrow), said by a screen reader and shown on hover. */
|
|
31
|
+
name?: string;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export interface SegProps<T extends string = string> {
|
|
@@ -49,7 +51,93 @@ export interface SegProps<T extends string = string> {
|
|
|
49
51
|
export function Seg<T extends string = string>({ options, value, onChange, disabled, label, style }: SegProps<T>) {
|
|
50
52
|
return (
|
|
51
53
|
<Segmented<T> fill className="gemboss-ei-seg" style={style} ariaLabel={label ?? ""} value={(value ?? "") as T | ""} onChange={onChange} disabled={disabled}
|
|
52
|
-
options={options.map((o) => ({ value: o.value, label: o.label ?? o.value }))} />
|
|
54
|
+
options={options.map((o) => ({ value: o.value, label: o.label ?? o.value, ...(o.name ? { ariaLabel: o.name, title: o.name } : {}) }))} />
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── ValueControl ────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
export interface ValueControlProps {
|
|
61
|
+
/** The number box's id (the Field's `htmlFor`). */
|
|
62
|
+
id?: string;
|
|
63
|
+
/** The field's name: the chips are announced as "<label> presets". */
|
|
64
|
+
label: string;
|
|
65
|
+
/** The named values, smallest first, each `value` as the box would hold it ("" = none). Shown as "name · number". */
|
|
66
|
+
presets: ReadonlyArray<{ value: string; label: string; title?: string }>;
|
|
67
|
+
/** What the box holds now: the stored value or the text being typed. */
|
|
68
|
+
text: string;
|
|
69
|
+
/** What an empty box follows (another screen's value, the theme's), greyed. */
|
|
70
|
+
placeholder?: string;
|
|
71
|
+
/** The unit after the box ("px", "%", "ms"). */
|
|
72
|
+
suffix?: string;
|
|
73
|
+
/** A slider under the box, for a bounded range of ≤ ~100 stops. `value` is where its thumb sits. */
|
|
74
|
+
slider?: { min: number; max: number; step?: number; value: number };
|
|
75
|
+
/** A chip: the caller saves it at once. */
|
|
76
|
+
onPreset: (value: string) => void;
|
|
77
|
+
/** Each key typed. */
|
|
78
|
+
onText: (raw: string) => void;
|
|
79
|
+
/** The box let go (blur, Enter). */
|
|
80
|
+
onCommit: () => void;
|
|
81
|
+
/** Each move of the slider. */
|
|
82
|
+
onSlide?: (value: number) => void;
|
|
83
|
+
/** Keys in the box (↑ ↓ nudges are the caller's). */
|
|
84
|
+
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
|
85
|
+
/** A line under it (e.g. why a typed value was not kept as typed). */
|
|
86
|
+
note?: string;
|
|
87
|
+
/** The keyboard a phone shows: "decimal" (default) has no minus key on iOS, so a value that goes below 0 asks for "text". */
|
|
88
|
+
inputMode?: "decimal" | "text";
|
|
89
|
+
disabled?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* `.gemboss-ei-value` (0.38.0): a setting you can click, drag or type (Chris 15/10/2026: "vừa bấm được vừa nhập được, lười thì
|
|
94
|
+
* bấm"). The named values first, as the kit's segmented control (one lights when the box holds it; a value of its own lights
|
|
95
|
+
* none), then the number box with its unit and, for a bounded range, a slider. The caller owns the value: it turns chips, keys
|
|
96
|
+
* and moves into saves.
|
|
97
|
+
*/
|
|
98
|
+
export function ValueControl({ id, label, presets, text, placeholder, suffix, slider, onPreset, onText, onCommit, onSlide, onKeyDown, note, inputMode = "decimal", disabled }: ValueControlProps) {
|
|
99
|
+
const lit = presets.find((p) => p.value === text.trim())?.value;
|
|
100
|
+
return (
|
|
101
|
+
// A chip pressed right after typing keeps the box's focus: its blur would save what was typed, the save locks the field, and
|
|
102
|
+
// the chip's click landed on a disabled chip (the spacing box's own guard, review of slice 5). The chip is the choice.
|
|
103
|
+
<div className="gemboss-ei-value" onMouseDown={(e) => { if ((e.target as HTMLElement).closest('[role="group"] button')) e.preventDefault(); }}>
|
|
104
|
+
{presets.length ? <Seg label={`${label} presets`} value={lit} disabled={disabled} onChange={onPreset}
|
|
105
|
+
options={presets.map((p) => ({ value: p.value, label: p.label }))} /> : null}
|
|
106
|
+
<div className="gemboss-ei-value-row">
|
|
107
|
+
<UnitField className="gemboss-ei-value-box" unit={suffix} id={id} inputMode={inputMode} value={text} placeholder={placeholder}
|
|
108
|
+
disabled={disabled} aria-label={label} onChange={(e) => onText(e.target.value)} onBlur={onCommit}
|
|
109
|
+
onKeyDown={(e) => { onKeyDown?.(e); if (!e.defaultPrevented) blurOnEnter(e); }} />
|
|
110
|
+
{slider && onSlide ? <RangeControl min={slider.min} max={slider.max} step={slider.step} value={slider.value} disabled={disabled} onChange={onSlide} /> : null}
|
|
111
|
+
</div>
|
|
112
|
+
{note ? <p className="gemboss-ei-hint" role="status">{note}</p> : null}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── UnitField ───────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
export interface UnitFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "className" | "style"> {
|
|
120
|
+
/** The unit after the value, inside the box ("px", "ms", "%"). Empty draws none. */
|
|
121
|
+
unit?: string;
|
|
122
|
+
/** Classes on the box, e.g. `gemboss-ei-inp` for a row's full-width control, `gemboss-ei-value-box` in ValueControl. */
|
|
123
|
+
className?: string;
|
|
124
|
+
/** Style on the box (a fixed width where a row lays out several controls). */
|
|
125
|
+
style?: React.CSSProperties;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* A number box with its unit INSIDE (0.40.0; control standard, research pickers-2026-10-16 §13b U1), as Polaris TextField's
|
|
130
|
+
* `suffix`: the kit's field group (`.gemboss-field--group`, the box) around a bare input and `.gemboss-field__suffix`. Drawn beside
|
|
131
|
+
* the box, the unit was a second edge: 71 rows of the element panel stopped short of the panel's right edge. The box is a <label>,
|
|
132
|
+
* so a click or a tap anywhere in it, the unit included, focuses the input (one 44px target under a finger), as the kit's other
|
|
133
|
+
* field groups do. One owner for the shape: FieldRenderer's number, ValueControl's box and the theme inspector's design rows.
|
|
134
|
+
*/
|
|
135
|
+
export function UnitField({ unit, className, style, ...input }: UnitFieldProps) {
|
|
136
|
+
return (
|
|
137
|
+
<label className={`gemboss-field gemboss-field--group${className ? ` ${className}` : ""}`} style={style}>
|
|
138
|
+
<input {...input} className="gemboss-field__bare" />
|
|
139
|
+
{unit ? <span className="gemboss-field__suffix">{unit}</span> : null}
|
|
140
|
+
</label>
|
|
53
141
|
);
|
|
54
142
|
}
|
|
55
143
|
|
|
@@ -75,15 +163,25 @@ export interface ColorControlProps {
|
|
|
75
163
|
export function ColorControl({ id, value, onChange, disabled, label, placeholder }: ColorControlProps) {
|
|
76
164
|
const [draft, setDraft] = useState(value);
|
|
77
165
|
useEffect(() => { setDraft(value); }, [value]);
|
|
78
|
-
|
|
166
|
+
// NOT SET IS NOT BLACK (control standard §13b U7, Chris 20/10/2026): an empty colour drew the swatch black beside "Not set", so a
|
|
167
|
+
// seller read a colour nobody chose. Empty, the swatch is white with the slash a colour picker draws for "none" (editor.css
|
|
168
|
+
// `[data-empty]`), and opens on white.
|
|
169
|
+
const empty = !draft.trim();
|
|
170
|
+
const { swatch, usable } = swatchState(draft, "#ffffff");
|
|
171
|
+
// Why the swatch is off, said under it, read with it (a tooltip alone reached neither a finger nor the keys). The COMMITTED
|
|
172
|
+
// value's reason, and kept while the panel saves (it disables every field): taken from the draft or dropped while saving, the
|
|
173
|
+
// line came and went on every write and while a hex was typed (review of #4636). A draft that is a whole hex has none.
|
|
174
|
+
const why = usable ? null : swatchWhy(value);
|
|
175
|
+
const whyId = useId();
|
|
79
176
|
return (
|
|
80
177
|
<div className="gemboss-ei-color">
|
|
81
|
-
<input type="color" className="gemboss-ei-swatch" aria-label={`${label} swatch`} value={swatch}
|
|
178
|
+
<input type="color" className="gemboss-ei-swatch" aria-label={`${label} swatch`} value={swatch} data-empty={empty ? "" : undefined}
|
|
82
179
|
disabled={disabled || !usable}
|
|
83
|
-
title={usable ? undefined :
|
|
180
|
+
title={usable ? undefined : swatchWhy(draft) ?? undefined} aria-describedby={why ? whyId : undefined}
|
|
84
181
|
onChange={(e) => { setDraft(e.target.value); onChange(e.target.value); }} />
|
|
85
|
-
<input id={id} className="gemboss-field gemboss-ei-inp" placeholder={placeholder ?? "
|
|
182
|
+
<input id={id} className="gemboss-field gemboss-ei-inp" placeholder={placeholder ?? "Not set"} value={draft} disabled={disabled}
|
|
86
183
|
onChange={(e) => setDraft(e.target.value)} onBlur={() => onChange(draft)} onKeyDown={blurOnEnter} />
|
|
184
|
+
{why ? <p id={whyId} className="gemboss-ei-color-why">{why}</p> : null}
|
|
87
185
|
</div>
|
|
88
186
|
);
|
|
89
187
|
}
|
|
@@ -132,11 +230,26 @@ export interface EdgeBoxProps {
|
|
|
132
230
|
narrow?: boolean;
|
|
133
231
|
/** Ids for the boxes, per side. */
|
|
134
232
|
ids?: Partial<Record<EdgeSide, string>>;
|
|
233
|
+
/**
|
|
234
|
+
* A side's letter dragged sideways (Figma's and Webflow's scrub, 0.37.0): `dx`, the pixels moved since the press, and the keys
|
|
235
|
+
* held now, for the caller to turn into a value. A mouse or a pen only: a finger on the letter scrolls the panel as before, and
|
|
236
|
+
* the box takes the number (its 44px under a finger is the control there). Present, each letter is a 24px drag row.
|
|
237
|
+
*/
|
|
238
|
+
onScrub?: (side: EdgeSide, dx: number, keys: { shiftKey: boolean; altKey: boolean }) => void;
|
|
239
|
+
/** The drag let go: `moved` when it changed anything (the caller saves once, one Undo step); not moved, it was a click. */
|
|
240
|
+
onScrubEnd?: (side: EdgeSide, moved: boolean) => void;
|
|
135
241
|
}
|
|
136
242
|
|
|
137
243
|
/** `.gemboss-ei-edgebox`: four px boxes, T · R · B · L, mono and centred. */
|
|
138
|
-
export function EdgeBox({ label, values, placeholders, onChange, onCommit, disabled, narrow = true, ids }: EdgeBoxProps) {
|
|
244
|
+
export function EdgeBox({ label, values, placeholders, onChange, onCommit, disabled, narrow = true, ids, onScrub, onScrubEnd }: EdgeBoxProps) {
|
|
139
245
|
const cls = narrow ? "gemboss-field gemboss-field--narrow gemboss-ei-inp" : "gemboss-field gemboss-ei-inp";
|
|
246
|
+
const drag = useRef<{ side: EdgeSide; id: number; x: number; moved: boolean } | null>(null);
|
|
247
|
+
const end = (e: React.PointerEvent<HTMLSpanElement>) => {
|
|
248
|
+
const d = drag.current;
|
|
249
|
+
if (!d || d.id !== e.pointerId) return;
|
|
250
|
+
drag.current = null;
|
|
251
|
+
onScrubEnd?.(d.side, d.moved);
|
|
252
|
+
};
|
|
140
253
|
return (
|
|
141
254
|
<div className="gemboss-ei-edgebox">
|
|
142
255
|
{SIDES.map((s) => (
|
|
@@ -144,7 +257,29 @@ export function EdgeBox({ label, values, placeholders, onChange, onCommit, disab
|
|
|
144
257
|
<input className={cls} inputMode="numeric" placeholder={placeholders?.[s] ?? "0"}
|
|
145
258
|
aria-label={`${label} ${SIDE_NAME[s]}`} id={ids?.[s]} value={values[s] ?? ""} disabled={disabled}
|
|
146
259
|
onChange={(e) => onChange(s, e.target.value)} onBlur={onCommit} onKeyDown={blurOnEnter} />
|
|
147
|
-
|
|
260
|
+
{onScrub ? (
|
|
261
|
+
// Rendered while disabled too: a field that locks in the middle of a drag (another save landing) must not drop the
|
|
262
|
+
// drag's handlers, or it never ends and nothing is saved (review of 3a). A drag only refuses to START when disabled.
|
|
263
|
+
<span className="gemboss-ei-edge-scrub" title={`Drag to change ${SIDE_NAME[s]}`} aria-disabled={disabled || undefined}
|
|
264
|
+
onPointerDown={(e) => {
|
|
265
|
+
if (disabled || e.pointerType === "touch" || e.button !== 0) return;
|
|
266
|
+
e.preventDefault();
|
|
267
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
268
|
+
drag.current = { side: s, id: e.pointerId, x: e.clientX, moved: false };
|
|
269
|
+
}}
|
|
270
|
+
onPointerMove={(e) => {
|
|
271
|
+
const d = drag.current;
|
|
272
|
+
if (!d || d.id !== e.pointerId) return;
|
|
273
|
+
const dx = e.clientX - d.x;
|
|
274
|
+
// A press that wanders a pixel is still a click.
|
|
275
|
+
if (!d.moved && Math.abs(dx) < 2) return;
|
|
276
|
+
d.moved = true;
|
|
277
|
+
onScrub(s, dx, { shiftKey: e.shiftKey, altKey: e.altKey });
|
|
278
|
+
}}
|
|
279
|
+
onPointerUp={end} onPointerCancel={end}
|
|
280
|
+
// Its own capture only (the event bubbles from nothing here, but the check is the fleet's rule).
|
|
281
|
+
onLostPointerCapture={(e) => { if (e.target === e.currentTarget) end(e); }}>{s.toUpperCase()}</span>
|
|
282
|
+
) : <span>{s.toUpperCase()}</span>}
|
|
148
283
|
</div>
|
|
149
284
|
))}
|
|
150
285
|
</div>
|
|
@@ -153,7 +288,9 @@ export function EdgeBox({ label, values, placeholders, onChange, onCommit, disab
|
|
|
153
288
|
|
|
154
289
|
// ── ImageControl ────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
155
290
|
|
|
156
|
-
/** A
|
|
291
|
+
/** A field's own button (an image slot's Library / Generate / Clear, a link's Choose, a panel's "+ Add"): the kit's `.ui-btn`, a Polaris
|
|
292
|
+
* secondary button (28px, 12px / 550, radius 8; 44px under a finger). It was a dashed pill (`.gemboss-ei-addchip`, 27px, 11px / 600,
|
|
293
|
+
* radius 999): control standard U3, research pickers-2026-10-16 §13b. */
|
|
157
294
|
export function ChipButton({ disabled, expanded, onClick, children }: {
|
|
158
295
|
disabled?: boolean;
|
|
159
296
|
/** `aria-expanded`, for a chip that opens something under it (the Library grid). */
|
|
@@ -161,7 +298,7 @@ export function ChipButton({ disabled, expanded, onClick, children }: {
|
|
|
161
298
|
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
|
162
299
|
children: React.ReactNode;
|
|
163
300
|
}) {
|
|
164
|
-
return <button type="button" className="
|
|
301
|
+
return <button type="button" className="ui-btn" disabled={disabled} aria-expanded={expanded} onClick={onClick}>{children}</button>;
|
|
165
302
|
}
|
|
166
303
|
|
|
167
304
|
export interface ImageControlProps {
|
package/src/editor/frame.tsx
CHANGED
|
@@ -156,20 +156,24 @@ export function EditorBack({ as: Comp = "button", label = "Back", ...rest }:
|
|
|
156
156
|
|
|
157
157
|
/**
|
|
158
158
|
* The auto-save pill: "Saving…" with a spinner while an edit is in flight, "✓ Draft saved" once it lands.
|
|
159
|
-
* `idle` draws nothing unless `idleText` says what autosave means here (the section editor's "Autosaves",
|
|
159
|
+
* `idle` draws nothing unless `idleText` says what autosave means here (the section editor's "Autosaves", muted).
|
|
160
|
+
* `live` (default true): "Saving…" and "✓ Draft saved" are a polite live region. `live={false}` for a surface that speaks
|
|
161
|
+
* through a live region of its own, one that is in the page from its first render and says only what changed (the page
|
|
162
|
+
* editor's save pill): the pill is then words with a title, and nothing is announced twice.
|
|
160
163
|
*/
|
|
161
164
|
export function EditorSaveState({
|
|
162
165
|
state, savedText = "✓ Draft saved", savingText = "Saving…", savingTitle = "Saving draft…",
|
|
163
|
-
savedTitle = "Every change is saved to the draft automatically", idleText,
|
|
166
|
+
savedTitle = "Every change is saved to the draft automatically", idleText, live = true,
|
|
164
167
|
}: {
|
|
165
168
|
state: "idle" | "saving" | "saved";
|
|
166
169
|
savedText?: React.ReactNode; savingText?: React.ReactNode; savingTitle?: string; savedTitle?: string; idleText?: React.ReactNode;
|
|
170
|
+
live?: boolean;
|
|
167
171
|
}) {
|
|
168
172
|
if (state === "idle") {
|
|
169
|
-
return idleText != null && idleText !== false ? <span className="gemboss-editor-savestate" title={savedTitle}
|
|
173
|
+
return idleText != null && idleText !== false ? <span className="gemboss-editor-savestate is-idle" title={savedTitle}>{idleText}</span> : null;
|
|
170
174
|
}
|
|
171
175
|
return (
|
|
172
|
-
<span className={`gemboss-editor-savestate is-${state}`} role
|
|
176
|
+
<span className={`gemboss-editor-savestate is-${state}`} {...(live ? { role: "status", "aria-live": "polite" as const } : null)} title={state === "saving" ? savingTitle : savedTitle}>
|
|
173
177
|
{state === "saving" ? <><span className="gemboss-narr-spin" aria-hidden />{savingText}</> : savedText}
|
|
174
178
|
</span>
|
|
175
179
|
);
|