Panel components and stylesheets

Stability: provisional · Panel API version: 1.0 · SDK path: sdk/1/

Stylesheets

panel.css

SciStudio panel stylesheet (SDK major 1).

One shared sheet so every panel — built-in, packaged, or agent-written — looks like the rest of the application without re-deriving its styling. The host injects the app's design tokens (--ss-ink, --ss-canvas, --ss-ember, --ss-pine, --ss-sea, --ss-sand, plus the light/dark mode flag) into the frame; the fallbacks below keep a panel readable when it is opened standalone, for example from its panel.sample.json.

Link it from a panel document:

<link rel="stylesheet" href="../../sdk/1/panel.css" />

Classes are semantic, not utilities: describe what a thing IS (a card, a toolbar, a field) rather than how it looks, so the look can change centrally.

renderers.css

Styles for the data views in renderers.js (SDK major 1). Link it after panel.css, which supplies the design tokens these rules use:

<link rel="stylesheet" href="../../sdk/1/panel.css" />
<link rel="stylesheet" href="../../sdk/1/renderers.css" />

Theme tokens

The SDK copies the host's --ss-* theme tokens onto the panel's root element and sets data-theme to light or dark. The stylesheets read these tokens, as space-separated RGB channels, with fallbacks for sample mode.

  • --ss-canvas
  • --ss-ember
  • --ss-ink
  • --ss-pine
  • --ss-sand
  • --ss-sea

UI components: sdk/1/panel-ui.js

SciStudio panel component set (SDK major 1).

A small set of Preact components covering the shapes panels actually need, so a panel — built-in, packaged, or agent-written — is assembled from named parts instead of re-deriving markup and styling. Pair it with panel.css, which gives these components the application's look through the host-injected design tokens.

import { html, render } from "../../lib/preact-htm@3.1.1/dist/preact-standalone.module.js";
import { Panel, Card, Meta, Field, Button } from "../../sdk/1/panel-ui.js";

render(html`<${Panel}>
  <${Meta} items=${["Array", "shape [3, 3]"]} />
  <${Card}>…<//>
<//>`, document.getElementById("root"));

Components take plain props and children; nothing here talks to the host — data comes from the SDK (window.scistudio) in the panel's own code. Props a component does not name are passed to its root element.

The questionnaire components ask the user what a MiniApp should do before it is built. The questions are data: panels/<id>/questionnaire.json, rendered by Questionnaire, submitted through scistudio.submitAnswers, and checked by the validate_panel agent tool. Every question is optional and every question offers "Decide for me"; a spec cannot change either.

import { Questionnaire } from "../../sdk/1/panel-ui.js";

await scistudio.ready();
const spec = await (await fetch("questionnaire.json")).json();
render(html`<${Panel}><${Questionnaire} spec=${spec} onSubmit=${scistudio.submitAnswers} /><//>`, root);

questionnaire.json is {title, intro?, submit_label?, questions}. Each question has id (lowercase, unique), type, prompt, and optional help: single and multiple take options (at least two {value, label, description?}) and allow_other; text takes multiline and placeholder; number takes min, max, step, unit, placeholder; range is a slider and needs min and max. An answer is {status: "answered", value, other?}, {status: "decide_for_me"}, or {status: "skipped"}.

Panel

The panel shell: a padded column with the standard vertical rhythm. Use it as the root of a panel.

Prop Type Default Description
class string optional Extra class names added to the root element.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional Content.

Other props are passed to the root element.

Stack

A vertical stack with the standard gap.

Prop Type Default Description
class string optional Extra class names added to the root element.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional Content.

Other props are passed to the root element.

Row

A horizontal row. Put a Spacer between children to push them apart.

Prop Type Default Description
class string optional Extra class names added to the root element.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional Content.

Other props are passed to the root element.

Spacer

Flexible empty space inside a Row.

Takes no props.

Card

The bordered surface the application uses for grouped content.

Prop Type Default Description
class string optional Extra class names added to the root element.
tight boolean optional Use the smaller padding.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional Content.

Other props are passed to the root element.

Meta

A metadata strip such as ["Array", "shape [3, 3]", "dtype float64"]. The first item is emphasised, matching the application's summary bars.

Prop Type Default Description
items string[] optional The entries; empty, null, and undefined entries are skipped.
class string optional Extra class names added to the root element.
children any optional Extra content after the items.

Other props are passed to the root element.

Hint

Muted helper text.

Prop Type Default Description
class string optional Extra class names added to the root element.
children any optional Content.

Other props are passed to the root element.

Label

A small uppercase section label.

