@gemboss/ui 0.48.0 → 0.50.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +260 -0
  2. package/README.md +127 -11
  3. package/catalog/editor.html +1 -1
  4. package/catalog/index.html +10 -10
  5. package/gemboss-ui.css +3 -3
  6. package/package.json +22 -2
  7. package/parts/components.css +3 -3
  8. package/parts/core.css +1 -1
  9. package/parts/shell.css +2 -2
  10. package/src/app-shell.css +1 -1
  11. package/src/assistant/chat.tsx +50 -6
  12. package/src/assistant/host.tsx +19 -7
  13. package/src/assistant/index.ts +1 -1
  14. package/src/assistant/sessions.tsx +2 -1
  15. package/src/assistant-chat.css +1 -1
  16. package/src/assistant.css +1 -1
  17. package/src/auth-signin-plain.js +1 -0
  18. package/src/auth-sso-plain.js +1 -1
  19. package/src/auth.tsx +105 -8
  20. package/src/briefing-card.css +1 -1
  21. package/src/collections.css +1 -1
  22. package/src/create-shell.css +1 -0
  23. package/src/create-shell.tsx +221 -0
  24. package/src/editor-insert.css +1 -0
  25. package/src/editor.css +1 -1
  26. package/src/empty-stage.css +1 -0
  27. package/src/empty-stage.tsx +31 -0
  28. package/src/icons.tsx +2 -0
  29. package/src/index-table.tsx +7 -4
  30. package/src/index.ts +16 -2
  31. package/src/kit-plain.css +1 -1
  32. package/src/kit-plain.js +1 -1
  33. package/src/language-switcher.tsx +10 -41
  34. package/src/list-page.css +1 -0
  35. package/src/list-page.tsx +239 -0
  36. package/src/menu-extras.css +1 -0
  37. package/src/menu-keys.ts +199 -0
  38. package/src/menu-layer.ts +2 -1
  39. package/src/menu-plain.js +1 -0
  40. package/src/menu.css +1 -0
  41. package/src/menu.tsx +83 -35
  42. package/src/pager-extras.css +1 -0
  43. package/src/pager.css +1 -0
  44. package/src/pager.tsx +88 -0
  45. package/src/portal.css +1 -1
  46. package/src/segmented.tsx +12 -2
  47. package/src/selection.tsx +3 -1
  48. package/src/session-rail-plain.js +1 -1
  49. package/src/settings-dialog.tsx +29 -2
  50. package/src/shell.css +1 -1
  51. package/src/stage-path.css +1 -0
  52. package/src/stage-path.tsx +89 -0
  53. package/src/stage.css +1 -0
  54. package/src/stage.tsx +52 -0
  55. package/src/starting-point.css +1 -0
  56. package/src/starting-point.tsx +93 -0
  57. package/src/status-list.css +1 -0
  58. package/src/status-list.tsx +65 -0
  59. package/src/status-matrix.css +1 -0
  60. package/src/status-matrix.tsx +131 -0
  61. package/src/team-chat.css +1 -1
  62. package/src/translation-row.css +1 -0
  63. package/src/translation-row.tsx +113 -0
  64. package/src/ui.css +1 -1
