const[notice,setNotice]=useState(initialPreferences.invalid?"A remembered choice is no longer available. Choose a valid workspace and model; no alternative has been selected silently.":"");
setNotice(saved?"Workspace remembered for your next visit. Your model selection is unchanged.":"Browser storage is unavailable. This workspace selection will last only for this visit.");
}
functionchangeModel(id: string){
if(busy)return;
setOperation(null);setModelId(id);
constsaved=saveChoice({model: id});
setNotice(saved?"Model remembered for your next visit, independently of the workspace.":"Browser storage is unavailable. This model selection will last only for this visit.");
setNotice("Proposed resume: the original workspace and revision are retained; this run uses the globally selected model. Backend changes are required.");
<labelclassName="gc-selector"><span><Cpusize={15}/>AIinteractionmodel</span><selectid="global-model"aria-label="Global AI model"value={modelId}onChange={e=>changeModel(e.target.value)}aria-describedby={busy?"operation-lock":undefined}disabled={busy||availability==="no-models"}><optionvalue="">Chooseamodel</option>{models.map(m=><optionkey={m.id}value={m.id}>{m.name}{m.metadata?"":" · Core only"}</option>)}</select><small><spanclassName="cs-source">{sources.model}</span>{model?`${model.provider} · ${model.metadata?"Core + administration":"Core only"}`:"Independent of the workspace"}</small></label>
{(shelfOpen||!ready)&&<divid="context-shelf"className="gc-shelf-body"><divclassName="cs-context-intro"><pclassName="rv-eyebrow">Workingpreferences</p><h2>{variant==="C"?"Where you work. Which AI you use.":"Choose once, return to the same context."}</h2><p>Yourlastchoicestakeprecedenceoverinstallationdefaults.Workspaceandmodelarerememberedindependently.</p></div>{selectedContext}<divclassName="cs-context-footer"><span>{busy?"Both selectors are locked until the current operation ends.":"Saved on this browser for the prototype’s demo user."}</span>{ready&&<ButtononClick={()=>setShelfOpen(false)}>Done</Button>}</div></div>}
constgate=<sectionclassName="gc-gate"><spanclassName="gc-gate-icon"><LockKeyholesize={28}/></span><pclassName="rv-eyebrow">ThothII·Starthere</p><h1>Chooseyourworkingcontext</h1><p>SelectaworkspaceandanAImodelinthepanelabove.Botharerequiredbeforeopeninganalyticalworkoradministration.</p><ol><liclassName={currentWorkspace&&availability!=="no-workspaces"?"done":""}><span>{currentWorkspace?<Checksize={16}/>:"1"}</span><div><strong>Whereyouwork</strong><small>{currentWorkspace?.name??"Workspace, data, knowledge and sessions"}</small></div></li><liclassName={model&&availability!=="no-models"?"done":""}><span>{model?<Checksize={16}/>:"2"}</span><div><strong>WhichAIyouuse</strong><small>{model?.name??"A single model preference for the application"}</small></div></li></ol>{["unavailable","no-models","no-workspaces"].includes(availability)&&<divclassName="gc-warning"role="alert"><strong>{availability==="unavailable"?"The selected provider is unavailable":availability==="no-models"?"No usable model is configured":"No workspace is available"}</strong><p>Noactivitycanstart.Asktheinstallationoperatortocheckthecatalog,credentialsorworkspaceregistry.Noautomaticmodelfallback.</p></div>}<pclassName="gc-gate-foot">Yourdatastaysinsidetheselectedworkspace.AImodelsbelongtotheinstallation.</p></section>;
constcore=<>{pageHeader(active?active.title:"From a question to a trusted answer",active?"Persisted artifacts and reviewer decisions guide this session.":"Ask about your data. Review the evidence, approve the interpretation, then build the query.")}
{active?<divclassName="gc-analysis"><divclassName="gc-session-context"><LockKeyholesize={15}/><strong>{currentWorkspace?.name}</strong><span>Sessionrevision{active.revision}</span><span>{active.status==="archived"?"Read-only archive":activeJob?`Run model: ${models.find(m=>m.id===activeJob.model)?.name}`:`Next AI operation: ${model?.name}`}</span></div><divclassName="gc-workflow"><ol>{["Question","Schema linking","SQL draft","Validation"].map((t,i)=><likey={t}className={i===1?"current":""}><span>{String(i+1).padStart(2,"0")}</span>{t}</li>)}</ol><small>Workflowexcerpt·8phasesinproduction</small></div><divclassName="gc-artifact-layout"><sectionclassName="gc-artifact"><navaria-label="Session artifacts">{["Question","Schema","SQL"].map(t=><buttonkey={t}aria-current={artifact===t?"page":undefined}onClick={()=>setArtifact(t)}>{t}</button>)}</nav><div><pclassName="rv-eyebrow">Persistedartifact·example</p><h2>{artifact==="Question"?"Domanda analitica":artifact==="Schema"?"Collegamento allo schema":"Query proposta"}</h2>{artifact==="SQL"?<pre><code>{`SELECT COUNT(DISTINCT patient_id)\nFROM ${currentWorkspace?.schema}.visits\nWHERE visited_at >= DATE '2025-01-01'\n AND visited_at < DATE '2026-01-01';`}</code></pre>:<><plang="it">{artifact==="Question"?active.title:"La relazione tra pazienti e visite definisce la popolazione osservata. Il conteggio deve mantenere distinti i pazienti."}</p><divclassName="gc-evidence-line"><FileTextsize={16}/><span>Evidence: popolazionediriferimento</span></div><pclassName="rv-small">Syntheticillustration,notgeneratedfromadatabase.</p></>}</div></section><sectionclassName="gc-review"><pclassName="rv-eyebrow">Humanreview</p><h2>{active.status==="archived"?"Retained decisions":activeJob?"AI operation in progress":"Confirm the interpretation"}</h2><p>{active.status==="archived"?"This archive can be inspected, but its workflow cannot currently be resumed.":activeJob?"Workspace and model are locked until this operation finishes. No other operation can start in the meantime.":"Check the proposed population and time range before proceeding."}</p>{active.status!=="archived"&&<Buttonprimarydisabled={busy}onClick={()=>startJob(`Analysis: ${active.title}`,"llm")}>{activeJob?"Waiting for result":`Approve & continue with ${model?.name}`}</Button>}<pclassName="rv-small">{active.status==="archived"?"Archived and finalized sessions remain read-only.":"Model output proposes. The reviewer decides."}</p></section></div></div>:<divclassName="gc-core-start"><sectionclassName="gc-question"><divclassName="gc-question-heading"><spanclassName="gc-symbol"><MessageSquaresize={23}/></span><div><h2>Startananalyticalquestion</h2><p>{currentWorkspace?.database}·EvidenceandMemoryinthisworkspace</p></div></div><formonSubmit={e=>{e.preventDefault();submitQuestion();}}><labelhtmlFor="analysis-question"className="rv-eyebrow">Yourquestion</label><textareaid="analysis-question"rows={4}value={question}placeholder="Quanti pazienti hanno almeno una visita di follow-up nel 2025?"onChange={e=>{setQuestion(e.target.value);setDirty(!!e.target.value);}}/><divclassName="gc-composer-footer"><span><Cpusize={15}/>{model?.name}<small>Globalmodel</small></span><buttonclassName="rv-button rv-primary"type="submit"disabled={!question.trim()||availability==="needs-preprocessing"||busy}>Startanalysis<ArrowRightsize={16}/></button></div></form></section><sectionclassName="gc-method"><pclassName="rv-eyebrow">Areviewablepath</p><div><span><strong>01</strong>Askaquestion</span><ChevronRightsize={15}/><span><strong>02</strong>Reviewcontext</span><ChevronRightsize={15}/><span><strong>03</strong>Validatethequery</span></div><p>Everyanalyticalresultisbackedbypersistedartifactsandexplicitreviewdecisions.</p></section></div>}
onSave: updated=>{if(updated){setCards(c=>({...c,[workspace]:selected==="new"?[updated,...rows]:rows.map(r=>r.id===updated.id?updated : r)}));setSelection(s=>({...s,[scope]:updated.id}));}setDirty(false);setEditing(false);setNotice("Saved in this workspace’s demo data only.");},
connectionValues: connections[workspace],onConnectionSave: values=>{setConnections(c=>({...c,[workspace]:values}));setEditing(false);setDirty(false);setNotice("Connection saved in memory only. No real credentials were used.");},
constadministration=admin&&<>{pageHeader(modules[page].title,modules[page].subtitle,"Administration")}<divclassName="gc-admin-context"><span>{page==="pi"?"Installation scope: applies to all workspaces":`Workspace: ${currentWorkspace?.name}`}</span><span>{page==="pi"?"Global LLM selection is not configuration authoring":`Global LLM: ${model?.name}`}</span>{page==="memory"&&<Buttonprimarydisabled={busy}onClick={()=>{if(busy||!canLeave())return;setSelection(s=>({...s,[scope]:"new"}));setEditing(true);setFocused(true);setDirty(false);setSection(0);}}>Newcard</Button>}</div>
{<divclassName="rv-operations"><divclassName="gc-operation-intro"><ShieldChecksize={19}/><span>{page==="pi"?"Installation diagnostics":`${currentWorkspace?.name} · Operational context`}</span><strong>{scenario==="ready"?"Ready for review":scenario}</strong></div><divclassName="rv-operation-body">{sections}<divclassName="rv-operation-detail">{hasList&§ion===0&&<detailsclassName="rv-choose-record"><summary>Chooserecord<span>{item?.title??"New record"}</span></summary>{list}</details>}{detail}</div></div></div>}
</>;
return<><divclassName={`rv-host rv-host-${width} gc-host cs-host ${portalOpen?"gc-portal-open":""}`}><headerclassName="rv-topbar"><buttonaria-label="Toggle portal navigation"onClick={()=>setPortalOpen(!portalOpen)}><Menusize={20}/></button><strong>Omics<span>Portal</span></strong><spanclassName="rv-topbar-divider"/><spanclassName="rv-topbar-location">Clinicaldata&research</span><spanclassName="rv-topbar-user">Administrator<spanclassName="rv-avatar">AD</span></span></header><divclassName="gc-host-body"><asideclassName="gc-portal"aria-label="Illustrative portal navigation"><pclassName="rv-eyebrow">Portal</p>{[[LayoutDashboard,"Overview"],[Users,"Patients"],[Database,"Clinical data"],[Settings2,"ThothII"]].map(([Icon,label])=>{constI=IconastypeofMenu;return<divkey={String(label)}className={label==="ThothII"?"current":""}><Isize={18}/><span>{String(label)}</span></div>;})}<small>Hostshellpreview</small></aside><divclassName="gc-mount"><divclassName="gc-brandline"><spanclassName="gc-brand">Thoth<span>II</span></span><span>Clinicalanalyticsworkbench</span><small>Contextshelfreview·V4</small></div><divclassName={`gc-app gc-variant-${variant}`}><divclassName="gc-global-top">{contextShelf}</div>{operation&&<sectionclassName="gc-operation-strip"aria-label="Current operation"><div><pclassName="rv-eyebrow">{busy?"Operation running":`Operation ${operation.state}`}</p><strong>{operation.label}</strong><small>{workspaces.find(w=>w.id===operation.workspace)?.name??"Installation"}·{models.find(m=>m.id===operation.model)?.name??operation.model}</small><pid="operation-lock">{busy?"Workspace and model are locked. Finish this operation before starting another.":"Workspace and model are available again."}</p></div>{busy&&<divclassName="rv-actions"><ButtononClick={()=>setOperation({...operation,state:"complete"})}>Demo: finish</Button><ButtononClick={()=>setOperation({...operation,state:"cancelled"})}>Demo: stop</Button><ButtononClick={()=>setOperation({...operation,state:"failed"})}>Demo: fail</Button></div>}</section>}<divclassName="gc-body"><asideclassName="gc-rail"><buttonclassName="gc-mobile-nav"onClick={()=>setNavOpen(!navOpen)}aria-expanded={navOpen}><Menusize={17}/>Sessions&navigation<ChevronDownsize={16}/></button><divclassName={`gc-navigation ${navOpen?"open":""}`}><navclassName="cs-core-nav"aria-label="Core navigation">{nav("core")}</nav><SessionRailworkspace={workspace}ready={ready}busy={busy}activeId={active?.id}canReadAll={canReadAll}onOpen={s=>{if(busy||!ready||!canLeave())return;setNavOpen(false);if(s.status==="paused")setResumeTarget(s);else{resetDetail();setQuestion("");setActive(s);writeRoute({...route,page:"core"});}requestAnimationFrame(()=>mainRef.current?.focus());}}/>{canReadAll&&<detailsclassName="cs-admin-nav"open={variant==="A"?true:undefined}><summary><Settings2size={16}/>Administration<ChevronDownsize={14}/></summary><navaria-label="Administration navigation">{(["database","memory","evidence","workspace","pi"]asPage[]).map(nav)}</nav></details>}</div></aside><mainref={mainRef}tabIndex={-1}className={`gc-main rv-variant-${variant}`}aria-label={ready?titles[page]:"Context required"}>{notice&&<divclassName="rv-notice"role="status"><span>{notice}</span><buttonaria-label="Dismiss notice"onClick={()=>setNotice("")}><Xsize={16}/></button></div>}{dirty&&<divclassName="rv-dirty"role="status">Unsaveddraft.Saveordiscardbeforechangingworkspaceorpage.</div>}{!ready?gate:<>{resumeReview}{page==="core"&&active&&<divclassName="gc-new-question"><Buttondisabled={busy}onClick={()=>{if(busy||!canLeave())return;setActive(null);setQuestion("");setDirty(false);setNotice("Ready for a new question in the selected workspace.");}}>Newquestion<ArrowRightsize={15}/></Button></div>}{page==="core"||page==="sessions"?core : administration}</>}<footerclassName="gc-page-footer"><span>Prototype·Syntheticcontentonly</span><span>{ready?"Context applies across ThothII":"Core and administration are locked"}</span></footer></main></div></div></div></div></div>
{import.meta.env.DEV&&<asideclassName="rv-lab gc-lab"aria-label="Prototype review controls"><divclassName="rv-lab-main"><spanclassName="rv-lab-tag">CONTEXTSHELF·V4</span><buttonaria-label="Previous variant"onClick={()=>cycle(-1)}><ArrowLeftsize={17}/></button><selectaria-label="Layout variant"value={variant}onChange={e=>{if(canLeave()){writeRoute({...route,variant: e.target.valueasVariant},true);resetDetail();setQuestion("");setShelfOpen(!ready);}}}>{Object.entries(variants).map(([k,v])=><optionkey={k}value={k}>{k}·{v}</option>)}</select><buttonaria-label="Next variant"onClick={()=>cycle(1)}><ArrowRightsize={17}/></button><spanclassName="rv-lab-help">Noserverwrites</span></div><details><summary>Reviewscenarios&state</summary><divclassName="rv-lab-options"><label>Hostwidth<selectaria-label="Host width"value={width}onChange={e=>setWidth(e.target.value)}><optionvalue="full">Availablewidth</option><optionvalue="compact">960pxhost</option><optionvalue="phone">390pxhost</option></select></label><label>Availability<selectaria-label="Availability scenario"disabled={busy}value={availability}onChange={e=>{if(canLeave()){setAvailability(e.target.valueasAvailability);resetDetail();}}}><optionvalue="available">Available</option><optionvalue="unavailable">Providerunavailable</option><optionvalue="no-workspaces">Noworkspaces</option><optionvalue="no-models">Nomodels</option><optionvalue="needs-preprocessing">Needspreprocessing</option></select></label><Buttondisabled={busy}onClick={()=>{if(busy||!canLeave())return;setOperation(null);writeRoute({...route,workspace:""});setModelId("");resetDetail();setActive(null);setQuestion("");setResumeTarget(null);setShelfOpen(true);}}>Clearcurrentcontext</Button><Buttondisabled={busy||!ready}onClick={()=>{if(!busy&&canLeave()){setResumeTarget(sessions[2]);setNavOpen(false);}}}>Demo: Geneticssessionlink</Button><Buttondisabled={busy}onClick={()=>{if(busy||!canLeave())return;if(!forget()){setNotice("Browser storage could not be cleared.");return;}location.reload();}}>Forgetsavedchoices</Button><Buttondisabled={busy}onClick={()=>{if(busy||!canLeave())return;setCanReadAll(!canReadAll);writeRoute({...route,page:"core"});resetDetail();}}>Demoaccess:{canReadAll?"Administrator":"Analyst"}</Button><p>Workspacesource:{sources.workspace}·Modelsource:{sources.model}</p><p>Page:{page}·Workspace:{workspace||"none"}·Model:{model?.name??"none"}·Gate:{ready?"open":"closed"}·Draft:{dirty?"unsaved":"clean"}·Operation:{operation?.state??"idle"}</p><p>Onlyexplicitworkspace/modelchoicespersistonthisbrowser.Reloadresetssimulatedoperationsanddrafts.Defaultsareillustrative.V1/V2/V3arepreservedonports5174/5175/5176.</p></div></details></aside>}
The URL keys A/B/C identify the three descendants of C in this new review, not
the old context-bar/context-rail/context-shelf alternatives. Switch in the bottom
review bar without reloading, or with arrow keys outside interactive controls.
## What this iteration compares
All variants have one collapsible panel at the top of the entire ThothII app.
The panel is collapsed when valid selections have been resolved, but opens when
the required context is incomplete. A visible summary always identifies workspace
and interaction model. Opening the panel reveals both selectors and their origins.
| Proposal | Top panel | Session rail |
| --- | --- | --- |
| C1: Compact strip | A compact workspace/model summary; two side-by-side selectors when expanded | Administration precedes the session tabs, closest to the previous navigation order |
| C2: Context ribbon | Separate labelled workspace/model areas aligned with the expanded fields | Sessions first, with compact divided rows; administration below |
| C3: Workspace heading | Workspace is the main heading, model a secondary read-only label; expanded panel has explanation beside stacked fields | Sessions first, with individually bounded rows and administration below |
On narrow containers, fields stack and sessions/navigation expand inline above
the main content. The illustrative left navigation belongs to Omics Portal,
not to the ThothII application. Warm surfaces, Manrope controls, Fraunces headings
and the red portal frame continue the prior Impeccable direction.
## Session management restored to the right rail
- Remove the central “Continue where you left off” area and its separate session
navigation link. Access sessions from the right rail in every proposal.
- Restore My sessions / All sessions tabs, with an explicit selected tab and
keyboard Left/Right/Home/End navigation. Search and paused/archived groupings
are represented with synthetic records.
- All sessions is an authorized administrator view, not a cross-workspace selector.
Both tabs use the globally selected workspace. The administrator fixture adds
a colleague-owned archived record; the analyst fixture hides All sessions and
administration. Actual production permissions remain backend-owned.
- Resume stays behind a review of the session's original workspace/revision and
the globally selected model. Genuine archives remain read-only pending the
already-asked archive/restoration clarification.
- This is a layout study, not authorization to remove original session grouping,
rename, archive, bulk operations or other existing behavior from production.
Those must be retained and verified in the eventual implementation.
## Default and last-choice audit
The concepts already exist in current code, but in different places:
| Value | Existing source | Finding |
| --- | --- | --- |
| Installation workspace default | `Settings.workspace`, read from `SETTINGS_FILE`, normally `backend/data/settings.json` in local development | The inspected local file has `workspace: "psd"`. It is not a newly introduced YAML field. |
| Session model default | `modelCatalog.defaults.session` in the installation YAML | The PSD configuration has `zai/glm-5.3`. |
| Metadata model default | `modelCatalog.defaults.metadataGeneration` in the same YAML | The PSD configuration also has `zai/glm-5.3`; the schema allows the two defaults to differ. |
| Last personal choice | `frontend/src/workspaces/preferences.ts` | Currently module-level memory only, not durable browser storage. A page reload loses it. |
return<sectionclassName="cs-sessions"aria-label="Session navigation"><divclassName="cs-session-heading"><FolderClocksize={17}/><h2>Sessions</h2><span>{rows.length}</span></div>{canReadAll?<divclassName="cs-session-tabs"role="tablist"aria-label="Session scope">{(["mine","all"]asconst).map((value,i)=><buttonkey={value}ref={node=>{tabs.current[i]=node;}}id={`scope-${value}`}type="button"role="tab"aria-controls="session-list"aria-selected={effectiveScope===value}tabIndex={effectiveScope===value?0:-1}disabled={!ready}onClick={()=>setScope(value)}onKeyDown={e=>onKey(e,i)}>{value==="mine"?"My sessions":"All sessions"}</button>)}</div>:<pclassName="cs-mine-label">Mysessions</p>}<divid="session-list"role={canReadAll?"tabpanel":undefined}aria-labelledby={canReadAll?`scope-${effectiveScope}`:undefined}>{effectiveScope==="all"&&<pclassName="cs-scope-note">Administratorview·Selectedworkspace</p>}<labelclassName="rv-search"><Searchsize={15}/><inputaria-label="Search sessions"placeholder="Search sessions"disabled={!ready}value={query}onChange={e=>setQuery(e.target.value)}/></label>{(["paused","archived"]asconst).map(status=><divclassName="cs-session-group"key={status}><pclassName="rv-eyebrow">{status==="paused"?"In progress":"Archived"}<span>{rows.filter(s=>s.status===status).length}</span></p>{rows.filter(s=>s.status===status).map(s=><buttonkey={s.id}className="cs-session-row"disabled={busy||!ready}aria-current={s.id===activeId?"true":undefined}onClick={()=>onOpen(s)}><spanclassName="cs-session-mark">{status==="paused"?String(s.phase).padStart(2,"0"):"✓"}</span><span><stronglang="it">{s.title}</strong><small>{effectiveScope==="all"?`${s.owner} · `:""}{status==="paused"?"Paused · Can resume":"Read-only archive"}</small></span><ChevronRightsize={13}/></button>)}</div>)}{!rows.length&&<pclassName="cs-session-empty">{!ready?"Choose a valid workspace and model to access sessions.":query?"No sessions match this search.":"No sessions in this workspace yet."}</p>}</div></section>;
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.