Prop Type Default Description
class string optional Extra class names added to the root element.
children any optional Content.

Other props are passed to the root element.

Badge

A rounded pill for counts and states.

Prop Type Default Description
class string optional Extra class names added to the root element.
children any optional Content.

Other props are passed to the root element.

Button

A button in the application's style. It is type="button", so it never submits a form.

Prop Type Default Description
class string optional Extra class names added to the root element.
primary boolean optional Use the emphasised style for the main action.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional The button's label.

Other props are passed to the root element.

Input

A text input in the application's style.

Prop Type Default Description
class string optional Extra class names added to the root element.
number boolean optional Use the narrow numeric width.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).

Other props are passed to the root element.

Select

A drop-down list in the application's style.

Prop Type Default Description
class string optional Extra class names added to the root element.
options Array<string \| {value, label}> optional The choices; a string is both value and label.
children any optional Extra <option> elements after options.

Other props are passed to the root element.

Field

A labelled control row: a name, the control, and an optional readout. The shape the slice selectors and similar per-axis controls use.

Prop Type Default Description
name any optional The label before the control.
readout any optional A value shown after the control.
class string optional Extra class names added to the root element.
children any optional The control.

Other props are passed to the root element.

ScrollArea

A scrollable data surface; put a table or grid inside.

Prop Type Default Description
class string optional Extra class names added to the root element.
elementRef object optional A Preact ref that receives the scrolling element.
children any optional Content.

Other props are passed to the root element.

Table

A dense table with sticky headers.

Prop Type Default Description
class string optional Extra class names added to the root element.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional The <thead> and <tbody>.

Other props are passed to the root element.

Legend

A value-scale legend: minimum label, colour ramp, optional middle label, maximum label.

Prop Type Default Description
min any optional Label at the low end.
mid any optional Label in the middle; omitted when undefined.
max any optional Label at the high end.
stops string[] optional CSS colours forming the ramp, low to high.
class string optional Extra class names added to the root element.

Other props are passed to the root element.

ItemGrid

A responsive grid of Item cards, for collection or composite children.

Prop Type Default Description
class string optional Extra class names added to the root element.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional Content.

Other props are passed to the root element.

Item

One clickable item card: a name over a secondary line.

Prop Type Default Description
name any optional The item's name.
sub any optional The secondary line, such as its type.
title string optional Tooltip text.
class string optional Extra class names added to the root element.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional Extra content.

Other props are passed to the root element.

ListRow

One full-width clickable row: a label over a value, and an optional trailing hint on the right. The shape a slot inventory or any name/type listing uses.

Prop Type Default Description
label any optional The row's label.
value any optional The value under the label.
trailing any optional A hint on the right, such as Preview →.
class string optional Extra class names added to the root element.
elementRef object optional A Preact ref that receives the root DOM element (a plain ref on a function component does not reach it).
children any optional Extra content under the value.

Other props are passed to the root element.

Pager

Plain navigation for complete, paginated data: a label such as rows 1–50 of 200 · page 1/4 between Previous and Next. Paging is navigation, never a sign that data is partial, so do not show a truncation warning beside it.

Prop Type Default Description
page number required The current page, from 1.
totalPages number required The number of pages.
label string optional The text between the buttons; defaults to page <page>/<totalPages>.
onPrev function optional Called by Previous; disabled on page 1.
onNext function optional Called by Next; disabled on the last page.
class string optional Extra class names added to the root element.

Other props are passed to the root element.

Icon

A lucide icon by name, in lucide's PascalCase (ChevronRight) or kebab-case (chevron-right). Needs the lucide library loaded as window.lucide; renders nothing when the library is absent or the name is unknown, so a missing icon never breaks a panel.

Prop Type Default Description
name string required The icon name.
class string optional Extra class names added to the root element.

Other props are passed to the root element.

ErrorState

An error state (role="alert"). Use it for a read that failed, never for complete data that is paged.

Prop Type Default Description
class string optional Extra class names added to the root element.
children any optional The message.

Other props are passed to the root element.

LoadingState

A loading state (aria-busy). Show it until the first data arrives, so a slow read does not flash an empty surface.

Prop Type Default Description
class string optional Extra class names added to the root element.
children any optional The message; defaults to Loading….

Other props are passed to the root element.

EmptyState

An empty state for a target that genuinely has nothing to show.

Prop Type Default Description
class string optional Extra class names added to the root element.
children any optional The message.

Other props are passed to the root element.

SingleChoiceQuestion

A single-choice question: one pill per option, plus "Decide for me". Choosing the chosen option again clears it back to skipped.