@@ -1 +1 @@
1
- (function(g){"use strict";var q=g.gembossUI;if(!q){g.console&&g.console.error("@gemboss/ui session-rail-plain.js: load kit-plain.js first (it defines gembossUI)");return}if(q.sessionRail)return;var S="http://www.w3.org/2000/svg",j=function(r){return typeof Node<"u"&&r instanceof Node};function B(r,t){var i={},n;for(n in r)i[n]=r[n];for(n in t)i[n]=t[n];return i}function l(r,t,i){var n=document.createElement(r);if(t)for(var o in t){var d=t[o];d==null||d===!1||(o==="className"?n.className=d:n.setAttribute(o,d===!0?"":String(d)))}return(Array.isArray(i)?i:i==null?[]:[i]).forEach(function(a){n.appendChild(j(a)?a:document.createTextNode(String(a)))}),n}function Q(r){try{return g.localStorage.getItem(r)}catch{return null}}function J(r,t){try{g.localStorage.setItem(r,t)}catch{}}var O={newChat:"New chat",expand:"Expand sidebar",collapse:"Collapse sidebar",expandTitle:"Expand",collapseTitle:"Collapse",searchPlaceholder:"Search conversations\u2026",searchLabel:"Search history",clearSearch:"Clear search",noneFound:"No conversations found",noneYet:"No conversations yet",openNow:"Open now",pinned:"Pinned",today:"Today",yesterday:"Yesterday",previous7:"Previous 7 days",previous30:"Previous 30 days",older:"Older",working:"working",options:"Options",save:"Save",cancel:"Cancel",pin:"Pin",unpin:"Unpin",rename:"Rename",exportMarkdown:"Export Markdown",remove:"Delete",confirmRemove:"Delete this chat?",justNow:"just now",minutesAgo:function(r){return r+" min ago"},hoursAgo:function(r){return r+" hr ago"},daysAgo:function(r){return r+" day"+(r>1?"s":"")+" ago"},dateLocale:"en-US"};function H(r,t,i){t=t||O;var n=Math.floor(((i===void 0?Date.now():i)-r)/6e4);if(n<1)return t.justNow;if(n<60)return t.minutesAgo(n);var o=Math.floor(n/60);if(o<24)return t.hoursAgo(o);var d=Math.floor(o/24);return d<7?t.daysAgo(d):new Date(r).toLocaleDateString(t.dateLocale)}function P(r){return String(r).toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g,"").replace(/đ/g,"d")}function _(r,t,i){t=t||O;var n=r.filter(function(p){return p.pinned}),o=r.filter(function(p){return!p.pinned}),d=new Date(i===void 0?Date.now():i),a=new Date(d.getFullYear(),d.getMonth(),d.getDate()).getTime(),A=864e5,T=[{label:t.today,min:a},{label:t.yesterday,min:a-A},{label:t.previous7,min:a-7*A},{label:t.previous30,min:a-30*A},{label:t.older,min:-1/0}],f=T.map(function(p){return{label:p.label,items:[]}});o.forEach(function(p){for(var v=-1,L=0;L<T.length;L++)if(p.updatedAt>=T[L].min){v=L;break}f[v===-1?f.length-1:v].items.push(p)});var w=n.length?[{label:t.pinned,items:n}]:[];return w.concat(f.filter(function(p){return p.items.length>0}))}q.assistantSessions={relativeTime:H,normalizeForSearch:P,groupSessions:_,labels:O};function b(r){return function(t){var i=document.createElementNS(S,"svg");i.setAttribute("viewBox","0 0 20 20"),t&&i.setAttribute("class",t),i.setAttribute("aria-hidden","true"),i.setAttribute("focusable","false");var n=document.createElementNS(S,"g");return[["fill","none"],["stroke","currentColor"],["stroke-width","1.6"],["stroke-linecap","round"],["stroke-linejoin","round"]].forEach(function(o){n.setAttribute(o[0],o[1])}),r.forEach(function(o){var d=document.createElementNS(S,o.tag||"path");for(var a in o)a!=="tag"&&d.setAttribute(a,o[a]);n.appendChild(d)}),i.appendChild(n),i}}var X={compose:b([{d:"M11 4H5.5A1.5 1.5 0 0 0 4 5.5v9A1.5 1.5 0 0 0 5.5 16h9a1.5 1.5 0 0 0 1.5-1.5V9"},{d:"M14.5 3.5l2 2L10 12H8v-2z"}]),chevronLeft:b([{d:"M12 5l-5 5 5 5"}]),chevronRight:b([{d:"M8 5l5 5-5 5"}]),search:b([{tag:"circle",cx:"9",cy:"9",r:"5"},{d:"M13 13l3.5 3.5"}]),close:b([{d:"M6 6l8 8M14 6l-8 8"}]),check:b([{d:"M4.5 10.5l3.5 3.5 7.5-8"}]),more:b([{tag:"circle",cx:"5",cy:"10",r:".8"},{tag:"circle",cx:"10",cy:"10",r:".8"},{tag:"circle",cx:"15",cy:"10",r:".8"}]),pin:b([{d:"M8 3.5h4l-.5 5 2.5 2.5H6l2.5-2.5z"},{d:"M10 11v5.5"}]),pinFilled:function(r){var t=document.createElementNS(S,"svg");t.setAttribute("viewBox","0 0 20 20"),r&&t.setAttribute("class",r),t.setAttribute("aria-hidden","true"),t.setAttribute("focusable","false");var i=document.createElementNS(S,"path");i.setAttribute("d","M8 3.5h4l-.5 5 2.5 2.5H6l2.5-2.5z"),i.setAttribute("fill","currentColor");var n=document.createElementNS(S,"path");return n.setAttribute("d","M10 11v5.5"),n.setAttribute("stroke","currentColor"),n.setAttribute("stroke-width","1.6"),n.setAttribute("stroke-linecap","round"),t.appendChild(i),t.appendChild(n),t},edit:b([{d:"M4 16h3l8.5-8.5-3-3L4 13z"}]),exportIcon:b([{d:"M10 3.5v9M6.5 7L10 3.5 13.5 7"},{d:"M4 12.5v3h12v-3"}]),remove:b([{d:"M5 6h10M8 6V4.5h4V6M6.5 6l.7 9.5h5.6l.7-9.5"}])};q.sessionRail=function(r){r=r||{};var t={sessions:[],unlistedIds:[],activeId:void 0,pendingId:null,busyIds:[],collapsed:!1,footer:null,titleMaxLength:200,newChatTitle:void 0},i={},n=O,o=X,d=r.storageKey||"",a={query:"",editingId:null,editValue:"",menuId:null,menuTop:0,menuLeft:0,confirmDeleteId:null},A=[];function T(e){["sessions","unlistedIds","busyIds"].forEach(function(s){e[s]!==void 0&&(t[s]=e[s]||[])}),["activeId","pendingId","footer","newChatTitle"].forEach(function(s){e[s]!==void 0&&(t[s]=e[s])}),e.collapsed!==void 0&&(t.collapsed=!!e.collapsed),e.titleMaxLength!==void 0&&(t.titleMaxLength=e.titleMaxLength||200),e.labels&&(n=B(n,e.labels)),e.icons&&(o=B(o,e.icons)),["onToggleCollapsed","onNewChat","onOpen","onRename","onTogglePin","onDelete","onExport"].forEach(function(s){e[s]!==void 0&&(i[s]=e[s])})}T(r),r.collapsed===void 0&&d&&(t.collapsed=Q(d)==="1");var f=l("aside",{className:"gemboss-assistant-rail"}),w=l("button",{type:"button",className:"gemboss-assistant-newchat"});w.addEventListener("click",function(){i.onNewChat&&i.onNewChat()});var p=l("button",{type:"button",className:"gemboss-assistant-collapse"});p.addEventListener("click",function(){if(i.onToggleCollapsed){i.onToggleCollapsed();return}t.collapsed=!t.collapsed,d&&J(d,t.collapsed?"1":"0"),K()}),f.appendChild(l("div",{className:"gemboss-assistant-rail-top"},[w,p]));var v=l("div",{className:"gemboss-assistant-search gemboss-field gemboss-field--group"}),L=o.search(),y=l("input",{className:"gemboss-field__bare"}),V=null;y.addEventListener("input",function(){a.query=y.value,F(),E()}),y.addEventListener("keydown",function(e){e.key==="Escape"&&(e.preventDefault(),Y(""),y.blur())}),v.appendChild(L),v.appendChild(y);var C=l("div",{className:"gemboss-assistant-threads"});f.appendChild(C);var z=[];function Y(e){a.query=e,y.value=e,F(),E()}function F(){var e;a.query?(e=l("button",{type:"button",className:"gemboss-assistant-search-clear","aria-label":n.clearSearch},o.close()),e.addEventListener("click",function(){Y("")})):e=l("kbd",{className:"gemboss-assistant-kbd"},"/"),V?v.replaceChild(e,V):v.appendChild(e),V=e}function M(){a.menuId!==null&&(a.menuId=null,U(),E())}function U(){A.forEach(function(e){e()}),A=[]}function Z(){U();var e=function(u){var m=C.querySelector(".gemboss-assistant-menu");(!m||!m.contains(u.target))&&M()},s=function(){M()};document.addEventListener("mousedown",e),g.addEventListener("scroll",s,!0),g.addEventListener("resize",s),A.push(function(){document.removeEventListener("mousedown",e),g.removeEventListener("scroll",s,!0),g.removeEventListener("resize",s)})}function $(e,s){if(a.menuId===s){M();return}var u=e.getBoundingClientRect(),m=184,c=200,h=u.bottom+c+8>g.innerHeight;a.menuLeft=Math.max(8,Math.min(u.right-m,g.innerWidth-m-8)),a.menuTop=h?Math.max(8,u.top-6-c):u.bottom+6,a.confirmDeleteId=null,a.menuId=s,Z(),E()}function ee(e){a.editingId=e.id,a.editValue=e.title,E()}function W(){a.editingId=null,a.editValue="",E()}function G(e){var s=a.editValue.trim();a.editingId=null,E(),!(!s||s===e.title)&&i.onRename&&i.onRename(e.id,s)}function D(e,s,u,m){var c=l("button",{type:"button",role:"menuitem",className:m?"danger":null},[e,l("span",null,s)]);return c.addEventListener("click",u),c}function te(e){var s=a.editingId===e.id,u=l("div",{className:"gemboss-assistant-thread"+(e.id===t.activeId||e.id===t.pendingId?" active":"")+(s?" editing":"")+(a.menuId===e.id?" menu-open":"")});if(s){var m=l("input",{className:"gemboss-field",maxlength:String(t.titleMaxLength)});m.value=a.editValue,m.addEventListener("input",function(){a.editValue=m.value}),m.addEventListener("keydown",function(x){x.key==="Enter"?(x.preventDefault(),G(e)):x.key==="Escape"&&(x.preventDefault(),W())});var c=l("button",{type:"button",className:"gemboss-assistant-thread-act","aria-label":n.save},o.check());c.addEventListener("click",function(){G(e)});var h=l("button",{type:"button",className:"gemboss-assistant-thread-act","aria-label":n.cancel},o.close());return h.addEventListener("click",W),u.appendChild(l("div",{className:"gemboss-assistant-thread-edit"},[m,c,h])),u}var I=l("span",{className:"gemboss-assistant-thread-titlerow"});t.busyIds.indexOf(e.id)>=0&&I.appendChild(l("span",{className:"gemboss-assistant-thread-running","aria-label":n.working})),e.pinned&&I.appendChild(o.pinFilled("gemboss-assistant-pin-dot")),I.appendChild(l("span",{className:"gemboss-assistant-thread-title"},e.title));var k=l("button",{type:"button",className:"gemboss-assistant-thread-main",title:e.title},[I,l("span",{className:"gemboss-assistant-thread-meta"},H(e.updatedAt,n))]);k.addEventListener("click",function(){i.onOpen&&i.onOpen(e.id)}),k.gbRailKey=e.id+":main";var R=l("button",{type:"button",className:"gemboss-assistant-thread-act","aria-label":n.options},o.more());if(R.addEventListener("mousedown",function(x){x.stopPropagation()}),R.addEventListener("click",function(){$(R,e.id)}),R.gbRailKey=e.id+":more",u.appendChild(k),u.appendChild(l("div",{className:"gemboss-assistant-thread-actions"},R)),a.menuId===e.id){var N=l("div",{className:"gemboss-popover-surface gemboss-assistant-menu",role:"menu"});N.style.position="fixed",N.style.top=a.menuTop+"px",N.style.left=a.menuLeft+"px",a.confirmDeleteId===e.id?(N.appendChild(l("div",{className:"gemboss-assistant-menu-confirm"},n.confirmRemove)),N.appendChild(D(o.remove(),n.remove,function(){M(),i.onDelete&&i.onDelete(e.id)},!0)),N.appendChild(D(o.close(),n.cancel,function(){a.confirmDeleteId=null,E()}))):(i.onTogglePin&&N.appendChild(D(e.pinned?o.pin():o.pinFilled(),e.pinned?n.unpin:n.pin,function(){M(),i.onTogglePin(e)})),N.appendChild(D(o.edit(),n.rename,function(){M(),ee(e)})),i.onExport&&N.appendChild(D(o.exportIcon(),n.exportMarkdown,function(){M(),i.onExport(e.id)})),N.appendChild(D(o.remove(),n.remove,function(){a.confirmDeleteId=e.id,E()},!0))),u.appendChild(N)}return u}function ne(){var e=P(a.query.trim());if(e){var s=t.sessions.filter(function(c){return P(c.title+" "+(c.preview||"")).indexOf(e)>=0});return s.length?[{label:"",items:s}]:[]}var u=_(t.sessions,n),m=t.unlistedIds.filter(function(c){return!t.sessions.some(function(h){return h.id===c})}).map(function(c){return{id:c,title:n.newChat,updatedAt:Date.now(),pinned:!1}});return m.length?[{label:n.openNow,items:m}].concat(u):u}function E(){for(var e=document.activeElement&&C.contains(document.activeElement)?document.activeElement:null,s=!!e&&e.tagName==="INPUT",u=e?e.gbRailKey:null;C.firstChild;)C.removeChild(C.firstChild);var m=ne();m.length?m.forEach(function(h){var I=l("div",{className:"gemboss-assistant-group"});h.label&&I.appendChild(l("div",{className:"gemboss-assistant-group-label"},h.label)),h.items.forEach(function(k){I.appendChild(te(k))}),C.appendChild(I)}):C.appendChild(l("div",{className:"gemboss-assistant-threads-empty"},a.query?n.noneFound:n.noneYet)),u&&Array.prototype.some.call(C.querySelectorAll("button"),function(h){return h.gbRailKey!==u?!1:(h.focus({preventScroll:!0}),!0)});var c=C.querySelector(".gemboss-assistant-thread-edit input");if(c&&(s||a.editingId!==null)){c.focus();try{c.setSelectionRange(c.value.length,c.value.length)}catch{}}}function K(){for(f.className="gemboss-assistant-rail"+(t.collapsed?" is-collapsed":"");w.firstChild;)w.removeChild(w.firstChild);for(w.title=t.newChatTitle!==void 0&&t.newChatTitle!==null?t.newChatTitle:n.newChat,w.appendChild(o.compose()),w.appendChild(l("span",null,n.newChat));p.firstChild;)p.removeChild(p.firstChild);p.setAttribute("aria-label",t.collapsed?n.expand:n.collapse),p.title=t.collapsed?n.expandTitle:n.collapseTitle,p.appendChild(t.collapsed?o.chevronRight():o.chevronLeft()),t.sessions.length>0?(y.setAttribute("placeholder",n.searchPlaceholder),y.setAttribute("aria-label",n.searchLabel),y.value!==a.query&&(y.value=a.query),F(),v.parentNode!==f&&f.insertBefore(v,C)):v.parentNode===f&&f.removeChild(v),E(),z.forEach(function(e){e.parentNode===f&&f.removeChild(e)}),z=t.footer?(Array.isArray(t.footer)?t.footer:[t.footer]).filter(j):[],z.forEach(function(e){f.appendChild(e)})}return K(),{el:f,update:function(e){T(e||{}),K()},focusSearch:function(){v.parentNode===f&&y.focus()},destroy:function(){U(),f.parentNode&&f.parentNode.removeChild(f)}}}})(typeof globalThis<"u"?globalThis:this);
1
+ (function(g){"use strict";var q=g.gembossUI;if(!q){g.console&&g.console.error("@gemboss/ui session-rail-plain.js: load kit-plain.js first (it defines gembossUI)");return}if(q.sessionRail)return;var S="http://www.w3.org/2000/svg",j=function(r){return typeof Node<"u"&&r instanceof Node};function B(r,t){var i={},n;for(n in r)i[n]=r[n];for(n in t)i[n]=t[n];return i}function l(r,t,i){var n=document.createElement(r);if(t)for(var o in t){var d=t[o];d==null||d===!1||(o==="className"?n.className=d:n.setAttribute(o,d===!0?"":String(d)))}return(Array.isArray(i)?i:i==null?[]:[i]).forEach(function(a){n.appendChild(j(a)?a:document.createTextNode(String(a)))}),n}function Q(r){try{return g.localStorage.getItem(r)}catch{return null}}function J(r,t){try{g.localStorage.setItem(r,t)}catch{}}var O={newChat:"New chat",expand:"Expand sidebar",collapse:"Collapse sidebar",expandTitle:"Expand",collapseTitle:"Collapse",searchPlaceholder:"Search conversations\u2026",searchLabel:"Search history",clearSearch:"Clear search",noneFound:"No conversations found",noneYet:"No conversations yet",openNow:"Open now",pinned:"Pinned",today:"Today",yesterday:"Yesterday",previous7:"Previous 7 days",previous30:"Previous 30 days",older:"Older",working:"working",options:"Options",save:"Save",cancel:"Cancel",pin:"Pin",unpin:"Unpin",rename:"Rename",exportMarkdown:"Export Markdown",remove:"Delete",confirmRemove:"Delete this chat?",justNow:"just now",minutesAgo:function(r){return r+" min ago"},hoursAgo:function(r){return r+" hr ago"},daysAgo:function(r){return r+" day"+(r>1?"s":"")+" ago"},dateLocale:"en-US"};function H(r,t,i){t=t||O;var n=Math.floor(((i===void 0?Date.now():i)-r)/6e4);if(n<1)return t.justNow;if(n<60)return t.minutesAgo(n);var o=Math.floor(n/60);if(o<24)return t.hoursAgo(o);var d=Math.floor(o/24);return d<7?t.daysAgo(d):new Date(r).toLocaleDateString(t.dateLocale)}function P(r){return String(r).toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g,"").replace(/đ/g,"d")}function _(r,t,i){t=t||O;var n=r.filter(function(p){return p.pinned}),o=r.filter(function(p){return!p.pinned}),d=new Date(i===void 0?Date.now():i),a=new Date(d.getFullYear(),d.getMonth(),d.getDate()).getTime(),A=864e5,T=[{label:t.today,min:a},{label:t.yesterday,min:a-A},{label:t.previous7,min:a-7*A},{label:t.previous30,min:a-30*A},{label:t.older,min:-1/0}],f=T.map(function(p){return{label:p.label,items:[]}});o.forEach(function(p){for(var v=-1,L=0;L<T.length;L++)if(p.updatedAt>=T[L].min){v=L;break}f[v===-1?f.length-1:v].items.push(p)});var w=n.length?[{label:t.pinned,items:n}]:[];return w.concat(f.filter(function(p){return p.items.length>0}))}q.assistantSessions={relativeTime:H,normalizeForSearch:P,groupSessions:_,labels:O};function b(r){return function(t){var i=document.createElementNS(S,"svg");i.setAttribute("viewBox","0 0 20 20"),t&&i.setAttribute("class",t),i.setAttribute("aria-hidden","true"),i.setAttribute("focusable","false");var n=document.createElementNS(S,"g");return[["fill","none"],["stroke","currentColor"],["stroke-width","1.6"],["stroke-linecap","round"],["stroke-linejoin","round"]].forEach(function(o){n.setAttribute(o[0],o[1])}),r.forEach(function(o){var d=document.createElementNS(S,o.tag||"path");for(var a in o)a!=="tag"&&d.setAttribute(a,o[a]);n.appendChild(d)}),i.appendChild(n),i}}var X={compose:b([{d:"M11 4H5.5A1.5 1.5 0 0 0 4 5.5v9A1.5 1.5 0 0 0 5.5 16h9a1.5 1.5 0 0 0 1.5-1.5V9"},{d:"M14.5 3.5l2 2L10 12H8v-2z"}]),chevronLeft:b([{d:"M12 5l-5 5 5 5"}]),chevronRight:b([{d:"M8 5l5 5-5 5"}]),search:b([{tag:"circle",cx:"9",cy:"9",r:"5"},{d:"M13 13l3.5 3.5"}]),close:b([{d:"M6 6l8 8M14 6l-8 8"}]),check:b([{d:"M4.5 10.5l3.5 3.5 7.5-8"}]),more:b([{tag:"circle",cx:"5",cy:"10",r:".8"},{tag:"circle",cx:"10",cy:"10",r:".8"},{tag:"circle",cx:"15",cy:"10",r:".8"}]),pin:b([{d:"M8 3.5h4l-.5 5 2.5 2.5H6l2.5-2.5z"},{d:"M10 11v5.5"}]),pinFilled:function(r){var t=document.createElementNS(S,"svg");t.setAttribute("viewBox","0 0 20 20"),r&&t.setAttribute("class",r),t.setAttribute("aria-hidden","true"),t.setAttribute("focusable","false");var i=document.createElementNS(S,"path");i.setAttribute("d","M8 3.5h4l-.5 5 2.5 2.5H6l2.5-2.5z"),i.setAttribute("fill","currentColor");var n=document.createElementNS(S,"path");return n.setAttribute("d","M10 11v5.5"),n.setAttribute("stroke","currentColor"),n.setAttribute("stroke-width","1.6"),n.setAttribute("stroke-linecap","round"),t.appendChild(i),t.appendChild(n),t},edit:b([{d:"M4 16h3l8.5-8.5-3-3L4 13z"}]),exportIcon:b([{d:"M10 3.5v9M6.5 7L10 3.5 13.5 7"},{d:"M4 12.5v3h12v-3"}]),remove:b([{d:"M5 6h10M8 6V4.5h4V6M6.5 6l.7 9.5h5.6l.7-9.5"}])};q.sessionRail=function(r){r=r||{};var t={sessions:[],unlistedIds:[],activeId:void 0,pendingId:null,busyIds:[],collapsed:!1,footer:null,titleMaxLength:200,newChatTitle:void 0},i={},n=O,o=X,d=r.storageKey||"",a={query:"",editingId:null,editValue:"",menuId:null,menuTop:0,menuLeft:0,confirmDeleteId:null},A=[];function T(e){["sessions","unlistedIds","busyIds"].forEach(function(s){e[s]!==void 0&&(t[s]=e[s]||[])}),["activeId","pendingId","footer","newChatTitle"].forEach(function(s){e[s]!==void 0&&(t[s]=e[s])}),e.collapsed!==void 0&&(t.collapsed=!!e.collapsed),e.titleMaxLength!==void 0&&(t.titleMaxLength=e.titleMaxLength||200),e.labels&&(n=B(n,e.labels)),e.icons&&(o=B(o,e.icons)),["onToggleCollapsed","onNewChat","onOpen","onRename","onTogglePin","onDelete","onExport"].forEach(function(s){e[s]!==void 0&&(i[s]=e[s])})}T(r),r.collapsed===void 0&&d&&(t.collapsed=Q(d)==="1");var f=l("aside",{className:"gemboss-assistant-rail"}),w=l("button",{type:"button",className:"gemboss-assistant-newchat"});w.addEventListener("click",function(){i.onNewChat&&i.onNewChat()});var p=l("button",{type:"button",className:"gemboss-assistant-collapse"});p.addEventListener("click",function(){if(i.onToggleCollapsed){i.onToggleCollapsed();return}t.collapsed=!t.collapsed,d&&J(d,t.collapsed?"1":"0"),K()}),f.appendChild(l("div",{className:"gemboss-assistant-rail-top"},[w,p]));var v=l("div",{className:"gemboss-assistant-search gemboss-field gemboss-field--group"}),L=o.search(),y=l("input",{className:"gemboss-field__bare"}),V=null;y.addEventListener("input",function(){a.query=y.value,F(),E()}),y.addEventListener("keydown",function(e){e.key==="Escape"&&(e.preventDefault(),Y(""),y.blur())}),v.appendChild(L),v.appendChild(y);var C=l("div",{className:"gemboss-assistant-threads"});f.appendChild(C);var z=[];function Y(e){a.query=e,y.value=e,F(),E()}function F(){var e;a.query?(e=l("button",{type:"button",className:"gemboss-assistant-search-clear","aria-label":n.clearSearch},o.close()),e.addEventListener("click",function(){Y("")})):e=l("kbd",{className:"gemboss-assistant-kbd"},"/"),V?v.replaceChild(e,V):v.appendChild(e),V=e}function M(){a.menuId!==null&&(a.menuId=null,U(),E())}function U(){A.forEach(function(e){e()}),A=[]}function Z(){U();var e=function(u){var m=C.querySelector(".gemboss-assistant-menu");(!m||!m.contains(u.target))&&M()},s=function(){M()};document.addEventListener("mousedown",e),g.addEventListener("scroll",s,!0),g.addEventListener("resize",s),A.push(function(){document.removeEventListener("mousedown",e),g.removeEventListener("scroll",s,!0),g.removeEventListener("resize",s)})}function $(e,s){if(a.menuId===s){M();return}var u=e.getBoundingClientRect(),m=184,c=200,h=u.bottom+c+8>g.innerHeight;a.menuLeft=Math.max(8,Math.min(u.right-m,g.innerWidth-m-8)),a.menuTop=h?Math.max(8,u.top-6-c):u.bottom+6,a.confirmDeleteId=null,a.menuId=s,Z(),E()}function ee(e){a.editingId=e.id,a.editValue=e.title,E()}function W(){a.editingId=null,a.editValue="",E()}function G(e){var s=a.editValue.trim();a.editingId=null,E(),!(!s||s===e.title)&&i.onRename&&i.onRename(e.id,s)}function D(e,s,u,m){var c=l("button",{type:"button",role:"menuitem",className:m?"danger":null},[e,l("span",null,s)]);return c.addEventListener("click",u),c}function te(e){var s=a.editingId===e.id,u=l("div",{className:"gemboss-assistant-thread"+(e.id===t.activeId||e.id===t.pendingId?" active":"")+(s?" editing":"")+(a.menuId===e.id?" menu-open":"")});if(s){var m=l("input",{className:"gemboss-field",maxlength:String(t.titleMaxLength)});m.value=a.editValue,m.addEventListener("input",function(){a.editValue=m.value}),m.addEventListener("keydown",function(x){x.key==="Enter"?(x.preventDefault(),G(e)):x.key==="Escape"&&(x.preventDefault(),W())});var c=l("button",{type:"button",className:"gemboss-assistant-thread-act","aria-label":n.save},o.check());c.addEventListener("click",function(){G(e)});var h=l("button",{type:"button",className:"gemboss-assistant-thread-act","aria-label":n.cancel},o.close());return h.addEventListener("click",W),u.appendChild(l("div",{className:"gemboss-assistant-thread-edit"},[m,c,h])),u}var I=l("span",{className:"gemboss-assistant-thread-titlerow"});t.busyIds.indexOf(e.id)>=0&&I.appendChild(l("span",{className:"gemboss-assistant-thread-running","aria-label":n.working})),e.pinned&&I.appendChild(o.pinFilled("gemboss-assistant-pin-dot")),I.appendChild(l("span",{className:"gemboss-assistant-thread-title"},e.title));var k=l("button",{type:"button",className:"gemboss-assistant-thread-main",title:e.title},[I,l("span",{className:"gemboss-assistant-thread-meta"},H(e.updatedAt,n)+(e.preview?" \xB7 "+e.preview:""))]);k.addEventListener("click",function(){i.onOpen&&i.onOpen(e.id)}),k.gbRailKey=e.id+":main";var R=l("button",{type:"button",className:"gemboss-assistant-thread-act","aria-label":n.options},o.more());if(R.addEventListener("mousedown",function(x){x.stopPropagation()}),R.addEventListener("click",function(){$(R,e.id)}),R.gbRailKey=e.id+":more",u.appendChild(k),u.appendChild(l("div",{className:"gemboss-assistant-thread-actions"},R)),a.menuId===e.id){var N=l("div",{className:"gemboss-popover-surface gemboss-assistant-menu",role:"menu"});N.style.position="fixed",N.style.top=a.menuTop+"px",N.style.left=a.menuLeft+"px",a.confirmDeleteId===e.id?(N.appendChild(l("div",{className:"gemboss-assistant-menu-confirm"},n.confirmRemove)),N.appendChild(D(o.remove(),n.remove,function(){M(),i.onDelete&&i.onDelete(e.id)},!0)),N.appendChild(D(o.close(),n.cancel,function(){a.confirmDeleteId=null,E()}))):(i.onTogglePin&&N.appendChild(D(e.pinned?o.pin():o.pinFilled(),e.pinned?n.unpin:n.pin,function(){M(),i.onTogglePin(e)})),N.appendChild(D(o.edit(),n.rename,function(){M(),ee(e)})),i.onExport&&N.appendChild(D(o.exportIcon(),n.exportMarkdown,function(){M(),i.onExport(e.id)})),N.appendChild(D(o.remove(),n.remove,function(){a.confirmDeleteId=e.id,E()},!0))),u.appendChild(N)}return u}function ne(){var e=P(a.query.trim());if(e){var s=t.sessions.filter(function(c){return P(c.title+" "+(c.preview||"")).indexOf(e)>=0});return s.length?[{label:"",items:s}]:[]}var u=_(t.sessions,n),m=t.unlistedIds.filter(function(c){return!t.sessions.some(function(h){return h.id===c})}).map(function(c){return{id:c,title:n.newChat,updatedAt:Date.now(),pinned:!1}});return m.length?[{label:n.openNow,items:m}].concat(u):u}function E(){for(var e=document.activeElement&&C.contains(document.activeElement)?document.activeElement:null,s=!!e&&e.tagName==="INPUT",u=e?e.gbRailKey:null;C.firstChild;)C.removeChild(C.firstChild);var m=ne();m.length?m.forEach(function(h){var I=l("div",{className:"gemboss-assistant-group"});h.label&&I.appendChild(l("div",{className:"gemboss-assistant-group-label"},h.label)),h.items.forEach(function(k){I.appendChild(te(k))}),C.appendChild(I)}):C.appendChild(l("div",{className:"gemboss-assistant-threads-empty"},a.query?n.noneFound:n.noneYet)),u&&Array.prototype.some.call(C.querySelectorAll("button"),function(h){return h.gbRailKey!==u?!1:(h.focus({preventScroll:!0}),!0)});var c=C.querySelector(".gemboss-assistant-thread-edit input");if(c&&(s||a.editingId!==null)){c.focus();try{c.setSelectionRange(c.value.length,c.value.length)}catch{}}}function K(){for(f.className="gemboss-assistant-rail"+(t.collapsed?" is-collapsed":"");w.firstChild;)w.removeChild(w.firstChild);for(w.title=t.newChatTitle!==void 0&&t.newChatTitle!==null?t.newChatTitle:n.newChat,w.appendChild(o.compose()),w.appendChild(l("span",null,n.newChat));p.firstChild;)p.removeChild(p.firstChild);p.setAttribute("aria-label",t.collapsed?n.expand:n.collapse),p.title=t.collapsed?n.expandTitle:n.collapseTitle,p.appendChild(t.collapsed?o.chevronRight():o.chevronLeft()),t.sessions.length>0?(y.setAttribute("placeholder",n.searchPlaceholder),y.setAttribute("aria-label",n.searchLabel),y.value!==a.query&&(y.value=a.query),F(),v.parentNode!==f&&f.insertBefore(v,C)):v.parentNode===f&&f.removeChild(v),E(),z.forEach(function(e){e.parentNode===f&&f.removeChild(e)}),z=t.footer?(Array.isArray(t.footer)?t.footer:[t.footer]).filter(j):[],z.forEach(function(e){f.appendChild(e)})}return K(),{el:f,update:function(e){T(e||{}),K()},focusSearch:function(){v.parentNode===f&&y.focus()},destroy:function(){U(),f.parentNode&&f.parentNode.removeChild(f)}}}})(typeof globalThis<"u"?globalThis:this);
@@ -16,10 +16,13 @@
16
16
  * still while it is open.