Prop Type Default Description
question object required The question from the spec: {id, prompt, help?, options, allow_other?}.
answer object optional The current answer; skipped when absent.
onChange function optional Receives the new answer.

MultipleChoiceQuestion

A multiple-choice question: pills that toggle independently, plus "Decide for me". Clearing every pill returns the question to skipped.

Prop Type Default Description
question object required The question from the spec: {id, prompt, help?, options, allow_other?}.
answer object optional The current answer; skipped when absent.
onChange function optional Receives the new answer.

TextQuestion

A free-text question, one line or several, plus "Decide for me". Empty text is skipped.

Prop Type Default Description
question object required The question from the spec: {id, prompt, help?, multiline?, placeholder?}.
answer object optional The current answer; skipped when absent.
onChange function optional Receives the new answer.

NumberQuestion

A number question — a number box, or a slider when the spec's type is range — plus "Decide for me". An empty box is skipped.

Prop Type Default Description
question object required The question from the spec: {id, type, prompt, help?, min?, max?, step?, unit?, placeholder?}.
answer object optional The current answer; skipped when absent.
onChange function optional Receives the new answer.

Question

One question of any type, drawn by the component for its type.

Prop Type Default Description
question object required The question from the spec.
answer object optional The current answer; skipped when absent.
onChange function optional Receives the new answer.

SubmitBar

The bar under the questions: a Submit button that is enabled with any number of answers, and the message a submit left behind.

Prop Type Default Description
label string optional The button text; defaults to Submit.
busy boolean optional A submit is in flight; the button is disabled only then.
message string optional Text beside the button, such as the submit result's message.
kind string optional "done", "return" (go back to your AI chat), or "error"; colours the message.
onSubmit function optional Called when the button is pressed.

Questionnaire

A whole questionnaire from its spec: title, intro, every question, and the submit bar. A spec that breaks a rule renders an error state that lists each problem instead of a form. Submitting sends one answer per question — skipped for those left alone — to onSubmit and shows the message it resolves with; the result's notified: false shows as a reminder to return to the AI chat.

Prop Type Default Description
spec object required The parsed questionnaire.json.
onSubmit function optional Receives the answers keyed by question id and returns a promise; pass scistudio.submitAnswers.
initialAnswers object optional Answers to start from, keyed by question id.
class string optional Extra class names added to the root element.

Other props are passed to the root element.

Data views: sdk/1/renderers.js

The data views SciStudio's own previews are built from, importable by any panel. Load panel.css and renderers.css, and use the vendored Preact at ../../lib/preact-htm@3.1.1/dist/preact-standalone.module.js; a second Preact instance breaks hooks.

import { html, render } from "../../lib/preact-htm@3.1.1/dist/preact-standalone.module.js";
import { ArrayView, TextView } from "../../sdk/1/renderers.js";

The views take values, controlled state, and callbacks. They never call window.scistudio: the panel reads the data, keeps the state, and decides what saving or opening does. Importing a module mounts nothing, and several instances can share a page. Every view accepts error, which takes precedence over data, and shows its loading state while its main data prop is absent. Pass new objects when values change; inputs are not mutated.

Each view also lives in its own renderer-*.js module. Other named exports of those modules are helpers, not part of this contract.

ArrayView

Module: sdk/1/renderer-array.js

The numeric heatmap for an array of any dimensionality: one plane is shown, and every other axis gets an index control.

Give it either local values in data, or a caller-owned plane and tile for a bounded remote array read with array.plane and array.tile. Local data is windowed automatically; with plane/tile the caller reads the window that onScroll asks for.

Prop Type Default Description
data number \| Array \| TypedArray optional Local values: a scalar, rectangular nested arrays, or a typed numeric array. Flat data with shape is indexed row-major.
shape number[] optional Shape of data; inferred from nested arrays when omitted. Must match the number of values.
axes string[] optional Axis names, one per dimension.
dtype string optional Data type label shown in the summary; number for local data when omitted.
indices object optional Selected index per non-displayed axis, {axis: index}.
onSliceChange function optional (axis, index) when the reader moves an index control; update indices in response.
plane object optional An array.plane result: source_shape, source_dtype, axes, slice_axes, vmin, vmax.
tile object optional The loaded window {values, y0, x0}, values as rows of cells.
rowHeight number optional Row height in pixels used to place the window.
scrollRef object optional A Preact ref for the scrolling element.
onScroll function optional (event) when the surface scrolls; read the window it now shows.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.

DataFrameView

Module: sdk/1/renderer-dataframe.js

A paged, sortable table. The caller reads each page (for example with table.page) and passes it in; the view asks for another page or sort order through onQueryChange.

Prop Type Default Description
data object optional The page: {columns, rows, total?, total_rows?, page?, page_size?, total_pages?, sort?: {by, direction}}. The loading state shows until it is given.
query object { page: 1, pageSize: 50 } The requested {page, pageSize, sortBy?, sortDir?}.
loading boolean false Marks a read in flight.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.
onQueryChange function () => {} (nextQuery) when the reader pages or sorts; sorting returns to page 1.

SeriesView

Module: sdk/1/renderer-series.js

A series as a line chart or a table of every row. Rows without a finite value are drawn as breaks in the line, listed in a notice, and shown as they are in the table.

Prop Type Default Description
data object optional A series.points result {index, values, offset?, total?}, several pages with their index and values joined in order, or computed values and index arrays. Every row is shown; nothing is sampled.
loading boolean false Set while further pages are still being read; the view says how many rows of data.total it holds.
mode "chart" \| "table" "chart" Which view to show.
onModeChange function () => {} (mode) when the reader switches view.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.
plotly object globalThis.Plotly The Plotly library for chart mode; load plotly@2.35.3 from the shared libraries. Table mode needs none.

TextView

Module: sdk/1/renderer-text.js

A text document, shown literally in a scrolling surface.

Prop Type Default Description
text string "" The text read so far.
meta object {} The latest text.chunk result, or {total_bytes?, encoding?, next_offset?}; null shows the loading state.
done boolean true false while more text is still being read.
hasMore boolean false More of the document remains and is not being read right now; shows how much is on screen and a Read more control.
onReadMore function optional Called by Read more; read the next chunks and append them to text.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.

ArtifactView

Module: sdk/1/renderer-artifact.js

A stored file: its path, MIME type, and size, with the image inline when it is one.

Prop Type Default Description
info object optional An artifact.info result {name?, path?, mime_type?, size?}; the loading state shows until it is given.
url string optional URL of the file's bytes, such as an artifact.file result's url or a caller-owned blob URL.
imageFailed boolean false Set after the image failed to load, to explain why nothing is shown.
onImageError function () => {} Called when the inline image fails to load.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.

MetadataView

Module: sdk/1/renderer-base.js

The fallback view for any data object: its type and ancestry, shape and dtype, stored file, and recorded metadata.

Prop Type Default Description
meta object optional A metadata result {type_chain?, shape?, dtype?, metadata?}; the loading state shows until it is given.
file object optional The stored file {name?, path?, mime_type?, size?, url?}, when there is one.
imageFailed boolean false Set after the image failed to load.
onImageError function () => {} Called when the inline image fails to load.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.

CollectionView

Module: sdk/1/renderer-collection.js

A grid of a collection's items. The caller owns paging and what opening an item does.

Prop Type Default Description
items object[] [] The items read so far, {ref?, data_ref?, display_name?, type_name?, metadata?}.
count number items.length Total number of items in the collection.
itemType string "items" What the items are called in the summary.
loading boolean false Marks a read in flight.
hasMore boolean false Show the Show more control.
onLoadMore function optional Called by Show more.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.
onOpen function optional (ref, item) when the reader opens an item; items are not clickable without it.

CompositeView

Module: sdk/1/renderer-composite.js

The slot inventory of a composite, one row per slot.

Prop Type Default Description
slots object[] \| null [] The slots {name, type_name?, ref?}, as composite.slots returns them; null shows the loading state.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.
onOpen function optional (ref, slot) when the reader opens a slot; rows are not clickable without it.

PlotView

Module: sdk/1/renderer-plot.js

A rendered plot figure with zoom, PDF paging, and a Save control for the formats the plot was rendered in.

Prop Type Default Description
info object optional An artifact.info result for the plot {name?, mime_type?, formats?}; the loading state shows until it is given.
file object optional The figure: {mime_type?, url?} for an image, or {mime_type, data: ArrayBuffer} for a PDF.
zoom number 1 Zoom factor, from 0.5 to 4 in 25% steps.
onZoom function () => {} (next) when the reader zooms.
saveFormat string optional The selected save format, one of info.formats.
onSaveFormatChange function () => {} (format) when the reader picks a format.
page number 1 Current PDF page, from 1.
onPageChange function () => {} (page) when the reader turns a PDF page.
saving boolean false Marks a save in flight.
onSave function optional (format) when the reader saves; read that format's own bytes (for example artifact.file with variant). Save is disabled without it.
error string optional A displayable message. It takes precedence over any data, so a failed read never leaves earlier values looking current.
libBaseUrl string optional scistudio.libBaseUrl; needed to render PDF figures.