17
17
  */
18
18
  import "./settings-dialog.css";
19
- import React, { useEffect, useId, useMemo, useState } from "react";
19
+ import React, { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
20
20
  import { Icon } from "./icons";
21
21
  import { useModalDialog } from "./dialog";
22
22
 
23
+ // Before paint in a browser, so the strip never shows a frame at its old scroll; useEffect where there is no layout.
24
+ const useBeforePaint = typeof window === "undefined" ? useEffect : useLayoutEffect;
25
+
23
26
  const cx = (...p: Array<string | false | null | undefined>) => p.filter(Boolean).join(" ");
24
27
 
25
28
  export interface SettingsSectionItem {
@@ -99,6 +102,30 @@ export function SettingsDialog({
99
102
  // No silent fallback to the first section: the rail, the title and the popup's name would describe one section while the
100
103
  // surface's children show another (review #4073).
101
104
  const active = (sections as SettingsSectionItem[]).find((s) => s.id === activeId);
105
+ // Under 861px the rail is one strip that scrolls sideways, and opening on a section past its edge showed a row that did not
106
+ // say where you are: Domain in the GemBoss admin at 390px started at x=380 (21/10/2026). Bring the active one into view by
107
+ // the strip's own scroll, so only the strip moves. Again when the rail's sections change (a search cleared brings the
108
+ // active one back) and when the strip changes size (the dialog showing, the window crossing 861px, a font loading).
109
+ const strip = useRef<HTMLDivElement>(null);
110
+ useBeforePaint(() => {
111
+ const box = strip.current;
112
+ if (!open || !box) return undefined;
113
+ const reveal = () => {
114
+ const on = box.querySelector<HTMLElement>("[aria-current]");
115
+ if (!on) return;
116
+ const b = box.getBoundingClientRect();
117
+ const r = on.getBoundingClientRect();
118
+ if (r.left < b.left) box.scrollLeft -= b.left - r.left + 8;
119
+ else if (r.right > b.right) box.scrollLeft += r.right - b.right + 8;
120
+ if (r.top < b.top) box.scrollTop -= b.top - r.top + 8;
121
+ else if (r.bottom > b.bottom) box.scrollTop += r.bottom - b.bottom + 8;
122
+ };
123
+ reveal();
124
+ if (typeof ResizeObserver === "undefined") return undefined;
125
+ const ro = new ResizeObserver(reveal);
126
+ ro.observe(box);
127
+ return () => ro.disconnect();
128
+ }, [open, active?.id, groups]);
102
129
  const close = () => ref.current?.close();
103
130
  const item = (s: SettingsSectionItem) => {
104
131
  const on = s.id === active?.id;
@@ -125,7 +152,7 @@ export function SettingsDialog({
125
152
  onKeyDown={(e) => { if (e.key === "Escape") { e.preventDefault(); close(); } }} />
126
153
  </label>
127
154
  </div>
128
- <div className="ui-settings__scroll">
155
+ <div className="ui-settings__scroll" ref={strip}>
129
156
  {groups.length === 0 ? <p className="ui-settings__empty">{noMatch(query)}</p> : groups.map((g, i) => (
130
157
  <div key={`${i}:${g.title}`} className="ui-settings__group">
131
158
  {g.title ? <h3 className="ui-settings__group-title">{g.title}</h3> : null}
package/src/shell.css CHANGED
@@ -1 +1 @@
1
- .gemboss-popover-surface{background:var(--gemboss-surface);background:color-mix(in srgb,var(--gemboss-surface) 93%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.4);backdrop-filter:blur(14px) saturate(1.4);border:1px solid var(--gemboss-divider);border-radius:var(--gemboss-radius-lg);box-shadow:var(--gemboss-shadow-raised)}@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){.gemboss-popover-surface{background:var(--gemboss-surface)}}.icon-nav{position:fixed;top:56px;left:0;z-index:200;height:calc(100dvh - 56px);background:var(--gemboss-rail-bg);border-right:1px solid var(--gemboss-divider);display:flex;flex-direction:column;padding-top:8px;overflow:hidden}.gemboss-account-menu{position:absolute;top:calc(100% + 6px);right:0;min-width:220px;z-index:320;overflow:hidden}.gemboss-account-menu-item,.ui-menu__item{display:block;box-sizing:border-box;width:100%;min-height:32px;padding:4px 16px;border:0;background:transparent;font:inherit;font-size:13px;color:var(--gemboss-text);text-align:left;text-decoration:none;cursor:pointer;transition:background var(--gemboss-transition-fast) ease,color var(--gemboss-transition-fast) ease}.ui-menu__item{padding-inline:6px}@media (pointer: coarse),(max-width: 47.9975em){.gemboss-account-menu-item,.ui-menu__item{min-height:44px;padding:12px 16px}}.gemboss-account-menu-item:hover,.ui-menu__item:hover{background:var(--gemboss-hover)}.gemboss-account-menu-item:focus-visible,.ui-menu__item:focus-visible{outline:2px solid var(--gemboss-primary);outline-offset:-2px;background:var(--gemboss-hover)}.gemboss-account-menu-item.danger,.ui-menu__item--danger{color:var(--gemboss-danger-strong)}.gemboss-account-chip{width:30px;height:30px;border:1px solid #cfd6ce;border-radius:var(--gemboss-radius-full);background:#f5f7f4;color:#303633;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;font-size:11px;font-weight:700;cursor:pointer}.gemboss-platform-main{box-sizing:border-box;min-height:100dvh;padding-top:56px;background:var(--gemboss-app-bg)}.icon-nav{top:56px;height:calc(100dvh - 56px);background:var(--gemboss-nav-bg);border-right-color:var(--gemboss-divider)}@media (max-width: 1040px){.gemboss-platform-topbar{grid-template-columns:minmax(120px,1fr) minmax(220px,520px) minmax(120px,1fr)}}@media (max-width: 760px){.gemboss-platform-main{margin-left:0}}.gemboss-account-chip{width:32px;height:32px;border-radius:50%;background:var(--gemboss-primary);color:#fff;font-weight:600;font-size:12px;display:inline-flex;align-items:center;justify-content:center;border:none;cursor:pointer;transition:transform var(--gemboss-transition-base) ease}.gemboss-account-chip:hover{transform:scale(1.05)}.gemboss-account-chip:focus-visible{outline:2px solid var(--gemboss-primary);outline-offset:2px}.gemboss-platform-main{margin-top:56px;min-height:calc(100vh - 56px);padding:24px;background:#fafafb}@media (max-width: 768px){.gemboss-platform-main{margin-left:0;padding:16px}}.gemboss-platform-shell{display:block;min-height:100vh;background:var(--gemboss-app-bg)}.gemboss-platform-topbar{position:fixed;inset:0 0 auto;z-index:300;height:56px;border-bottom:1px solid var(--gemboss-divider);background:var(--gemboss-topbar-bg);display:grid;grid-template-columns:minmax(200px,1fr) minmax(280px,560px) minmax(200px,1fr);align-items:center;gap:16px;padding:0 16px;box-shadow:var(--gemboss-shadow-resting)}:root:has(.gemboss-platform-topbar){scroll-padding-top:64px}.gemboss-account-chip{width:32px;height:32px;border:1px solid var(--gemboss-chip-line);border-radius:var(--gemboss-radius-full);background:var(--gemboss-surface);color:var(--gemboss-chip-ink);display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;font-size:11px;font-weight:700;cursor:pointer;transition:border-color var(--gemboss-transition-base) ease,background-color var(--gemboss-transition-base) ease}.gemboss-account-chip:hover{border-color:var(--gemboss-chip-line-hover);background:var(--gemboss-chip-bg-hover);transform:none}.icon-nav{position:fixed;top:56px;left:0;z-index:200;height:calc(100dvh - 56px);border-right:1px solid var(--gemboss-divider);background:var(--gemboss-nav-bg);padding-top:12px}.gemboss-platform-main{min-height:calc(100vh - 56px);margin-top:56px;background:var(--gemboss-app-bg);padding:24px}@media (max-width: 1120px){.gemboss-platform-topbar{grid-template-columns:220px minmax(220px,1fr) auto;gap:12px}}@media (max-width: 760px){.gemboss-platform-topbar{height:auto;min-height:56px;grid-template-columns:minmax(0,1fr) auto;padding:12px}.gemboss-account-chip,.icon-nav{display:none}.gemboss-platform-main{margin-left:0;padding:16px}}.gnav-scroll{scrollbar-width:thin;scrollbar-color:rgba(31,30,29,.22) transparent}.gemboss-assistant-railfoot.is-button{width:100%;border:none;background:none;cursor:pointer;text-align:left;font:inherit;border-radius:8px;transition:background .12s ease,color .12s ease}.gemboss-assistant-railfoot.is-button:hover{background:var(--gemboss-primary-soft);color:var(--gemboss-primary-dark)}.gemboss-assistant-empty{padding:24px 20px;color:var(--gemboss-text-muted);font-size:13px}@media (max-width: 760px){.gemboss-platform-topbar{display:flex;align-items:center;gap:8px;height:56px;min-height:56px;padding:0 12px}.gemboss-account-chip{display:inline-flex}.gemboss-platform-main{padding-top:16px;padding-bottom:calc(56px + env(safe-area-inset-bottom))}}@media (pointer: coarse){.gemboss-account-chip{width:44px;height:44px}.gnav-item{min-height:44px}.gemboss-assistant-collapse,.gemboss-assistant-newchat{min-height:44px;min-width:44px}}.icon-nav{width:240px;padding:12px 0 0;overflow:hidden;background:var(--gemboss-nav-bg);display:flex;flex-direction:column}.gnav-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding-inline:12px}.gnav-bottom{padding:0 12px 12px}.gnav-item{display:flex;align-items:center;gap:12px;width:100%;padding:8px 12px;margin:1px 0;border:0;background:transparent;cursor:pointer;text-align:left;border-radius:var(--gemboss-radius-md);text-decoration:none;font-family:inherit;color:var(--gemboss-text);font-size:var(--gemboss-fs-body);font-weight:500;line-height:1.2;transition:background-color var(--gemboss-transition-fast) ease,color var(--gemboss-transition-fast) ease}.gnav-item:hover{background:var(--gemboss-nav-hover)}.gnav-item:focus-visible{outline:2px solid var(--gemboss-accent);outline-offset:-2px}.gnav-item.active{color:var(--gemboss-active-fg);font-weight:600}.gnav-group:not(.has-active-child)>.gnav-item.active{background:var(--gemboss-active-bg)}.gnav-group:not(.has-active-child)>.gnav-item.active:hover{background:var(--gemboss-active-bg)}.gnav-group.has-active-child>.gnav-item.active:hover{background:var(--gemboss-nav-hover)}.gnav-ico{flex:0 0 20px;width:20px;height:20px;display:grid;place-items:center}.gnav-ico svg{width:20px;height:20px;fill:currentColor}.gnav-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.gnav-sub{list-style:none;margin:1px 0 6px;padding:0}.gnav-subitem{position:relative;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 12px 6px 44px;margin:1px 0;border-radius:var(--gemboss-radius-sm);color:var(--gemboss-text-muted);font-size:13px;line-height:1.2;text-decoration:none;white-space:nowrap;transition:background-color var(--gemboss-transition-fast) ease,color var(--gemboss-transition-fast) ease}@media (pointer: coarse){.gnav-subitem{min-height:44px}}.gnav-subitem:hover{background:var(--gemboss-nav-hover);color:var(--gemboss-text)}.gnav-subitem.active{color:var(--gemboss-active-fg);font-weight:600;background:var(--gemboss-active-bg)}.gnav-subitem.active:hover{background:var(--gemboss-active-bg);color:var(--gemboss-active-fg)}.gnav-subitem.active:before{content:"";position:absolute;left:20px;top:50%;transform:translateY(-50%);width:3px;height:15px;border-radius:2px;background:var(--gemboss-primary)}.gnav-subitem.is-soon{opacity:.5;cursor:default}.gnav-subitem em{font-style:normal;font-size:var(--gemboss-fs-micro);font-weight:600;letter-spacing:.04em;text-transform:uppercase;border:1px solid var(--gemboss-divider);border-radius:var(--gemboss-radius-sm);padding:1px 6px}.gnav-sep{height:1px;background:var(--gemboss-divider);margin:8px 6px}.gnav-item.gnav-assistant{color:var(--gemboss-accent-strong);font-weight:600;background:var(--gemboss-accent-soft)}.gnav-item.gnav-assistant .gnav-ico svg{fill:var(--gemboss-accent)}.gnav-item.gnav-assistant:hover{background:color-mix(in srgb,var(--gemboss-accent) 16%,var(--gemboss-surface))}.gnav-item.gnav-assistant.active{background:var(--gemboss-active-bg);color:var(--gemboss-active-fg)}.gnav-item.gnav-assistant.active .gnav-ico svg{fill:var(--gemboss-active-fg)}.gemboss-platform-main{margin-left:240px}.gemboss-mobile-menu-btn,.gemboss-mobile-nav-backdrop{display:none}@media (max-width: 760px){.gemboss-platform-main{margin-left:0;padding-bottom:env(safe-area-inset-bottom)}.gemboss-mobile-menu-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:38px;height:38px;margin-left:-2px;border:0;border-radius:8px;background:transparent;cursor:pointer;color:var(--gemboss-text)}.gemboss-mobile-menu-btn:active{background:var(--gemboss-hover)}.icon-nav{display:flex;width:min(82vw,300px);transform:translate(-100%);transition:transform .26s cubic-bezier(.4,0,.2,1),box-shadow .26s cubic-bezier(.4,0,.2,1);z-index:1000;box-shadow:none}.gemboss-platform-shell.is-mobile-nav-open .icon-nav{transform:translate(0);box-shadow:2px 0 26px #1f1e1d33}.gemboss-mobile-nav-backdrop{display:block;position:fixed;inset:56px 0 0;width:100%;border:0;padding:0;background:#14131261;opacity:0;pointer-events:none;transition:opacity .22s ease;z-index:999}.gemboss-platform-shell.is-mobile-nav-open .gemboss-mobile-nav-backdrop{opacity:1;pointer-events:auto}}@media (max-width: 305.98px){.gemboss-platform-topbar{padding:0 8px}}@media (min-width: 306px) and (max-width: 460px){.gemboss-platform-topbar{padding:0 8px}.gemboss-mobile-menu-btn,.gemboss-account-chip{width:44px;height:44px}}.gemboss-assistant-chatslot{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;min-width:0}.gemboss-assistant-chatslot[hidden]{display:none}.gemboss-assistant-page>.gemboss-assistant-tabs{flex:0 0 auto;display:flex;align-items:center;gap:4px;padding:4px 8px 0;overflow-x:auto;scrollbar-width:none}.gemboss-assistant-tabs::-webkit-scrollbar{display:none}.gemboss-assistant-tab{display:flex;align-items:center;flex:0 1 auto;min-width:0;max-width:190px;border:1px solid var(--gemboss-divider);border-radius:var(--gemboss-radius-md);background:transparent}.gemboss-assistant-tab.active{background:var(--gemboss-active-bg);border-color:var(--gemboss-active-bg)}.gemboss-assistant-tab-main{display:flex;align-items:center;gap:6px;min-width:0;min-height:44px;padding:0 4px 0 8px;border:0;background:transparent;font:inherit;font-size:var(--gemboss-fs-caption);color:var(--gemboss-text-muted);cursor:pointer}.gemboss-assistant-tab.active .gemboss-assistant-tab-main{color:var(--gemboss-text-strong)}.gemboss-assistant-tab-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gemboss-assistant-tab-dot{flex:0 0 auto;width:6px;height:6px;border-radius:var(--gemboss-radius-full);background:var(--gemboss-divider)}.gemboss-assistant-tab-dot.is-running{background:var(--gemboss-primary);animation:gemboss-assistant-pulse 1.4s ease-in-out infinite}.gemboss-assistant-tab-close{flex:0 0 auto;width:44px;height:44px;display:grid;place-items:center;border:0;background:transparent;color:var(--gemboss-text-muted);cursor:pointer;padding:0}.gemboss-assistant-tab-close:hover{color:var(--gemboss-text-strong)}.gemboss-assistant-tab-close svg{width:14px;height:14px}.gemboss-assistant-sronly{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.gemboss-assistant-thread-running{flex:0 0 auto;width:6px;height:6px;border-radius:var(--gemboss-radius-full);background:var(--gemboss-primary);animation:gemboss-assistant-pulse 1.4s ease-in-out infinite}@media (prefers-reduced-motion: reduce){.gemboss-assistant-tab-dot.is-running,.gemboss-assistant-thread-running{animation:none}}.gemboss-assistant-route-claim{display:block;height:100%}.gemboss-assistant-layout.is-dock{height:auto;min-height:0}.gemboss-assistant-layout.is-dock .gemboss-assistant-page{height:auto;max-width:none;padding:4px 0 8px}.gnav-item.gnav-assistant{position:relative}body.gemboss-assistant-busy .gnav-item.gnav-assistant:after,body.gemboss-assistant-unread .gnav-item.gnav-assistant:after{content:"";position:absolute;top:6px;left:26px;width:8px;height:8px;border-radius:50%;border:2px solid var(--gemboss-surface)}body.gemboss-assistant-busy .gnav-item.gnav-assistant:after{background:var(--gemboss-primary);animation:gemboss-assistant-pulse 1.4s ease-in-out infinite}body.gemboss-assistant-unread .gnav-item.gnav-assistant:after{background:var(--gemboss-success-strong)}@media (prefers-reduced-motion: reduce){body.gemboss-assistant-busy .gnav-item.gnav-assistant:after{animation:none}}.gemboss-assistant-layout{display:flex;height:calc(100dvh - 56px);width:100%;min-height:0}.gemboss-assistant-rail{flex:0 0 264px;width:264px;display:flex;flex-direction:column;min-height:0;border-right:1px solid var(--gemboss-divider);background:transparent;padding:16px 12px 12px;gap:12px;transition:flex-basis .18s ease,width .18s ease}.gemboss-assistant-rail-top{display:flex;align-items:center;gap:6px}.gemboss-assistant-newchat{display:flex;align-items:center;gap:8px;flex:1 1 auto;min-width:0;padding:12px;border:1px solid var(--gemboss-divider);border-radius:12px;background:transparent;color:var(--gemboss-text);font-size:var(--gemboss-fs-body);font-weight:600;cursor:pointer;transition:background .12s ease,border-color .12s ease}.gemboss-assistant-newchat:hover{background:#00000009;border-color:var(--gemboss-text-muted)}.gemboss-assistant-newchat svg{width:17px;height:17px;fill:currentColor;flex:0 0 auto}.gemboss-assistant-newchat span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto}.gemboss-assistant-kbd{flex:0 0 auto;font-size:var(--gemboss-fs-micro);font-weight:600;line-height:1;padding:4px 6px;border:1px solid var(--gemboss-divider);border-radius:6px;background:transparent;color:var(--gemboss-text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.gemboss-assistant-search:focus-within .gemboss-assistant-kbd{display:none}.gemboss-assistant-collapse{flex:0 0 auto;display:grid;place-items:center;width:34px;height:34px;border:0;border-radius:8px;background:transparent;color:var(--gemboss-text-muted);cursor:pointer;transition:background .12s ease,color .12s ease}.gemboss-assistant-collapse:hover{background:#0000000d;color:var(--gemboss-text)}.gemboss-assistant-collapse svg{width:16px;height:16px;fill:currentColor}.gemboss-assistant-search{gap:6px}.gemboss-assistant-search>svg{width:15px;height:15px;fill:var(--gemboss-text-muted);flex:0 0 auto}.gemboss-assistant-search-clear{display:grid;place-items:center;width:20px;height:20px;border:0;border-radius:6px;background:transparent;color:var(--gemboss-text-muted);cursor:pointer}.gemboss-assistant-search-clear:hover{background:#00000012;color:var(--gemboss-text)}.gemboss-assistant-search-clear svg{width:13px;height:13px;fill:currentColor}.gemboss-assistant-railfoot{flex:0 0 auto;display:flex;align-items:center;gap:6px;padding:8px 12px 2px;border-top:1px solid var(--gemboss-divider);margin-top:4px;color:var(--gemboss-text-muted);font-size:var(--gemboss-fs-caption);line-height:1.35}.gemboss-assistant-railfoot svg{width:13px;height:13px;flex:0 0 auto;fill:currentColor;opacity:.8}.gemboss-assistant-threads{flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:2px}.gemboss-assistant-threads-empty{padding:16px 8px;color:var(--gemboss-text-muted);font-size:var(--gemboss-fs-caption)}.gemboss-assistant-group{display:flex;flex-direction:column;gap:2px}.gemboss-assistant-group+.gemboss-assistant-group{margin-top:12px}.gemboss-assistant-group-label{padding:4px 12px;font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--gemboss-text-muted);text-transform:uppercase}.gemboss-assistant-thread{position:relative;display:flex;align-items:center;border-radius:8px;transition:background .12s ease}.gemboss-assistant-thread:hover{background:var(--gemboss-hover, rgba(0,0,0,.04))}.gemboss-assistant-thread.active{background:#00000012}.gemboss-assistant-thread-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:2px;padding:8px 12px;border:0;background:transparent;text-align:left;cursor:pointer}.gemboss-assistant-thread-titlerow{display:flex;align-items:center;gap:6px;width:100%;min-width:0}.gemboss-assistant-pin-dot{width:12px;height:12px;flex:0 0 auto;fill:var(--gemboss-text-muted)}.gemboss-assistant-thread-title{flex:1 1 auto;min-width:0;font-size:13px;font-weight:500;color:var(--gemboss-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.gemboss-assistant-thread-meta{font-size:var(--gemboss-fs-caption);color:var(--gemboss-text-muted)}.gemboss-assistant-thread.menu-open{background:#00000012}.gemboss-assistant-thread.menu-open .gemboss-assistant-thread-actions{opacity:1}.gemboss-assistant-menu{z-index:60;width:184px;padding:6px;display:flex;flex-direction:column}.gemboss-assistant-menu-confirm{padding:8px 12px 6px;font-size:var(--gemboss-fs-caption);font-weight:600;color:var(--gemboss-text)}.gemboss-assistant-menu button{display:flex;align-items:center;gap:8px;width:100%;padding:8px 12px;border:0;border-radius:8px;background:transparent;text-align:left;font-size:13px;color:var(--gemboss-text);cursor:pointer}.gemboss-assistant-menu button:hover{background:var(--gemboss-hover, rgba(0,0,0,.05))}.gemboss-assistant-menu button.danger{color:var(--gemboss-danger-strong)}.gemboss-assistant-menu button svg{width:15px;height:15px;fill:currentColor;flex:0 0 auto}.gemboss-assistant-thread-actions{position:absolute;right:6px;display:flex;gap:2px;opacity:0;transition:opacity .12s ease}.gemboss-assistant-thread:hover .gemboss-assistant-thread-actions,.gemboss-assistant-thread:focus-within .gemboss-assistant-thread-actions{opacity:1}.gemboss-assistant-thread-act{display:grid;place-items:center;width:26px;height:26px;border:0;border-radius:8px;background:transparent;color:var(--gemboss-text-muted);cursor:pointer;transition:background .12s ease,color .12s ease}.gemboss-assistant-thread-act:hover{background:#00000012;color:var(--gemboss-text)}.gemboss-assistant-thread-act.danger:hover{color:var(--gemboss-danger-strong)}.gemboss-assistant-thread-act svg{width:15px;height:15px;fill:currentColor}.gemboss-assistant-thread.editing{background:#00000012}.gemboss-assistant-thread-edit{display:flex;align-items:center;gap:2px;width:100%;padding:6px}.gemboss-assistant-thread-edit input{flex:1 1 auto;min-width:0}.gemboss-assistant-layout .gemboss-assistant-page{height:100%;max-width:820px;flex:1 1 auto;min-width:0;padding-top:16px}.gemboss-assistant-layout .gemboss-assistant-page.is-switching{opacity:.55;transition:opacity .12s ease;pointer-events:none}.gemboss-assistant-rail.is-collapsed{flex-basis:60px;width:60px;padding:16px 8px 12px}.gemboss-assistant-rail.is-collapsed .gemboss-assistant-rail-top{flex-direction:column;gap:8px}.gemboss-assistant-rail.is-collapsed .gemboss-assistant-newchat{justify-content:center;padding:8px;flex:0 0 auto}.gemboss-assistant-rail.is-collapsed .gemboss-assistant-newchat span,.gemboss-assistant-rail.is-collapsed .gemboss-assistant-search,.gemboss-assistant-rail.is-collapsed .gemboss-assistant-railfoot,.gemboss-assistant-rail.is-collapsed .gemboss-assistant-threads{display:none}@media (max-width: 760px){.gemboss-assistant-rail{flex-basis:60px;width:60px;padding:16px 8px 12px}.gemboss-assistant-rail .gemboss-assistant-rail-top{flex-direction:column;gap:8px}.gemboss-assistant-newchat{justify-content:center;padding:8px;flex:0 0 auto}.gemboss-assistant-newchat span,.gemboss-assistant-search,.gemboss-assistant-railfoot,.gemboss-assistant-threads,.gemboss-assistant-collapse{display:none}}.gemboss-assistant-page{height:calc(100dvh - 56px);width:100%;max-width:760px;margin:0 auto;padding:24px 0 16px;display:flex;flex-direction:column;min-height:0}.gemboss-assistant-page>*{flex:1 1 auto;min-height:0}.gemboss-assistant-empty{flex:1;display:grid;place-items:center;color:var(--gemboss-text-muted);font-size:14px}.gemboss-platform-shell{display:flow-root}.gemboss-platform-main{padding-bottom:calc(24px + var(--gemboss-invite-h, 0px))}@keyframes gemboss-assistant-pulse{0%,to{opacity:1}50%{opacity:.35}}.gb-account{position:relative}.gb-account>summary{list-style:none}.gb-account>summary::-webkit-details-marker{display:none}.gb-account-head{padding:12px 16px;border-bottom:1px solid var(--gemboss-divider)}.gb-account-name{font-size:13px;font-weight:600;color:var(--gemboss-text-strong)}.gb-account-email{font-size:12px;color:var(--gemboss-text-muted)}.gb-account-role{display:inline-block;margin-top:4px;padding:1px 6px;border-radius:4px;font-size:11px;font-weight:600;color:var(--gemboss-active-fg);background:var(--gemboss-active-bg)}.gb-account-form{margin:0}.gnav-scroll::-webkit-scrollbar{width:9px}.gnav-scroll::-webkit-scrollbar-track{background:transparent}.gnav-scroll::-webkit-scrollbar-thumb{background:#1f1e1d33;border-radius:6px;border:2px solid transparent;background-clip:content-box}
1
+ .gemboss-popover-surface{background:var(--gemboss-surface);background:color-mix(in srgb,var(--gemboss-surface) 93%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.4);backdrop-filter:blur(14px) saturate(1.4);border:1px solid var(--gemboss-divider);border-radius:var(--gemboss-radius-lg);box-shadow:var(--gemboss-shadow-raised)}@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){.gemboss-popover-surface{background:var(--gemboss-surface)}}.icon-nav{position:fixed;top:56px;left:0;z-index:200;height:calc(100dvh - 56px);background:var(--gemboss-rail-bg);border-right:1px solid var(--gemboss-divider);display:flex;flex-direction:column;padding-top:8px;overflow:hidden}.gemboss-account-menu{position:absolute;top:calc(100% + 6px);right:0;min-width:220px;z-index:320;overflow:hidden}.gemboss-account-menu-item,.ui-menu__item{box-sizing:border-box;width:100%;min-height:32px;padding:4px 16px;border:0;background:transparent;font:inherit;font-size:13px;color:var(--gemboss-text);text-align:left;text-decoration:none;cursor:pointer;transition:background var(--gemboss-transition-fast) ease,color var(--gemboss-transition-fast) ease}.gemboss-account-menu-item{display:block}.ui-menu__item{display:flex;align-items:center;gap:8px;padding-inline:6px;white-space:nowrap;border-radius:var(--gemboss-radius-sm)}@media (pointer: coarse),(max-width: 47.9975em){.gemboss-account-menu-item,.ui-menu__item{min-height:44px;padding:12px 16px}}.gemboss-account-menu-item:hover,.ui-menu__item:hover{background:var(--gemboss-hover)}.gemboss-account-menu-item:focus-visible,.ui-menu__item:focus-visible{outline:2px solid var(--gemboss-primary);outline-offset:-2px;background:var(--gemboss-hover)}.gemboss-account-menu-item.danger,.ui-menu__item--danger{color:var(--gemboss-danger-strong)}.gemboss-account-chip{width:30px;height:30px;border:1px solid #cfd6ce;border-radius:var(--gemboss-radius-full);background:#f5f7f4;color:#303633;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;font-size:11px;font-weight:700;cursor:pointer}.gemboss-platform-main{box-sizing:border-box;min-height:100dvh;padding-top:56px;background:var(--gemboss-app-bg)}.icon-nav{top:56px;height:calc(100dvh - 56px);background:var(--gemboss-nav-bg);border-right-color:var(--gemboss-divider)}@media (max-width: 760px){.gemboss-platform-main{margin-left:0}}.gemboss-account-chip{width:32px;height:32px;border-radius:50%;background:var(--gemboss-primary);color:#fff;font-weight:600;font-size:12px;display:inline-flex;align-items:center;justify-content:center;border:none;cursor:pointer;transition:transform var(--gemboss-transition-base) ease}.gemboss-account-chip:hover{transform:scale(1.05)}.gemboss-account-chip:focus-visible{outline:2px solid var(--gemboss-primary);outline-offset:2px}.gemboss-platform-main{margin-top:56px;min-height:calc(100vh - 56px);padding:24px;background:#fafafb}@media (max-width: 768px){.gemboss-platform-main{margin-left:0;padding:16px}}.gemboss-platform-shell{display:block;min-height:100vh;background:var(--gemboss-app-bg)}.gemboss-platform-topbar{position:fixed;inset:0 0 auto;z-index:300;height:56px;border-bottom:1px solid var(--gemboss-divider);background:var(--gemboss-topbar-bg);display:grid;grid-template-columns:minmax(200px,1fr) minmax(0,560px) minmax(max-content,1fr);align-items:center;gap:16px;padding:0 16px;box-shadow:var(--gemboss-shadow-resting)}:root:has(.gemboss-platform-topbar){scroll-padding-top:64px}.gemboss-account-chip{width:32px;height:32px;border:1px solid var(--gemboss-chip-line);border-radius:var(--gemboss-radius-full);background:var(--gemboss-surface);color:var(--gemboss-chip-ink);display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;font-size:11px;font-weight:700;cursor:pointer;transition:border-color var(--gemboss-transition-base) ease,background-color var(--gemboss-transition-base) ease}.gemboss-account-chip:hover{border-color:var(--gemboss-chip-line-hover);background:var(--gemboss-chip-bg-hover);transform:none}.icon-nav{position:fixed;top:56px;left:0;z-index:200;height:calc(100dvh - 56px);border-right:1px solid var(--gemboss-divider);background:var(--gemboss-nav-bg);padding-top:12px}.gemboss-platform-main{min-height:calc(100vh - 56px);margin-top:56px;background:var(--gemboss-app-bg);padding:24px}@media (max-width: 1120px){.gemboss-platform-topbar{grid-template-columns:220px minmax(0,1fr) max-content;gap:12px}}@media (max-width: 760px){.gemboss-platform-topbar{height:auto;min-height:56px;grid-template-columns:minmax(0,1fr) auto;padding:12px}.gemboss-account-chip,.icon-nav{display:none}.gemboss-platform-main{margin-left:0;padding:16px}}.gnav-scroll{scrollbar-width:thin;scrollbar-color:rgba(31,30,29,.22) transparent}.gemboss-assistant-railfoot.is-button{width:100%;border:none;background:none;cursor:pointer;text-align:left;font:inherit;border-radius:8px;transition:background .12s ease,color .12s ease}.gemboss-assistant-railfoot.is-button:hover{background:var(--gemboss-primary-soft);color:var(--gemboss-primary-dark)}.gemboss-assistant-empty{padding:24px 20px;color:var(--gemboss-text-muted);font-size:13px}@media (max-width: 760px){.gemboss-platform-topbar{display:flex;align-items:center;gap:8px;height:56px;min-height:56px;padding:0 12px}.gemboss-account-chip{display:inline-flex}.gemboss-platform-main{padding-top:16px;padding-bottom:calc(56px + env(safe-area-inset-bottom))}}@media (pointer: coarse){.gemboss-account-chip{width:44px;height:44px}.gnav-item{min-height:44px}.gemboss-assistant-collapse,.gemboss-assistant-newchat{min-height:44px;min-width:44px}}.icon-nav{width:240px;padding:12px 0 0;overflow:hidden;background:var(--gemboss-nav-bg);display:flex;flex-direction:column}.gnav-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding-inline:12px}.gnav-bottom{padding:0 12px 12px}.gnav-item{display:flex;align-items:center;gap:12px;width:100%;padding:8px 12px;margin:1px 0;border:0;background:transparent;cursor:pointer;text-align:left;border-radius:var(--gemboss-radius-md);text-decoration:none;font-family:inherit;color:var(--gemboss-text);font-size:var(--gemboss-fs-body);font-weight:500;line-height:1.2;transition:background-color var(--gemboss-transition-fast) ease,color var(--gemboss-transition-fast) ease}.gnav-item:hover{background:var(--gemboss-nav-hover)}.gnav-item:focus-visible{outline:2px solid var(--gemboss-accent);outline-offset:-2px}.gnav-item.active{color:var(--gemboss-active-fg);font-weight:600}.gnav-group:not(.has-active-child)>.gnav-item.active{background:var(--gemboss-active-bg)}.gnav-group:not(.has-active-child)>.gnav-item.active:hover{background:var(--gemboss-active-bg)}.gnav-group.has-active-child>.gnav-item.active:hover{background:var(--gemboss-nav-hover)}.gnav-ico{flex:0 0 20px;width:20px;height:20px;display:grid;place-items:center}.gnav-ico svg{width:20px;height:20px;fill:currentColor}.gnav-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.gnav-sub{list-style:none;margin:1px 0 6px;padding:0}.gnav-subitem{position:relative;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 12px 6px 44px;margin:1px 0;border-radius:var(--gemboss-radius-sm);color:var(--gemboss-text-muted);font-size:13px;line-height:1.2;text-decoration:none;white-space:nowrap;transition:background-color var(--gemboss-transition-fast) ease,color var(--gemboss-transition-fast) ease}@media (pointer: coarse){.gnav-subitem{min-height:44px}}.gnav-subitem:hover{background:var(--gemboss-nav-hover);color:var(--gemboss-text)}.gnav-subitem.active{color:var(--gemboss-active-fg);font-weight:600;background:var(--gemboss-active-bg)}.gnav-subitem.active:hover{background:var(--gemboss-active-bg);color:var(--gemboss-active-fg)}.gnav-subitem.active:before{content:"";position:absolute;left:20px;top:50%;transform:translateY(-50%);width:3px;height:15px;border-radius:2px;background:var(--gemboss-primary)}.gnav-subitem.is-soon{opacity:.5;cursor:default}.gnav-subitem em{font-style:normal;font-size:var(--gemboss-fs-micro);font-weight:600;letter-spacing:.04em;text-transform:uppercase;border:1px solid var(--gemboss-divider);border-radius:var(--gemboss-radius-sm);padding:1px 6px}.gnav-sep{height:1px;background:var(--gemboss-divider);margin:8px 6px}.gnav-item.gnav-assistant{color:var(--gemboss-accent-strong);font-weight:600;background:var(--gemboss-accent-soft)}.gnav-item.gnav-assistant .gnav-ico svg{fill:var(--gemboss-accent)}.gnav-item.gnav-assistant:hover{background:color-mix(in srgb,var(--gemboss-accent) 16%,var(--gemboss-surface))}.gnav-item.gnav-assistant.active{background:var(--gemboss-active-bg);color:var(--gemboss-active-fg)}.gnav-item.gnav-assistant.active .gnav-ico svg{fill:var(--gemboss-active-fg)}.gemboss-platform-main{margin-left:240px}.gemboss-mobile-menu-btn,.gemboss-mobile-nav-backdrop{display:none}@media (max-width: 760px){.gemboss-platform-main{margin-left:0;padding-bottom:env(safe-area-inset-bottom)}.gemboss-mobile-menu-btn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:38px;height:38px;margin-left:-2px;border:0;border-radius:8px;background:transparent;cursor:pointer;color:var(--gemboss-text)}.gemboss-mobile-menu-btn:active{background:var(--gemboss-hover)}.icon-nav{display:flex;width:min(82vw,300px);transform:translate(-100%);transition:transform .26s cubic-bezier(.4,0,.2,1),box-shadow .26s cubic-bezier(.4,0,.2,1);z-index:1000;box-shadow:none}.gemboss-platform-shell.is-mobile-nav-open .icon-nav{transform:translate(0);box-shadow:2px 0 26px #1f1e1d33}.gemboss-mobile-nav-backdrop{display:block;position:fixed;inset:56px 0 0;width:100%;border:0;padding:0;background:#14131261;opacity:0;pointer-events:none;transition:opacity .22s ease;z-index:999}.gemboss-platform-shell.is-mobile-nav-open .gemboss-mobile-nav-backdrop{opacity:1;pointer-events:auto}}@media (max-width: 305.98px){.gemboss-platform-topbar{padding:0 8px}}@media (min-width: 306px) and (max-width: 460px){.gemboss-platform-topbar{padding:0 8px}.gemboss-mobile-menu-btn,.gemboss-account-chip{width:44px;height:44px}}.gemboss-assistant-chatslot{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;min-width:0}.gemboss-assistant-chatslot[hidden]{display:none}.gemboss-assistant-page>.gemboss-assistant-tabs{flex:0 0 auto;display:flex;align-items:center;gap:4px;padding:4px 8px 0;overflow-x:auto;scrollbar-width:none}.gemboss-assistant-tabs::-webkit-scrollbar{display:none}.gemboss-assistant-tab{display:flex;align-items:center;flex:0 1 auto;min-width:0;max-width:190px;border:1px solid var(--gemboss-divider);border-radius:var(--gemboss-radius-md);background:transparent}.gemboss-assistant-tab.active{background:var(--gemboss-active-bg);border-color:var(--gemboss-active-bg)}.gemboss-assistant-tab-main{display:flex;align-items:center;gap:6px;min-width:0;min-height:44px;padding:0 4px 0 8px;border:0;background:transparent;font:inherit;font-size:var(--gemboss-fs-caption);color:var(--gemboss-text-muted);cursor:pointer}.gemboss-assistant-tab.active .gemboss-assistant-tab-main{color:var(--gemboss-text-strong)}.gemboss-assistant-tab-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gemboss-assistant-tab-dot{flex:0 0 auto;width:6px;height:6px;border-radius:var(--gemboss-radius-full);background:var(--gemboss-divider)}.gemboss-assistant-tab-dot.is-running{background:var(--gemboss-primary);animation:gemboss-assistant-pulse 1.4s ease-in-out infinite}.gemboss-assistant-tab-close{flex:0 0 auto;width:44px;height:44px;display:grid;place-items:center;border:0;background:transparent;color:var(--gemboss-text-muted);cursor:pointer;padding:0}.gemboss-assistant-tab-close:hover{color:var(--gemboss-text-strong)}.gemboss-assistant-tab-close svg{width:14px;height:14px}.gemboss-assistant-sronly{position:absolute;left:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.gemboss-assistant-thread-running{flex:0 0 auto;width:6px;height:6px;border-radius:var(--gemboss-radius-full);background:var(--gemboss-primary);animation:gemboss-assistant-pulse 1.4s ease-in-out infinite}@media (prefers-reduced-motion: reduce){.gemboss-assistant-tab-dot.is-running,.gemboss-assistant-thread-running{animation:none}}.gemboss-assistant-route-claim{display:block;height:100%}.gemboss-assistant-layout.is-dock{height:auto;min-height:0}.gemboss-assistant-layout.is-dock .gemboss-assistant-page{height:auto;max-width:none;padding:4px 0 8px}.gnav-item.gnav-assistant{position:relative}body.gemboss-assistant-busy .gnav-item.gnav-assistant:after,body.gemboss-assistant-unread .gnav-item.gnav-assistant:after{content:"";position:absolute;top:6px;left:26px;width:8px;height:8px;border-radius:50%;border:2px solid var(--gemboss-surface)}body.gemboss-assistant-busy .gnav-item.gnav-assistant:after{background:var(--gemboss-primary);animation:gemboss-assistant-pulse 1.4s ease-in-out infinite}body.gemboss-assistant-unread .gnav-item.gnav-assistant:after{background:var(--gemboss-success-strong)}@media (prefers-reduced-motion: reduce){body.gemboss-assistant-busy .gnav-item.gnav-assistant:after{animation:none}}.gemboss-assistant-layout{display:flex;height:calc(100dvh - 56px);width:100%;min-height:0}.gemboss-assistant-rail{flex:0 0 264px;width:264px;display:flex;flex-direction:column;min-height:0;border-right:1px solid var(--gemboss-divider);background:transparent;padding:16px 12px 12px;gap:12px;transition:flex-basis .18s ease,width .18s ease}.gemboss-assistant-rail-top{display:flex;align-items:center;gap:6px}.gemboss-assistant-newchat{display:flex;align-items:center;gap:8px;flex:1 1 auto;min-width:0;padding:12px;border:1px solid var(--gemboss-divider);border-radius:12px;background:transparent;color:var(--gemboss-text);font-size:var(--gemboss-fs-body);font-weight:600;cursor:pointer;transition:background .12s ease,border-color .12s ease}.gemboss-assistant-newchat:hover{background:#00000009;border-color:var(--gemboss-text-muted)}.gemboss-assistant-newchat svg{width:17px;height:17px;fill:currentColor;flex:0 0 auto}.gemboss-assistant-newchat span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto}.gemboss-assistant-kbd{flex:0 0 auto;font-size:var(--gemboss-fs-micro);font-weight:600;line-height:1;padding:4px 6px;border:1px solid var(--gemboss-divider);border-radius:6px;background:transparent;color:var(--gemboss-text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.gemboss-assistant-search:focus-within .gemboss-assistant-kbd{display:none}.gemboss-assistant-collapse{flex:0 0 auto;display:grid;place-items:center;width:34px;height:34px;border:0;border-radius:8px;background:transparent;color:var(--gemboss-text-muted);cursor:pointer;transition:background .12s ease,color .12s ease}.gemboss-assistant-collapse:hover{background:#0000000d;color:var(--gemboss-text)}.gemboss-assistant-collapse svg{width:16px;height:16px;fill:currentColor}.gemboss-assistant-search{gap:6px}.gemboss-assistant-search>svg{width:15px;height:15px;fill:var(--gemboss-text-muted);flex:0 0 auto}.gemboss-assistant-search-clear{display:grid;place-items:center;width:20px;height:20px;border:0;border-radius:6px;background:transparent;color:var(--gemboss-text-muted);cursor:pointer}.gemboss-assistant-search-clear:hover{background:#00000012;color:var(--gemboss-text)}.gemboss-assistant-search-clear svg{width:13px;height:13px;fill:currentColor}.gemboss-assistant-railfoot{flex:0 0 auto;display:flex;align-items:center;gap:6px;padding:8px 12px 2px;border-top:1px solid var(--gemboss-divider);margin-top:4px;color:var(--gemboss-text-muted);font-size:var(--gemboss-fs-caption);line-height:1.35}.gemboss-assistant-railfoot svg{width:13px;height:13px;flex:0 0 auto;fill:currentColor;opacity:.8}.gemboss-assistant-threads{flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:2px}.gemboss-assistant-threads-empty{padding:16px 8px;color:var(--gemboss-text-muted);font-size:var(--gemboss-fs-caption)}.gemboss-assistant-group{display:flex;flex-direction:column;gap:2px}.gemboss-assistant-group+.gemboss-assistant-group{margin-top:12px}.gemboss-assistant-group-label{padding:4px 12px;font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--gemboss-text-muted);text-transform:uppercase}.gemboss-assistant-thread{position:relative;display:flex;align-items:center;border-radius:8px;transition:background .12s ease}.gemboss-assistant-thread:hover{background:var(--gemboss-hover, rgba(0,0,0,.04))}.gemboss-assistant-thread.active{background:#00000012}.gemboss-assistant-thread-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:2px;padding:8px 12px;border:0;background:transparent;text-align:left;cursor:pointer}.gemboss-assistant-thread-titlerow{display:flex;align-items:center;gap:6px;width:100%;min-width:0}.gemboss-assistant-pin-dot{width:12px;height:12px;flex:0 0 auto;fill:var(--gemboss-text-muted)}.gemboss-assistant-thread-title{flex:1 1 auto;min-width:0;font-size:13px;font-weight:500;color:var(--gemboss-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.gemboss-assistant-thread-meta{max-width:100%;font-size:var(--gemboss-fs-caption);color:var(--gemboss-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.gemboss-assistant-thread.menu-open{background:#00000012}.gemboss-assistant-thread.menu-open .gemboss-assistant-thread-actions{opacity:1}.gemboss-assistant-menu{z-index:60;width:184px;padding:6px;display:flex;flex-direction:column}.gemboss-assistant-menu-confirm{padding:8px 12px 6px;font-size:var(--gemboss-fs-caption);font-weight:600;color:var(--gemboss-text)}.gemboss-assistant-menu button{display:flex;align-items:center;gap:8px;width:100%;padding:8px 12px;border:0;border-radius:8px;background:transparent;text-align:left;font-size:13px;color:var(--gemboss-text);cursor:pointer}.gemboss-assistant-menu button:hover{background:var(--gemboss-hover, rgba(0,0,0,.05))}.gemboss-assistant-menu button.danger{color:var(--gemboss-danger-strong)}.gemboss-assistant-menu button svg{width:15px;height:15px;fill:currentColor;flex:0 0 auto}.gemboss-assistant-thread-actions{position:absolute;right:6px;display:flex;gap:2px;opacity:0;transition:opacity .12s ease}.gemboss-assistant-thread:hover .gemboss-assistant-thread-actions,.gemboss-assistant-thread:focus-within .gemboss-assistant-thread-actions{opacity:1}.gemboss-assistant-thread-act{display:grid;place-items:center;width:26px;height:26px;border:0;border-radius:8px;background:transparent;color:var(--gemboss-text-muted);cursor:pointer;transition:background .12s ease,color .12s ease}.gemboss-assistant-thread-act:hover{background:#00000012;color:var(--gemboss-text)}.gemboss-assistant-thread-act.danger:hover{color:var(--gemboss-danger-strong)}.gemboss-assistant-thread-act svg{width:15px;height:15px;fill:currentColor}.gemboss-assistant-thread.editing{background:#00000012}.gemboss-assistant-thread-edit{display:flex;align-items:center;gap:2px;width:100%;padding:6px}.gemboss-assistant-thread-edit input{flex:1 1 auto;min-width:0}.gemboss-assistant-layout .gemboss-assistant-page{height:100%;max-width:820px;flex:1 1 auto;min-width:0;padding-top:16px}.gemboss-assistant-layout .gemboss-assistant-page.is-switching{opacity:.55;transition:opacity .12s ease;pointer-events:none}.gemboss-assistant-rail.is-collapsed{flex-basis:60px;width:60px;padding:16px 8px 12px}.gemboss-assistant-rail.is-collapsed .gemboss-assistant-rail-top{flex-direction:column;gap:8px}.gemboss-assistant-rail.is-collapsed .gemboss-assistant-newchat{justify-content:center;padding:8px;flex:0 0 auto}.gemboss-assistant-rail.is-collapsed .gemboss-assistant-newchat span,.gemboss-assistant-rail.is-collapsed .gemboss-assistant-search,.gemboss-assistant-rail.is-collapsed .gemboss-assistant-railfoot,.gemboss-assistant-rail.is-collapsed .gemboss-assistant-threads{display:none}@media (max-width: 760px){.gemboss-assistant-rail{flex-basis:60px;width:60px;padding:16px 8px 12px}.gemboss-assistant-rail .gemboss-assistant-rail-top{flex-direction:column;gap:8px}.gemboss-assistant-newchat{justify-content:center;padding:8px;flex:0 0 auto}.gemboss-assistant-newchat span,.gemboss-assistant-search,.gemboss-assistant-railfoot,.gemboss-assistant-threads,.gemboss-assistant-collapse{display:none}}.gemboss-assistant-page{height:calc(100dvh - 56px);width:100%;max-width:760px;margin:0 auto;padding:24px 0 16px;display:flex;flex-direction:column;min-height:0}.gemboss-assistant-page>*{flex:1 1 auto;min-height:0}.gemboss-assistant-empty{flex:1;display:grid;place-items:center;color:var(--gemboss-text-muted);font-size:14px}.gemboss-platform-shell{display:flow-root}.gemboss-platform-main{padding-bottom:calc(24px + var(--gemboss-invite-h, 0px))}@keyframes gemboss-assistant-pulse{0%,to{opacity:1}50%{opacity:.35}}.gb-account{position:relative}.gb-account>summary{list-style:none}.gb-account>summary::-webkit-details-marker{display:none}.gb-account-head{padding:12px 16px;border-bottom:1px solid var(--gemboss-divider)}.gb-account-name{font-size:13px;font-weight:600;color:var(--gemboss-text-strong)}.gb-account-email{font-size:12px;color:var(--gemboss-text-muted)}.gb-account-role{display:inline-block;margin-top:4px;padding:1px 6px;border-radius:4px;font-size:11px;font-weight:600;color:var(--gemboss-active-fg);background:var(--gemboss-active-bg)}.gb-account-form{margin:0}.gnav-scroll::-webkit-scrollbar{width:9px}.gnav-scroll::-webkit-scrollbar-track{background:transparent}.gnav-scroll::-webkit-scrollbar-thumb{background:#1f1e1d33;border-radius:6px;border:2px solid transparent;background-clip:content-box}
@@ -0,0 +1 @@
1
+ .ui-stagepath{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,190px),1fr));gap:var(--ui-space-2);margin:0;padding:0;list-style:none}.ui-stagepath__stage{position:relative;display:flex;align-items:flex-start;gap:var(--ui-space-2);min-width:0;padding:var(--ui-space-3);border:1px solid var(--ui-border);border-radius:var(--ui-radius-lg);background:var(--ui-surface)}.ui-stagepath__stage--current{border-color:var(--gemboss-primary);box-shadow:inset 0 0 0 1px var(--gemboss-primary)}.ui-stagepath__n{flex:none;display:inline-grid;place-items:center;width:24px;height:24px;border-radius:50%;background:var(--ui-surface-2);font-size:var(--ui-fs-caption);font-weight:700;color:var(--ui-text-strong)}.ui-stagepath__stage--current .ui-stagepath__n{background:var(--gemboss-primary);color:var(--gemboss-on-primary)}.ui-stagepath__stage--done .ui-stagepath__n{background:var(--gemboss-success-soft);color:var(--gemboss-success-strong)}.ui-stagepath__text{display:grid;flex:1;gap:4px;min-width:0}.ui-stagepath__name{font-size:var(--ui-fs-body);line-height:20px;font-weight:650;color:var(--ui-text-strong);text-decoration:none;overflow-wrap:anywhere}a.ui-stagepath__name:after{content:"";position:absolute;inset:0;border-radius:inherit}a.ui-stagepath__name:hover{text-decoration:underline}a.ui-stagepath__name:focus-visible{outline:none}a.ui-stagepath__name:focus-visible:after{outline:2px solid var(--ui-focus);outline-offset:2px}.ui-stagepath__count{font-size:var(--ui-fs-caption);line-height:16px;color:var(--ui-text-muted);font-variant-numeric:tabular-nums}.ui-stagepath__bar{appearance:none;display:block;width:100%;height:4px;border:0;border-radius:999px;overflow:hidden;background:var(--ui-border);color:var(--gemboss-success)}.ui-stagepath__bar::-webkit-progress-bar{background:inherit;border-radius:inherit}.ui-stagepath__bar::-webkit-progress-value{background:currentColor;border-radius:inherit}.ui-stagepath__bar::-moz-progress-bar{background:currentColor;border-radius:inherit}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Stage path — @gemboss/ui, unreleased (after 0.49.0). An ordered path of stages in which each one has to finish before the next
3
+ * one counts: GemPress's translation review (redesign approved by Chris 23/10/2026) unlocks a language header & footer first, then
4
+ * pages, then the blog, then the docs, because a translated page is not worth publishing inside an untranslated frame. GemPress drew
5
+ * it with its own CSS (`gp-unlock`); GemNews and GemCommunity translations are next, so it lives here. Not `SetupGuide` (steps a
6
+ * person ticks, one open at a time, with a body each) and not `ProgressRing` (one count): this is several counts, in an order.
7
+ *
8
+ * The contract it draws, which every surface keeps:
9
+ * - the stages in their order, each with its number; a stage DONE (`done >= total`, so a stage with nothing in it holds nothing up)
10
+ * shows a green ✓ in place of its number;
11
+ * - ONE current stage, the one being worked on: an ink outline and an ink number (`current`; by default the first stage not done,
12
+ * `null` for none). It is `aria-current="step"`;
13
+ * - the count in words under the name ("3 / 35 pages approved"), and a thin bar that says the same: the words are what a screen
14
+ * reader hears, the bar is hidden from it. The words are the surface's to change (`caption`, `format`): the path does no plural
15
+ * and names no language;
16
+ * - a stage with `href` is a link, and the whole card is its target.
17
+ *
18
+ * Presentational and server-renderable: no state, no effect. The colours are tokens, so the dark palette follows. The bar is a
19
+ * `<progress>`, not a width in a style attribute, so it fills under a CSP that forbids inline style (GemPress, gemcare). Its
20
+ * stylesheet travels with it (`stage-path.css`, own entry; `@gemboss/ui/stage-path.css` for a plain-HTML surface, which writes the
21
+ * classes listed at the top of that file).
22
+ */
23
+ import "./stage-path.css";
24
+ import React, { type ReactNode } from "react";
25
+
26
+ const cx = (...p: Array<string | false | null | undefined>) => p.filter(Boolean).join(" ");
27
+ /** A count as the path draws it: a whole number, 0 or more; anything that is not a finite number is 0. */
28
+ const whole = (n: unknown) => (typeof n === "number" && Number.isFinite(n) ? Math.max(Math.round(n), 0) : 0);
29
+
30
+ export interface StagePathStage {
31
+ /** Unique in the path; what `current` names. */
32
+ key: string;
33
+ name: ReactNode;
34
+ /** How many are approved. Clamped into 0..total. */
35
+ done: number;
36
+ total: number;
37
+ /** The word after the total, in the number the surface wants printed: "pages", "parts". */
38
+ unit?: string;
39
+ /** Where the stage opens (its review queue). The whole card is the link. */
40
+ href?: string;
41
+ }
42
+
43
+ export interface StagePathProps {
44
+ stages: readonly StagePathStage[];
45
+ /** The stage being worked on. Default: the first stage not done. `null`: none (every stage is waiting on something else). */
46
+ current?: string | null;
47
+ /** The list's name for a screen reader (default "Unlock path"). */
48
+ label?: string;
49
+ /** The words under a stage's name. Default "3 / 35 pages approved". */
50
+ caption?: (count: { done: number; total: number; unit?: string }) => ReactNode;
51
+ /** A number as the surface writes it ("1,204"). Default `String`. */
52
+ format?: (n: number) => string;
53
+ className?: string;
54
+ }
55
+
56
+ /** Whether a stage is done: every item approved, and a stage with nothing in it is done. Pure, for a page that writes the markup. */
57
+ export function stagePathDone(stage: Pick<StagePathStage, "done" | "total">): boolean {
58
+ return whole(stage.done) >= whole(stage.total);
59
+ }
60
+
61
+ export function StagePath({ stages, current, label = "Unlock path", caption, format = String, className }: StagePathProps) {
62
+ if (!stages.length) return null; // nothing to unlock is no path
63
+ const rows = stages.map((s) => {
64
+ const total = whole(s.total);
65
+ return { s, total, done: Math.min(whole(s.done), total), finished: stagePathDone(s) };
66
+ });
67
+ const now = current === undefined ? rows.find((r) => !r.finished)?.s.key : current;
68
+ return (
69
+ <ol className={cx("ui-stagepath", className)} aria-label={label}>
70
+ {rows.map(({ s, total, done, finished }, i) => {
71
+ const on = now != null && s.key === now;
72
+ return (
73
+ <li key={s.key} className={cx("ui-stagepath__stage", finished && "ui-stagepath__stage--done", on && "ui-stagepath__stage--current")}
74
+ aria-current={on ? "step" : undefined}>
75
+ <span className="ui-stagepath__n" aria-hidden="true">{finished ? "✓" : i + 1}</span>
76
+ <span className="ui-stagepath__text">
77
+ {s.href ? <a className="ui-stagepath__name" href={s.href}>{s.name}</a> : <span className="ui-stagepath__name">{s.name}</span>}
78
+ <span className="ui-stagepath__count">
79
+ {caption ? caption({ done, total, unit: s.unit }) : `${format(done)} / ${format(total)}${s.unit ? ` ${s.unit}` : ""} approved`}
80
+ </span>
81
+ {/* 0 of 0 is a stage with nothing to hold up: a full bar, as its ✓ says. */}
82
+ <progress className="ui-stagepath__bar" value={total ? done : 1} max={total || 1} aria-hidden="true" />
83
+ </span>
84
+ </li>
85
+ );
86
+ })}
87
+ </ol>
88
+ );
89
+ }
package/src/stage.css ADDED
@@ -0,0 +1 @@
1
+ .ui-stage{--ui-stage-top: #1d2058;--ui-stage-deep: #0d0f2e;--ui-stage-glow: rgba(139, 147, 255, .5);--ui-stage-glow-low: rgba(86, 97, 245, .34);--ui-stage-glass: rgba(255, 255, 255, .08);--ui-stage-glass-line: rgba(255, 255, 255, .14);--ui-stage-ink: rgba(255, 255, 255, .88);--ui-stage-ink-dim: rgba(255, 255, 255, .6);--ui-stage-number: #a5abff;--ui-stage-lift: 0 16px 32px -12px rgba(4, 5, 26, .72), 0 4px 8px -2px rgba(4, 5, 26, .36);position:relative;display:flex;flex-direction:column;justify-content:center;gap:8px;padding:16px;overflow:hidden;border-radius:var(--gemboss-radius-lg, 12px);color:var(--ui-stage-ink);font-size:var(--gemboss-fs-caption, 12px);line-height:16px;background:radial-gradient(130% 90% at 100% 0%,var(--ui-stage-glow),transparent 55%),radial-gradient(90% 80% at 0% 100%,var(--ui-stage-glow-low),transparent 60%),linear-gradient(160deg,var(--ui-stage-top),var(--ui-stage-deep))}.ui-stage:before{content:"";position:absolute;inset:0;pointer-events:none;background-image:radial-gradient(rgba(255,255,255,.1) 1px,transparent 1.5px);background-size:16px 16px;-webkit-mask-image:linear-gradient(170deg,#000,transparent 70%);mask-image:linear-gradient(170deg,#000,transparent 70%)}.ui-stage>*{position:relative}.ui-stage__win{display:flex;flex-direction:column;overflow:hidden;border-radius:var(--gemboss-radius-md, 8px);background:var(--gemboss-surface, #ffffff);color:var(--gemboss-text, #1b1c1e);box-shadow:var(--ui-stage-lift)}.ui-stage__bar{flex:none;display:flex;align-items:center;gap:4px;height:16px;padding:0 8px;border-bottom:1px solid var(--gemboss-divider, #e5e7ea);font-size:var(--gemboss-fs-micro, 11px);color:var(--gemboss-text-muted, #686b71)}.ui-stage__bar i{width:6px;height:6px;border-radius:999px;background:var(--gemboss-divider, #e5e7ea)}.ui-stage__bar b{margin-left:4px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ui-stage__shot{display:block;width:100%;height:auto}.ui-stage__body{display:flex;flex-direction:column;gap:6px;padding:8px}.ui-stage__line{display:block;height:6px;border-radius:999px;background:var(--gemboss-divider, #e5e7ea)}.ui-stage__line--short{width:55%}.ui-stage__line--strong{height:8px;width:70%;background:var(--gemboss-text, #1b1c1e)}.ui-stage__tile{display:flex;align-items:center;gap:6px;padding:8px 12px;border-radius:var(--gemboss-radius-md, 8px);background:var(--ui-stage-glass);color:var(--ui-stage-ink)}.ui-stage__tile em{font-style:normal;font-family:ui-monospace,SF Mono,Menlo,monospace;color:var(--ui-stage-number)}.ui-stage__tile--on{background:var(--gemboss-surface, #ffffff);color:var(--gemboss-text, #1b1c1e);box-shadow:0 0 0 1px #fff6,0 0 24px var(--ui-stage-glow)}.ui-stage__tile--on em{color:var(--gemboss-accent, #5661f5)}.ui-stage__bubble{align-self:flex-start;max-width:85%;padding:6px 12px;border-radius:12px;border:1px solid var(--ui-stage-glass-line);background:var(--ui-stage-glass);color:var(--ui-stage-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ui-stage__bubble--me{align-self:flex-end;border-color:transparent;background:var(--gemboss-surface, #ffffff);color:var(--gemboss-text, #1b1c1e);box-shadow:var(--ui-stage-lift)}
package/src/stage.tsx ADDED
@@ -0,0 +1,52 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Stage (0.49, 22/10/2026): the ONE way the app draws a picture, taken from gemboss.ai's mega menu, where each panel has one
5
+ * visual: a night-blue stage lit with iris from one corner, a faint dot grid fading out, and on it either glass tiles (one
6
+ * picked, white and glowing) or white windows lifted off the stage showing a real page. Chris (22/10): pictures clean, colours
7
+ * in harmony, easy to read, the website's look; the design language lets an illustration light itself, and this is that
8
+ * light, named once here instead of a hex per picture.
9
+ *
10
+ * <Stage> the lit panel (aria-hidden: a picture says nothing a reader needs)
11
+ * <StageWindow title="…" image /> a white window with its three dots, a title and a real screenshot (or bare lines)
12
+ * <StageTile on>…</StageTile> a glass tile; `on` is the one picked
13
+ * <StageBubble me>…</StageBubble> a line of chat: the assistant's in glass, the person's in white
14
+ */
15
+ import "./stage.css";
16
+ import type { CSSProperties, ReactNode } from "react";
17
+
18
+ const cx = (...p: Array<string | false | null | undefined>) => p.filter(Boolean).join(" ");
19
+
20
+ /** `decorative={false}`: the stage holds words a reader needs (a panel beside a question), so it is not hidden from them. */
21
+ export function Stage({ children, className, style, decorative = true }: {
22
+ children?: ReactNode; className?: string; style?: CSSProperties; decorative?: boolean;
23
+ }) {
24
+ return <div className={cx("ui-stage", className)} style={style} aria-hidden={decorative ? "true" : undefined}>{children}</div>;
25
+ }
26
+
27
+ /** A white window lifted off the stage: three dots, an optional title, then a real screenshot or two lines of text. */
28
+ export function StageWindow({ title, image, alt = "", children, className, style }: {
29
+ title?: string; image?: string; /** For a stage that is not decorative: what the screenshot shows. */ alt?: string;
30
+ children?: ReactNode; className?: string; style?: CSSProperties;
31
+ }) {
32
+ return (
33
+ <div className={cx("ui-stage__win", className)} style={style}>
34
+ <div className="ui-stage__bar"><i /><i /><i />{title ? <b>{title}</b> : null}</div>
35
+ {image ? <img className="ui-stage__shot" src={image} alt={alt} loading="lazy" /> : null}
36
+ {children ? <div className="ui-stage__body">{children}</div> : null}
37
+ </div>
38
+ );
39
+ }
40
+
41
+ export function StageTile({ on, children }: { on?: boolean; children?: ReactNode }) {
42
+ return <div className={cx("ui-stage__tile", on && "ui-stage__tile--on")}>{children}</div>;
43
+ }
44
+
45
+ export function StageBubble({ me, children }: { me?: boolean; children?: ReactNode }) {
46
+ return <div className={cx("ui-stage__bubble", me && "ui-stage__bubble--me")}>{children}</div>;
47
+ }
48
+
49
+ /** A line of placeholder text inside a window (`short` is half as long). */
50
+ export function StageLine({ short, strong }: { short?: boolean; strong?: boolean }) {
51
+ return <span className={cx("ui-stage__line", short && "ui-stage__line--short", strong && "ui-stage__line--strong")} />;
52
+ }
@@ -0,0 +1 @@
1
+ .ui-start{display:flex;flex-direction:column;gap:20px}.ui-start__head{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}.ui-start__title{margin:0;font-size:var(--gemboss-fs-h2, 20px);font-weight:600;color:var(--ui-text)}.ui-start__sub{margin:0;font-size:var(--gemboss-fs-body, 14px);color:var(--ui-text-muted)}.ui-start__doors{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr));gap:16px}.ui-start__door{display:flex;flex-direction:column;gap:8px;padding:16px;border:1px solid var(--ui-border);border-radius:var(--gemboss-radius-xl, 16px);background:var(--ui-surface)}.ui-start__name{margin:4px 0 0;font-size:var(--gemboss-fs-h3, 16px);font-weight:600;color:var(--ui-text)}.ui-start__line{margin:0;flex:1 1 auto;font-size:var(--gemboss-fs-body, 14px);line-height:1.5;color:var(--ui-text-muted)}.ui-start__go{align-self:stretch;margin-top:8px}.ui-start__stage{box-sizing:border-box;height:152px}.ui-start__stage--fan{justify-content:flex-end;align-items:center;padding-bottom:0}.ui-start__stage .ui-start__win{position:absolute;top:24px;bottom:-12px;width:58%}.ui-start__stage .ui-start__win--front{left:28%}.ui-start__stage .ui-start__win--back{left:12%;transform:rotate(-6deg);transform-origin:50% 100%;opacity:.85}.ui-start__stage .ui-start__win .ui-stage__shot{flex:1 1 auto;min-height:0;object-fit:cover;object-position:50% 0}.ui-start__slot{display:block;margin:8px;padding:8px;text-align:center;border:1px dashed var(--gemboss-divider, #e5e7ea);border-radius:var(--gemboss-radius-sm, 6px);font-size:var(--gemboss-fs-micro, 11px);color:var(--gemboss-text-muted, #686b71)}
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ /**
4
+ * StartingPoint (0.49, 22/10/2026): "Choose your starting point". The same three doors wherever something new is made: ask the
5
+ * assistant, start from a template, start blank. Learned from Replo, where Plans, Tasks, Skills and reports all open this way, each
6
+ * door with a small picture of what happens behind it (the chat door shows a two-line exchange). One shape everywhere teaches the
7
+ * person once.
8
+ *
9
+ * At most three doors (Chris's rule for any choice). Each picture is a Stage (stage.tsx): the website's mega-menu visual, a lit
10
+ * night-blue panel with glass and white windows. A door may pass real screenshots (`images`): the template door fans two of them,
11
+ * the chat door shows the page it is about to draw.
12
+ */
13
+ import "./starting-point.css";
14
+ import type { ReactNode } from "react";
15
+ import { Stage, StageBubble, StageLine, StageTile, StageWindow } from "./stage";
16
+
17
+ export type StartingArt = "chat" | "template" | "blank";
18
+
19
+ export interface StartingDoor {
20
+ key: string;
21
+ art: StartingArt;
22
+ title: string;
23
+ /** One line on what happens behind the door. */
24
+ line: string;
25
+ /** The button's words ("Ask Gem", "Browse templates", "Start blank"). */
26
+ action: string;
27
+ onClick: () => void;
28
+ /** The chat door's picture: `ask` is the first line, `answer` the second. By default Gem asks and the person answers (a door
29
+ * Gem builds behind); `from: "me"` when the person asks and Gem answers. `step` is the tile under both, what happens next
30
+ * (default "Drafting"). */
31
+ chat?: { ask: string; answer: string; from?: "gem" | "me"; step?: string };
32
+ /** Real screenshots for the picture (the template door shows two, the chat door one). Without them, windows draw lines. */
33
+ images?: readonly string[];
34
+ busy?: boolean;
35
+ disabled?: boolean;
36
+ }
37
+
38
+ function Art({ door }: { door: StartingDoor }) {
39
+ const [first, second] = door.images ?? [];
40
+ if (door.art === "chat") {
41
+ return (
42
+ <Stage className="ui-start__stage">
43
+ <StageBubble me={door.chat?.from === "me"}>{door.chat?.ask ?? "What are we making?"}</StageBubble>
44
+ <StageBubble me={door.chat?.from !== "me"}>{door.chat?.answer ?? "…"}</StageBubble>
45
+ <StageTile on><em>01</em>{door.chat?.step ?? (first ? "Drafting your page" : "Drafting")}</StageTile>
46
+ </Stage>
47
+ );
48
+ }
49
+ if (door.art === "template") {
50
+ return (
51
+ <Stage className="ui-start__stage ui-start__stage--fan">
52
+ <StageWindow className="ui-start__win ui-start__win--back" image={second}>{second ? null : <><StageLine strong /><StageLine /><StageLine short /></>}</StageWindow>
53
+ <StageWindow className="ui-start__win ui-start__win--front" image={first}>{first ? null : <><StageLine strong /><StageLine /><StageLine short /></>}</StageWindow>
54
+ </Stage>
55
+ );
56
+ }
57
+ return (
58
+ <Stage className="ui-start__stage ui-start__stage--fan">
59
+ <StageWindow className="ui-start__win ui-start__win--front" title="Untitled page">
60
+ <span className="ui-start__slot">+ Add a section</span>
61
+ </StageWindow>
62
+ </Stage>
63
+ );
64
+ }
65
+
66
+ export function StartingPoint({ title = "Choose your starting point", sub, doors }: {
67
+ title?: ReactNode;
68
+ sub?: ReactNode;
69
+ /** One to three doors, in the order a person should consider them. */
70
+ doors: readonly StartingDoor[];
71
+ }) {
72
+ return (
73
+ <section className="ui-start">
74
+ <div className="ui-start__head">
75
+ <h2 className="ui-start__title">{title}</h2>
76
+ {sub ? <p className="ui-start__sub">{sub}</p> : null}
77
+ </div>
78
+ <div className="ui-start__doors">
79
+ {doors.slice(0, 3).map((door) => (
80
+ <div key={door.key} className="ui-start__door">
81
+ <Art door={door} />
82
+ <h3 className="ui-start__name">{door.title}</h3>
83
+ <p className="ui-start__line">{door.line}</p>
84
+ <button type="button" className="ui-btn ui-btn--primary ui-start__go" onClick={door.onClick}
85
+ disabled={door.disabled || door.busy} aria-busy={door.busy || undefined}>
86
+ {door.action}
87
+ </button>
88
+ </div>
89
+ ))}
90
+ </div>
91
+ </section>
92
+ );
93
+ }
@@ -0,0 +1 @@
1
+ .ui-statuslist{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,260px),1fr));gap:var(--ui-space-2) var(--ui-space-4);margin:0;padding:0;list-style:none}.ui-statuslist__item{display:flex;align-items:baseline;gap:var(--ui-space-2);min-width:0;font-size:var(--ui-fs-caption);line-height:20px;color:var(--ui-text-muted)}.ui-statuslist__dot{flex:none;align-self:center;width:8px;height:8px;box-sizing:border-box;border:1.5px solid;border-radius:50%}:is(.ui-statuslist__item--ok,.ui-statuslist__item--warning,.ui-statuslist__item--critical)>.ui-statuslist__dot{background:currentColor}.ui-statuslist__item--ok>.ui-statuslist__dot{color:var(--gemboss-success)}.ui-statuslist__item--warning>.ui-statuslist__dot{color:var(--gemboss-warning)}.ui-statuslist__item--critical>.ui-statuslist__dot{color:var(--gemboss-danger)}.ui-statuslist__label{flex:none;font-weight:650;color:var(--ui-text-strong)}.ui-statuslist__item--muted>.ui-statuslist__label{color:var(--ui-text-muted)}.ui-statuslist__text{min-width:0;overflow-wrap:anywhere}.ui-statuslist__text>a{color:inherit;text-underline-offset:2px}.ui-statuslist__text>a:hover{color:var(--ui-text-strong)}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Status list — @gemboss/ui, unreleased (after 0.49.0). A quiet strip of system health: GemPress's project home (redesign approved
3
+ * by Chris 23/10/2026) ends with "Backups: nightly · last 22 Oct", "Site rebuild: on publish", "Webhooks: delivering", one line
4
+ * each. GemPress drew it with its own CSS (`gp-sys`); every service has the same few things to say about itself (backups, hooks,
5
+ * keys, the assistant), so the strip lives here.
6
+ *
7
+ * The contract it draws, which every surface keeps: QUIET WHEN FINE. Each item is a dot, a name and one muted sentence; the dot is
8
+ * green, amber or red (`ok`, `warning`, `critical`) or an empty ring (`muted`: off, not applicable), and the sentence says what the
9
+ * colour means, so colour never stands alone. An item with `href` makes its sentence a link to where it is fixed. A banner is for
10
+ * when a person must act: something that needs a person today goes in a `ui-banner` above the page, not in this strip.
11
+ *
12
+ * Presentational and server-renderable: no state, no effect; the tones are the kit's success / warning / danger tokens, so the
13
+ * dark palette follows. `status-list.css` is its own entry; a plain-HTML surface links `@gemboss/ui/status-list.css` and writes
14
+ * the classes listed at the top of that file.
15
+ */
16
+ import "./status-list.css";
17
+ import React, { type ReactNode } from "react";
18
+
19
+ const cx = (...p: Array<string | false | null | undefined>) => p.filter(Boolean).join(" ");
20
+
21
+ export type StatusListTone = "ok" | "warning" | "critical" | "muted";
22
+ /** Each tone's class, written whole (never built from the tone), so a search for a class finds where it is drawn. */
23
+ const ITEM_CLASS: Readonly<Record<StatusListTone, string>> = {
24
+ ok: "ui-statuslist__item ui-statuslist__item--ok",
25
+ warning: "ui-statuslist__item ui-statuslist__item--warning",
26
+ critical: "ui-statuslist__item ui-statuslist__item--critical",
27
+ muted: "ui-statuslist__item ui-statuslist__item--muted",
28
+ };
29
+
30
+ export interface StatusListItem {
31
+ key: string;
32
+ /** The thing: "Backups". */
33
+ label: ReactNode;
34
+ /** One sentence about it: "nightly · last 22 Oct". */
35
+ text: ReactNode;
36
+ tone: StatusListTone;
37
+ /** Where it is fixed or read: the sentence becomes a link. */
38
+ href?: string;
39
+ }
40
+
41
+ export interface StatusListProps {
42
+ items: readonly StatusListItem[];
43
+ /** The list's name for a screen reader, when no heading names it ("System"). */
44
+ label?: string;
45
+ className?: string;
46
+ }
47
+
48
+ export function StatusList({ items, label, className }: StatusListProps) {
49
+ if (!items.length) return null;
50
+ return (
51
+ <ul className={cx("ui-statuslist", className)} aria-label={label}>
52
+ {items.map((it) => {
53
+ // A tone that is not one of the four (plain JS, a typo) is muted: never a colour the sentence does not explain.
54
+ const itemClass = Object.prototype.hasOwnProperty.call(ITEM_CLASS, it.tone) ? ITEM_CLASS[it.tone] : ITEM_CLASS.muted;
55
+ return (
56
+ <li key={it.key} className={itemClass}>
57
+ <span className="ui-statuslist__dot" aria-hidden="true" />
58
+ <b className="ui-statuslist__label">{it.label}</b>
59
+ <span className="ui-statuslist__text">{it.href ? <a href={it.href}>{it.text}</a> : it.text}</span>
60
+ </li>
61
+ );
62
+ })}
63
+ </ul>
64
+ );
65
+ }
@@ -0,0 +1 @@
1
+ .ui-matrix__caption{caption-side:bottom;padding:var(--ui-space-2) var(--ui-space-3);border-top:1px solid var(--ui-border);text-align:left;font-size:var(--ui-fs-caption);line-height:16px;color:var(--ui-text-muted)}.ui-matrix th.ui-matrix__row{font-weight:550;color:var(--ui-text-strong);white-space:normal;overflow-wrap:anywhere}.ui-matrix__row>a{display:inline-flex;align-items:center;min-height:24px;color:inherit;text-underline-offset:2px}.ui-matrix__cell{color:var(--ui-text-muted)}.ui-matrix__live{font-weight:650;color:var(--ui-text-strong)}.ui-matrix__cell--live .ui-matrix__live{color:var(--gemboss-success-strong)}.ui-matrix__cell--draft .ui-matrix__live{color:var(--gemboss-warning)}.ui-matrix__sub{display:block;margin-top:2px;font-size:var(--ui-fs-caption);line-height:16px}