For developers
API reference
Everything a host can reach: the four elements, their attributes, properties and methods, the events they dispatch and what the packages export. The names here are the ones in the package's frozen surface — a name that changes is a breaking change and is in the changelog. How to get started is on the installation page.
<guide-preview> — the viewer
The element a visitor's page loads. It runs a guide, one step or one page at a time, keeping the answers when the visitor goes back.
active-localeattributeactive-locale="sv"The language to show the guide in, as a locale code. Setting the attribute goes through the activeLocale property, so both the rendered text and the engine switch; removing it falls back to the default source locale. It is also read when the graph loads, so the first paint is already in the right language.
answer-displayattributeanswer-display="trail"Shows the visitor's earlier answers above the current step. "trail" lists each question with the answer as displayed (unit included); "history" is a plainer list of the question and the chosen option's label. Absent or any other value: no list.
example-filesattributeexample-filesBoolean. When present, a file field whose step carries an example photo gets a button beside the file picker that fetches the example and attaches it. Without it no such button is drawn, even if the guide has an example photo. Meant for demo and try-it pages.
provingattributeprovingBoolean. Marks a trial run inside the editor: file fields and map questions get stand-ins (a sample picture, a drawn map) instead of the real picker, hidden conditional fields stay hidden, and no guide-progress event is fired. Set by the editor; a host should not normally set it on a published page.
activeLocalepropertyactiveLocale: string (write-only)Sets the language the guide is shown in and tells the engine, so answers and templates follow; re-renders at once. Same effect as the active-locale attribute, which routes through this property. It has no getter, so reading it returns undefined.
clearmethodclear(message?: string): voidRemoves the guide from the viewer and shows an empty state with the given message. The default text is "Ingen guide har valts." (Swedish, not localised). Set graph again to show a guide.
getAnswersmethodgetAnswers(): AnswersThe visitor's answers so far, as an object keyed by variable name. Only answers on the path the visitor actually took are included. Returns an empty object when no guide is loaded. Files are not in it; use getFiles().
getCurrentNodeIdmethodgetCurrentNodeId(): string | nullThe id of the step the visitor is on, or null when no guide is loaded. Save it alongside getAnswers() and hand both back through given to let the visitor resume later.
getFilesmethodgetFiles(): { fieldId: string; variableName: string; file: File }[]The files the visitor has chosen in the guide's file fields, with each field's id and variable name. The viewer sends them nowhere; the host collects them when it is time to submit. Stand-ins from a trial run are never included.
getFormDatamethodgetFormData(): FormDataEvery answer and file encoded the way a browser encodes a form, ready to post with fetch as multipart. A file variable appears once, as the file; answers from a repeating page become name[index].field; a file with no variable name goes under file-<field id>.
getOutputmethodgetOutput(): EmailResultOutput | nullIf the current step produces output (today the email result), returns the recipient, subject and body, both as the template and with answers filled in, plus any variables that were missing. Otherwise null.
getStepNumbermethodgetStepNumber(): numberWhich step in sequence the visitor is on, as counted by the engine. Returns 1 when no guide is loaded. Mainly for the editor's second mirror of the same run; the viewer already shows the step number itself.
getTraversedConnectionIdsmethodgetTraversedConnectionIds(): string[]The ids of the guide's connections the run has passed through, in order. Empty when no guide is loaded. Mainly used by the editor to highlight the route on the canvas.
givenpropertygiven: GivenAnswers | null // { answers: Answers; nodeId?: string | null }Answers the host already knows, such as a name from a login or a saved run. They fill the fields but nothing is locked: the visitor can change them and they are validated as if typed. nodeId opens the guide on that step at load; can be set before or after graph and survives a restart.
graphpropertygraph: GraphData | nullThe guide to show, in the standard format. Older formats are migrated on load; a guide from a newer version is refused and the viewer says so instead of drawing a broken page. Reading returns a copy, or null when nothing is loaded.
mirrormethodmirror(source: GuidePreview | null): voidMakes this viewer draw the same run as another one, sharing its engine rather than a copy, so Next in one is Next in the other. null detaches it. Internal wiring for the editor's canvas; a host rarely needs it.
previousmethodprevious(): voidGoes back one step, as the visitor's Back button does, and fires preview-node-changed. Does nothing if there is no step to go back to.
refreshmethodrefresh(): voidRe-renders from the engine and drops any shown draft. For a mirrored viewer whose engine someone else moved; internal wiring for the editor.
restartmethodrestart(): voidStarts the guide over from the first step as a new case. The host's given answers are filled in again, but the step in given is not reused. Fires preview-node-changed and guide-progress with "restart".
reviewDeclarationEnabledpropertyreviewDeclarationEnabled: booleanWhether the review step shows the declaration of what the decision rests on beneath the visitor's answers. Defaults to true; set false on pages where there is no decision to account for, such as an expression of interest.
routeAnalysisEnabledpropertyrouteAnalysisEnabled: booleanWhether the viewer shows the routes: where a step leads on cards such as rules and service calls, and which paths reach a result in compact mode. Defaults to true. These are cards a visitor seldom stops on; in practice the editor sets it from its capabilities.
setThememethodsetTheme(theme: "light" | "dark" | null): voidSets the viewer's theme, same as the theme property. null hands the choice back to the operating system setting.
showDraftmethodshowDraft(values: Answers): voidShows unsaved values in the page's fields, namely what the other mirrored viewer is typing, without storing them. Internal wiring for the editor's mirroring; a host rarely needs it.
showNodemethodshowNode(nodeId: string): booleanJumps straight to a step and draws it. If the id is a field on a page, the page is shown with that field highlighted. Returns true if the step could be shown. Used by the editor to show the selected node; fires no event.
shownAnswerspropertyshownAnswers: Answers (write-only)Seeds answers so an already-answered step can be drawn with them, such as a picture on the editor's canvas. Decides nothing in the run. Internal wiring for the editor; it has no getter.
themepropertytheme: "light" | "dark" | nullLight or dark theme for the viewer; null follows the operating system. Written as data-fw-theme on the element, not the document, so the host's page around it is not recoloured.
unmatchedGivenmethodunmatchedGiven(): string[]The names in given that no node in the guide declares as a variable. Those values are kept anyway, but the list explains why a field stayed empty. The same names are also logged with console.info.
variableInspectorEnabledpropertyvariableInspectorEnabled: booleanWhether the viewer shows a collapsible list of the variables' current values below the guide. Defaults to true; a host would normally turn it off on a published page.
<guide-editor> — the editor
The element the person editing builds the guide in. The host decides what it offers, stores what it hands over and listens to its events.
active-localeattributeactive-locale="<language code>"Which of the guide's languages editing starts in. It only applies if the guide offers that language; otherwise a translator lands in the first non-source language and everyone else in the source language. It is a view and is never stored in the guide; read the current language with contentLocale.
editor-localeattributeeditor-locale="sv" | "en"The language of the tool itself: palette, panel, menus and messages. It is separate from the guide's content language (active-locale). The editor ships strings in Swedish and English.
feature-levelattributefeature-level="basic" | "advanced" | "service"How much of the editor the person editing gets: which node types and features are offered. Ignored while the modules attribute is set. Mirrored by the featureLevel property.
get-started-hrefattributeget-started-href="<url>"Address of the host's own getting-started page. With it, Help offers a Getting started item that leads there; without it the item is not shown.
modeattributemode="administrator" | "edit" | "translator" | "readonly"What the person editing may change. Without the attribute, or with an unknown value, the editor is locked read-only: the host has to grant capability explicitly. The mode is a view, not protection — permissions are the host's job. Sets the mode property.
mode-noticeattributemode-notice="off"With "off", the panel's box explaining why fields are locked is hidden. For a host that says so itself, for example in its own bar above the editor. The mode is unaffected.
modulesattributemodules="<module ids, space- or comma-separated>"Builds the editor's capabilities from modules instead of a level: the core (multiple choice, rules, variables, import/export) is always on and each module adds more. An empty attribute gives just the core; with no attribute, feature-level decides. The menu only lists modules backed by a registered node type, so submission appears only once PRO is loaded.
palette-openattributepalette-open (boolean)Whether the full node palette is expanded. The host sets it before first paint and hears palette-open-changed with { open } when the person editing changes it; the library stores nothing. Mirrored by the paletteOpen property.
palette-searchattributepalette-search="off"With "off", the node palette has no search field (Search node). The host's choice when setting the editor up.
panel-openattributepanel-open (boolean)Whether the side panel (Properties/Preview) is open over the canvas. The person editing opening or folding it is announced with panel-open-changed and { open }; the host decides whether to remember it and sets it before first paint. Mirrored by the panelOpen property.
panel-widthattributepanel-width="<px>"The side panel's width in pixels. When the person editing drags its edge, panel-width-changed fires with { width } (null after a reset), and the host can store the value and hand it back here. Mirrored by the panelWidth property.
themeattributetheme="light" | "dark"Light or dark theme for the editor. It is applied to the element, not the document, so the host page around it is unaffected. Sets the theme property.
addCustomNodeTypemethodaddCustomNodeType(spec: NodeTemplate): voidAdds a node template to the library, or replaces it if the key (spec.type) already exists. The palette refreshes, a confirmation toast is shown, and node-templates-changed fires with the whole list so the host can save it. The library is shared by every editor on the page.
buildIdpropertyreadonly buildId: stringWhich build of the editor is running: commit, whether there were uncommitted changes, and time. Meant for troubleshooting, such as a host's support page showing what a device is actually running.
capabilitiespropertycapabilities: EditorCapabilities (setter takes Partial<EditorCapabilities>)Reads the capabilities actually in effect (a copy). Setting it layers individual on/off overrides on top of what feature-level or modules give; each assignment replaces the previous overrides.
contentLocalepropertyreadonly contentLocale: stringThe language of the guide's content currently being edited, for example after the person editing switched language in the toolbar. Its starting value comes from active-locale and the mode.
featureLevelpropertyfeatureLevel: "basic" | "advanced" | "service"Same as the feature-level attribute: setting the property writes the attribute. Reads the level that is set, even when modules is in control instead.
getDatamethodgetData(): GraphDataThe guide as it stands, in the standard format and ready to store: nodes and connections from the canvas, the guide's settings, the node templates it uses, and the format version stamped on. This is what the host should save.
graphpropertygraph: GraphDataSetting graph opens a guide: it is migrated to the current format, the undo history and selection are reset, and a guide in an unsupported format is refused with an error message in the editor. Reading it returns the same as getData().
modepropertymode: "administrator" | "edit" | "translator" | "readonly"What the person editing may change; same values as the mode attribute, which sets this property. The property does not write the attribute back, and it does not validate the value — use one of the four.
modulespropertymodules: string[] | nullSame as the modules attribute: a list is written as a space-separated attribute, null removes the attribute. Reads the valid module ids after parsing (aliases resolved, unknown ones dropped), or null.
nodeTemplatespropertynodeTemplates: NodeTemplate[] (setter accepts unknown)The host's node templates. Setting the list replaces all non-built-in templates; invalid entries are skipped and the built-ins stay. Reading returns the editor-made templates without the built-ins. The library stores nothing — changes made in the editor are announced with node-templates-changed.
paletteOpenpropertypaletteOpen: booleanWhether the full node palette is expanded; setting it toggles the palette-open attribute.
panelOpenpropertypanelOpen: booleanWhether the side panel is open; setting it toggles the panel-open attribute. It is a view, not a change to the guide and not an undo step.
panelWidthpropertypanelWidth: number | nullThe side panel's requested width in pixels; setting it writes the panel-width attribute, and null removes it for the default width. Reads the requested value, not the width actually drawn within the bounds.
redomethodredo(): voidRedoes the last undone step, if there is one. Same as the person editing choosing Redo.
refreshGraphmethodrefreshGraph(value: GraphData): voidLoads a newer version of the same guide without moving the view: whoever is watching keeps their pan and zoom. Meant for e.g. a read-only window following a colleague's work. Otherwise like setting graph, so the undo history is reset.
removeCustomNodeTypemethodremoveCustomNodeType(type: string): voidRemoves a node template from the library (built-in templates cannot be removed). Nodes created from it keep working but carry the base type's name. Fires node-templates-changed.
replaceGraphmethodreplaceGraph(value: GraphData, reason: GraphChangedDetail["reason"]): voidReplaces the guide as an undoable step, unlike setting graph. The guide is migrated first, then graph-changed fires with { graph, reason }. The editor itself uses it for import and reset.
revealNodemethodrevealNode(nodeId: string, fieldId?: string): voidSelects the node and centres it in view. With fieldId, the side panel also opens on Properties and focus moves to that field. For a host that lists problems, such as what blocks publishing, and wants to take the person editing there.
setThememethodsetTheme(theme: ThemeChoice | null): voidSets the editor's theme; the same as assigning the theme property. The editor has no theme button of its own — the host builds the control in its interface and calls this.
showToastmethodshowToast(options: ToastOptions): void // { message: string; type: "success" | "error" | "info"; duration?: number }Shows a short message in the editor's own toast, for example that the guide was saved. The host supplies the text; it is not translated.
startTourmethodstartTour(): voidStarts the editor's step-by-step tour pointing out its parts. The same tour the person editing can start themselves.
themepropertytheme: "light" | "dark" | nullThe editor's theme, applied to the element so the host page is unaffected. The theme attribute sets the property, but the property does not write the attribute back. The library does not store the choice, and the editor fires no event when the property is set.
undomethodundo(): voidUndoes the last step, if there is one. Typing in progress is recorded as a step first. Same as the person editing choosing Undo.
unsupportedNodeTypesmethodunsupportedNodeTypes(graph?: GraphData): string[]Which node types in a guide the editor's current capabilities do not offer, as type names. Without an argument it checks the open guide; with one the host can ask before importing, for example to raise the level first.
updateCustomNodeTypemethodupdateCustomNodeType(type: string, changes: { label: string; icon?: string }): voidRenames a node template in the library and sets its icon. The change shows everywhere the template is used, and node-templates-changed fires. Omitting icon removes it.
<guide-versions> — the version list
The list of a guide's versions. It shows and asks; storing, publishing and deleting are the host's, when the list sends its intent events.
editor-localeattributeeditor-locale="sv"The language of the list's own buttons, headings and dates. Without it the source language is used.
orderattributeorder="saved" | "label" | "custom"How the rows are shown. saved and label sort a copy and change nothing on the host's side; custom shows the array exactly as the host handed it. Other values are ignored.
themeattributetheme="light" | "dark"Light or dark theme for this element only, in the same words as <guide-editor>. It never recolours the page around it, and the library does not remember the choice.
actionspropertyactions: VersionAction[] | null // "open" | "restore" | "activate" | "save" | "discard" | "duplicate" | "rename" | "note" | "delete"Which actions the host's storage can actually carry out; only those buttons are shown. null (the default) shows all of them except restore, which appears only when listed explicitly. Opening a row by clicking it is not governed by actions.
editorLocalepropertyreadonly editorLocale: string | undefinedReads the editor-locale attribute. There is no setter; set the language through the attribute.
orderpropertyorder: "saved" | "label" | "custom"The same as the order attribute, as a property. When the person editing drags a row, the element switches itself to custom.
setThememethodsetTheme(theme: "light" | "dark" | null): voidSets the theme; null hands the decision back to the operating system. The same as setting the theme property.
themepropertytheme: "light" | "dark" | nullThe theme as a property. It is written to data-fw-theme on the element, not on the document.
versionspropertyversions: GuideVersion[] // { id, number?, label?, note?, savedAt?, by?: { subject?, name?, me? }, reason?: "conflict", current?, open?, draft?, missing? }The versions to show, as the host describes them. Setting the property redraws the list; it is the only way to change it. The list never counts on its own: number is the host's.
<prompt-dialog> — the prompt dialog
The dialog the editor and the version list ask with. A host can use the same dialog for its own questions.
askmethodask(options: { title: string; label: string; message?: string; value?: string; placeholder?: string; confirmLabel: string; cancelLabel?: string }): Promise<string | null>Opens a modal dialog with a text field and resolves with what was typed, trimmed. Cancel, Escape or an empty field resolves null. Asking again while one is open cancels the earlier question.
choosemethodchoose(options: { title: string; message?: string; choices: Array<{ id: string; label: string }>; cancelLabel?: string }): Promise<string | null>The same dialog with one button per choice instead of a field. Resolves with the chosen id, or null if the person backs out.
editorLocalepropertyset editorLocale(value: string)The tool's language. It only affects the default text of the Cancel button; every other text is passed in by the host through ask() and choose(). Setter only, no attribute.
Events
What the elements tell the host. All are CustomEvents; the detail is described per event.
graph-changedeventCustomEvent<{ graph: GraphData; reason: "node-moved" | "node-nudged" | "node-created" | "node-removed" | "start-node-changed" | "node-updated" | "node-template-changed" | "guide-updated" | "graph-imported" | "form-skeleton-added" | "graph-reset" | "connection-created" | "connection-removed" | "connection-recolored" }> // bubbles, composedThe guide changed; detail.graph is the whole guide in the standard format and reason says why. It fires on every keystroke in a field (reason node-updated), so a host that saves should debounce its writes. This is the event init() listens to for onSave.
graph-reset-requesteventCustomEvent<null> // no detail; bubbles, composedThe person editing chose to start over and confirmed it in the editor's warning dialog. The editor changes nothing itself: the host decides what an empty or original guide is and hands it over with replaceGraph(graph, "graph-reset").
guide-progresseventCustomEvent<{ kind: "start" | "next" | "back" | "result" | "restart" | "submitted" | "validation-stopped"; nodeId: string | null; nodeType: string | null; step: number }> // bubbles, composedThe visitor's way through the guide, for analytics: start, next, back, reached a result, restarted, stopped by validation and (in PRO) submitted. It never carries answers. Silent in the editor's preview and test runs (the editor-view and proving attributes).
node-templates-changedeventCustomEvent<{ templates: NodeTemplate[] }> // bubbles, composedThe node template library changed, and detail.templates is the whole list. The library stores nothing; the host decides whether to save it and hands it back through nodeTemplates next time. init() wires it to onTemplatesChange.
palette-open-changedeventCustomEvent<{ open: boolean }> // bubbles, composedThe node palette was opened or folded. A view, not a change to the guide. The host may remember it and set paletteOpen before the editor is next drawn.
panel-open-changedeventCustomEvent<{ open: boolean }> // bubbles, composedThe side panel was opened or folded, by the person editing or by the editor. The library does not remember it; the host may store it and set panelOpen before the editor is next drawn.
panel-width-changedeventCustomEvent<{ width: number | null }> // bubbles, composedThe person editing finished dragging or stepping the side panel's edge; width is the requested width in px, null after a reset. The host may store it and hand it back through panelWidth.
preview-draft-changedeventCustomEvent<{ values: Answers }> // bubbles, composedWhat is currently typed on a page that has not been submitted yet, per keystroke. Mainly internal: the editor uses it to show the same half-filled page in both previews. Only raised on steps of type page.
preview-node-changedeventCustomEvent<{ nodeId: string; answers: Answers }> // bubbles, composedThe visitor moved to a new step, forwards or back; detail holds the step's id and all answers so far. A guide has no submit of its own, so this is where the host sees that a result was reached. Also raised in the editor's previews.
save-requesteventCustomEvent<null> // no detail; bubbles, composed, cancelableThe person editing asked to save (Ctrl+S or File → Save). A host that saves calls preventDefault() and reads the guide with getData(). If nobody does, the editor shows a notice that the page handles saving on its own.
theme-changeeventCustomEvent<{ theme: "light" | "dark" }> // bubbles, composedAnnounces that the theme was switched, so the host can remember the choice. The library stores no theme itself. Listen on document.
version-activate-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants this version to be the one visitors see (the row's button, on rows that are not current, missing or a draft). The list changes nothing; the host makes it live and then sends a new versions array.
version-delete-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants to remove the version. The list does not confirm, because only the host knows whether it can be undone; ask yourself if needed. The button is on every row but raises nothing on the current version while it still exists.
version-discard-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants to throw away unsaved changes. Shown only on a row with draft: true. The host drops the draft and gives the version's content back to the editor.
version-duplicate-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants a copy of the version. It is the row's main button on the current version and a menu item elsewhere. The host makes the copy and sends a new list.
version-note-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants to say what this version is. The event carries no text; the host asks itself, for example with <prompt-dialog>.ask(), stores the note and sends a new list.
version-open-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants to look at this version: a click on the row or its name. Opening moves and changes nothing; the host loads the version into the editor. Not governed by actions, and not raised for a missing version.
version-rename-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants to rename the version. The event carries no new name; the host asks for it itself, stores it and sends a new list.
version-reorder-intenteventCustomEvent<{ order: string[] }> // version ids in the new order; bubbles, composedThe person editing dragged a row (the grip, or Alt+arrow) into an order of their own. The element switches itself to order custom; the host stores the order if it wants and sends the list back in that order.
version-restore-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants to carry on from an older version. The button appears only when the host explicitly lists "restore" in actions, and never on the current or a missing version. The host puts that version's content into the editor as new work.
version-save-intenteventCustomEvent<{ version: GuideVersion }> // bubbles, composedThe person editing wants to save unsaved work into this version. It is the row's main button, only on a row with draft: true. The host writes the editor's content (getData()) into the version and sends a new list.
What the package exports — the viewer
The functions and types in flowweaver-viewer (and so in every bundle): setup, languages, code lists, maps, palettes and the answers.
AnswerFieldstypetype AnswerFields = Record<string, string>The parts of an answer that has parts, e.g. the label the visitor saw and the code behind it.
AnswerValuetypetype AnswerValue = string | AnswerFields | string[] | AnswerRecord[]One answer: a value, a value with parts, several values, or several records with parts. Read it with answerText, answerList or answerField rather than String().
Answerstypetype Answers = Record<string, AnswerValue>Every answer in a guide, keyed by variable name.
answerTextfunctionanswerText(value: AnswerValue | undefined): stringTurns an answer into one line of text for a result, a letter or a receipt. Several values are joined with ", ", an answer with parts shows its label, and a missing answer gives "".
answerListfunctionanswerList(value: AnswerValue | undefined): string[]Turns an answer into a list of comparable strings, for when the host checks whether the answer contains something. Objects are read by their label, empty entries are dropped, and an older newline-separated string is split.
answerFieldfunctionanswerField(value: AnswerValue | undefined, field: string): string[]Picks one part out of each object in an answer, e.g. the code ("value") of every chosen country. Returns an empty list for an answer without parts; objects lacking the part are skipped.
isAnswerEmptyfunctionisAnswerEmpty(value: AnswerValue | undefined): booleanTrue when the answer is missing, an empty or blank string, an empty list, or an object without a label.
type * from ../viewer/types/graphtypeexport type * from "../viewer/types/graph" // GraphData, FlowNodeData, Connection, GuideSettings, GuideMeta, …The types of the guide's file format: GraphData (start node, nodes, connections, settings, meta) and everything it is made of. This is the shape the host stores and sets as graph on the element.
SubmissionSchemaPropertytypeinterface SubmissionSchemaProperty { type: "string" | "number" | "boolean" | "object" | "array"; label?; format?; pattern?; enum?; options?; kind?; properties?; items?; minItems?; maxItems?; optional? }One field in the submission schema: JSON Schema keywords where they exist, plus our own (label, options, kind, optional).
SubmissionSchematypeinterface SubmissionSchema { $guide?: LocalizedTextMap; $serviceId?: string; exportedAt?: string; properties: Record<string, SubmissionSchemaProperty> }The shape of what a guide submits, stamped by the editor into meta.submissionSchema on export, so a receiver can build against it before the first submission arrives.
GuidePreviewclassclass GuidePreview extends HTMLElement // <guide-preview>The class behind <guide-preview>, the element that runs a finished guide for the visitor. The element is registered when the viewer bundle loads; the export is for typing or instanceof.
LanguagePacktypeinterface LanguagePack { viewer?: Record<string, string>; editor?: Record<string, string> }The texts for one language passed to init, split into what the visitor reads (viewer) and what the person editing reads (editor).
LanguageSetuptypeinterface LanguageSetup { offer?: readonly string[]; source?: string; content?: string; tool?: string }The language part of init: which languages a guide may be offered in, which a new guide is written in, which content language is shown, and the tool's own language.
HostSetuptypeinterface HostSetup { graph?; mode?; languages?; packs?; templates?; versions?; theme?; onSave?; onTemplatesChange? }Everything the host passes to init: the guide, the permission mode, languages, language packs, node templates, versions, theme and the save callbacks.
Wiringtypeinterface Wiring { coverage: Record<string, LocaleCoverage>; disconnect: () => void }What init returns: the coverage of each registered language pack and a function that stops listening.
initfunctioninit(element: HTMLElement, setup?: HostSetup): WiringWires an element into the host in one call: registers language packs first, declares the languages, sets mode, editor-locale, active-locale, theme, versions and templates, listens for changes to call onSave/onTemplatesChange, and sets the guide last. Returns the packs' coverage and disconnect(). Built for <guide-editor>; on <guide-preview> the languages, theme and guide apply, while onSave never fires because the viewer does not emit graph-changed.
SetupSeveritytypetype SetupSeverity = "error" | "warning"How serious a checkSetup finding is.
SetupFindingtypeinterface SetupFinding { id: string; severity: SetupSeverity; what: string; consequence: string }One checkSetup finding: a stable id to filter on, severity, what was observed and what it leads to (in English).
SetupReporttypeinterface SetupReport { ok: boolean; findings: SetupFinding[] }The answer from checkSetup; ok is true only when nothing was found.
CheckableElementtypeinterface CheckableElement extends HTMLElement { graph?: GraphData }The element checkSetup inspects: <guide-preview> or <guide-editor>.
checkSetupfunctioncheckSetup(element: CheckableElement): SetupReportInspects a mounted element's setup and reports in English: tokens that do not resolve, a <guide-editor> without mode, and languages the guide offers that lack viewer texts or fall outside declareLocales. It says nothing about storage or listeners, so ok: true means only that none of these was found. Call it after mounting, once the guide is set.
LocaleStringstypetype LocaleStrings = Readonly<Record<string, string>>A language pack: text keys mapped to texts.
registerLocalefunctionregisterLocale(locale: string, strings: LocaleStrings): LocaleCoverageRegisters the viewer's texts for one language, e.g. Arabic buttons or the host's own Swedish wording. Calling again for the same language replaces the pack. Returns how much the pack covers. Call it at startup, before the elements render.
unregisterLocalefunctionunregisterLocale(locale: string): voidRemoves a registered language pack.
localeStringsfunctionlocaleStrings(locale: string): Record<string, string>Returns every text for one language as a flat template to translate from: the package's own, overridden by the host's registered texts.
builtInLocalesfunctionbuiltInLocales(): string[]The language codes the package ships its own texts for, sorted.
availableLocalesfunctionavailableLocales(): string[]The language codes the viewer can be shown in: the package's own plus those the host registered, sorted.
AudienceCoveragetypeinterface AudienceCoverage { filled: number; total: number; missing: string[] }How many keys a language covers for one audience, and which are missing.
LocaleCoveragetypeinterface LocaleCoverage { locale: string; viewer: AudienceCoverage; editor: AudienceCoverage }A language's coverage, separately for the viewer's and the editor's texts.
coverageOffunctioncoverageOf(locale: string): LocaleCoverageReports how much of the viewer's and the editor's texts a language covers, counting registered and built-in texts. With only the viewer loaded the editor side reads 0 of 0.
LocaleDeclarationtypeinterface LocaleDeclaration { default?: string }The option to declareLocales: which language is the default.
declareLocalesfunctiondeclareLocales(locales: readonly string[], options?: LocaleDeclaration): voidDeclares which languages a guide may be offered in and which is the default (otherwise the first). Calling again replaces the declaration. Nothing is written into the guide.
clearDeclaredLocalesfunctionclearDeclaredLocales(): voidClears the language declaration, so declaredLocales returns null again.
declaredLocalesfunctiondeclaredLocales(): readonly string[] | nullThe declared languages, or null when the host has declared none.
declaredDefaultLocalefunctiondeclaredDefaultLocale(): string | nullThe declared default language, or null.
CodeListItemtypeinterface CodeListItem { value: string; label: LocalizedTextMap; synonyms?: LocalizedTextMap[]; sourceLabel?: string; exclusive?: boolean }One entry in a code list: the stored code, its label per language, synonyms to search by, and whether it excludes every other entry.
CodeStandardtypetype CodeStandard = "iso-3166-1-alpha-2" | "iso-3166-1-alpha-3" | "iso-3166-1-numeric" | "custom"Which standard a code list's codes follow.
CodeListtypeinterface CodeList { id; standard: CodeStandard; label; version; published; source; sourceUrl?; labelSources?; items: CodeListItem[] }A code list the host registers, with its standard, source and version.
registerCodeListfunctionregisterCodeList(list: CodeList): voidMakes a code list (e.g. countries or municipalities) available to fields and rules. The lists ship as JSON beside the bundles and none is built in. The same id replaces the earlier list; a malformed list is refused with a TypeError saying what is wrong.
codeListFaultfunctioncodeListFault(list: unknown): string | nullChecks a code list without registering it: returns a description of the fault (in Swedish), or null when the list is valid.
unregisterCodeListfunctionunregisterCodeList(id: string): voidRemoves a registered code list.
getCodeListfunctiongetCodeList(id: string): CodeList | nullReturns a registered code list, or null.
getCodeListsfunctiongetCodeLists(): CodeList[]Every registered code list.
MapKindtypetype MapKind = "point" | "points" | "area"What kind of geometry a map question asks for; per the code, points and area are reserved for v2.
MapPickResulttypeinterface MapPickResult { geometry: { type: string; coordinates: unknown }; label: string; zoom?: number }The answer from the host's map: GeoJSON in WGS84 ([longitude, latitude]), a readable label and optionally the zoom level.
MapProvidertypeinterface MapProvider { kinds: readonly MapKind[]; pick(options): Promise<MapPickResult | null>; snapshot?(geometry, options?): Promise<string> }The host's map: which kinds it supports, pick which lets the visitor point out geometry (null on cancel), and an optional still image.
registerMapProviderfunctionregisterMapProvider(next: MapProvider): voidRegisters the host's map provider, which map questions then call; FlowWeaver draws no map itself. There is one provider at a time and a new call replaces it.
unregisterMapProviderfunctionunregisterMapProvider(): voidRemoves the registered map provider.
registerLookupOriginsfunctionregisterLookupOrigins(origins: readonly string[]): voidSets which origins (e.g.
https://api.example.se) a lookup field may send the visitor's text to, besides the page's own. With no list, lookups go to the page's own origin only; an address outside the list is never called and the field says the lookup could not be reached. A new call replaces the list, and an entry that is not a bare origin is refused with an error.unregisterLookupOriginsfunctionunregisterLookupOrigins(): voidRemoves the list, so lookups again go to the page's own origin only.
Palettetypeinterface Palette { id: string; label: string; light: Record<string, string>; dark: Record<string, string> }A finished colour scale: token values for light and dark mode.
palettesconstantconst palettes: { hav: Palette; skog: Palette; skiffer: Palette; tegel: Palette }The four built-in colour scales (hav, skog, skiffer, tegel), to pass to applyPalette.
applyPalettefunctionapplyPalette(palette: Palette | null): voidRecolours FlowWeaver's elements in one step by adding a <style> to <head> with the scale's whole primary family and node colours, for light and dark mode. null removes it and restores the default colour. It also sets the --fw-* tokens on :root.
What the editor adds
What flowweaver-editor exports on top of the viewer.
GuideEditorclassclass GuideEditor extends HTMLElement // <guide-editor> attributes: feature-level, modules, mode, mode-notice, editor-locale, active-locale, theme, get-started-href, panel-width, panel-open, palette-open, palette-search graph: GraphData; getData(): GraphData; replaceGraph(value, reason); undo(); redo(); setTheme(theme: ThemeChoice | null) events: graph-changed, save-request, locale-change, panel-width-changed, panel-open-changed, palette-open-changedThe
<guide-editor>element, registered when the editor bundle loads. The host setsgraph, reads the guide back withgetData()(stamped with its format version) and listens forgraph-changedandsave-requestto store it itself; the editor stores nothing.GuideVersiontypeinterface GuideVersion { id: string; number?: number; label?: string; note?: string; savedAt?: number; by?: { subject?: string; name?: string; me?: boolean }; reason?: "conflict"; current?: boolean; open?: boolean; draft?: boolean; missing?: boolean; }One row in
<guide-versions>, as the host describes it from its own storage.number,byandreasonare the host's to set and are never computed by the list;currentis the version visitors see,openthe one the person editing is working from.GuideVersionsclassclass GuideVersions extends HTMLElement // <guide-versions> attributes: editor-locale, order, theme versions: GuideVersion[]; order: VersionOrder; actions: VersionAction[] | null; theme: ThemeChoice | null events: version-{open|restore|activate|save|discard|duplicate|rename|note|delete}-intent { detail: { version } }, version-reorder-intent { detail: { order: string[] } }The
<guide-versions>element: the list of a guide's versions. It decides nothing — it renders rows and raisesversion-*-intentevents; reading, writing, naming and removing stay with the host. Withactionsthe host says which actions its storage supports, so the menu shows only those.LinkPickContexttypeinterface LinkPickContext { locale: string; // language of the text being edited selectedText: string; // "" when nothing was selected }What the host's link picker receives when the person editing presses the link button: the language of the text being edited and the selected text.
LinkPickResulttypeinterface LinkPickResult { url: string; // the address the viewer renders; relative is fine label?: string; // link text when nothing was selected ref?: string; // the host's own reference for the page }The host's link picker's answer. With
refthe link is written[text](address "ref"); the viewer renders only the address, and the editor can later askresolvewhether the page has moved.LinkPickertypeinterface LinkPicker { pick(context: LinkPickContext): Promise<LinkPickResult | null>; resolve?(ref: string): Promise<{ url: string } | null>; }The host's link picker.
pickopens the host's own dialog and resolves tonullwhen the person editing cancels. The optionalresolveis asked about every reference when a guide is opened in the editor: a changed address is rewritten,null(page gone) becomes a health warning, and a thrown error counts as no answer.PromptDialogclassclass PromptDialog extends HTMLElement // <prompt-dialog> editorLocale: string (setter) ask(options: PromptOptions): Promise<string | null> choose(options: { title: string; message?: string; choices: Array<{ id: string; label: string }>; cancelLabel?: string }): Promise<string | null>The
<prompt-dialog>element, a modal dialog that asks for a word — for example what a copy should be called when<guide-versions>raisesversion-rename-intent.askresolves to the text, ornullon cancel or an empty answer;chooseoffers fixed choices instead of a field.PromptOptionstypeinterface PromptOptions { title: string; label: string; message?: string; value?: string; placeholder?: string; confirmLabel: string; cancelLabel?: string; }What the host passes to
PromptDialog.ask: title, a label above the field, an optional explanatory message, an initial value and button texts. WithoutcancelLabelthe editor's own Cancel text is used.ThemeChoicetypetype ThemeChoice = "light" | "dark";Light or dark theme for
themeon<guide-editor>and<guide-versions>;nullmeans the operating system's setting applies. The theme is set on the element, not on the host's page, and the library does not store the choice — remembering it is the host's job.VersionOrdertypetype VersionOrder = "saved" | "label" | "custom";How
<guide-versions>orders its rows: by date, by name, or in the order the host handed them (custom). Date and name are only a view; when the person editing drags a row, the list switches tocustomand asks the host for the new order withversion-reorder-intent.registerLinkPickerfunctionfunction registerLinkPicker(value: LinkPicker): voidRegisters the host's link picker, so the editor's link button opens the host's dialog instead of writing
[text](https://). A new registration replaces the previous one and forgets earlierresolveanswers.unregisterLinkPickerfunctionfunction unregisterLinkPicker(): voidRemoves the host's link picker; the link button then writes
[text](https://)as it does without one.
FlowWeaver PRO — submitting PRO
What the PRO bundles add: the receiver contract the host registers, and the schema of what a guide sends in.
registerSubmissionReceiverfunctionfunction registerSubmissionReceiver(value: SubmissionReceiver): voidRegisters the host's submission receiver. The guide hands the answers to
submitand shows the reference that comes back; with no receiver registered, sending fails and the visitor is told so. A new registration replaces the previous one and resets the retry guarantee read so far (retryWindowHours).unregisterSubmissionReceiverfunctionfunction unregisterSubmissionReceiver(): voidRemoves the host's receiver and the retry guarantee read from it. After that, sending fails and the recipient list is empty.
listSubmissionRecipientsfunctionfunction listSubmissionRecipients(): Promise<SubmissionRecipient[]>Asks the registered receiver for its catalog and returns the rows trimmed to exactly
{id, label}— any extra fields the host included are dropped. An empty list without a receiver. It also readsretryWindowHourswhen the catalog answered in that form.VISITOR_RECIPIENT_IDconstantconst VISITOR_RECIPIENT_ID = "@visitor";The recipient picker's one address exception: the visitor's own address from an answer — the visitor's data to the visitor.
@cannot start a sensible catalog id, so the value does not collide with the host's ids.SubmissionCatalogtypeinterface SubmissionCatalog { recipients: readonly SubmissionRecipient[]; retryWindowHours?: number; }The catalog reply's other form: the rows plus the host's stated guarantee of how many hours a
submissionIdis recognised. It is the receiver'sGET /recipientsresponse, so the host can pass it on as-is. Only a finite number above zero counts as a guarantee; omitted means none.SubmissionPayloadtypeinterface SubmissionPayload { recipientId: string; recipientIds: string[]; copyRecipientIds: string[]; submissionId?: string; serviceId?: string; serviceVersion?: string; caseType?: string; locale: string; answers: Answers; records: SubmissionRecord[]; row: SubmissionRowCell[]; emailCopy: boolean; visitorEmail?: string; protection: { honeypot: string; elapsedMs: number }; }What the guide hands to
submit: recipient ids from the host's catalog (never addresses — the host resolves those), the answers by variable name, a readable summary, asubmissionIdthat stays the same across retries, and the raw bot-protection data for the receiver to judge.recipientIdis the first recipient, kept for older receivers; preferrecipientIds.SubmissionReceipttypeinterface SubmissionReceipt { reference: string; }What
submitresolves to: the case reference, which the host owns and numbers. The guide shows it to the visitor; a thrown error gives an honest failure message and never an invented number.SubmissionReceivertypeinterface SubmissionReceiver { recipients(): readonly SubmissionRecipient[] | SubmissionCatalog | Promise<readonly SubmissionRecipient[] | SubmissionCatalog>; submit(payload: SubmissionPayload): Promise<SubmissionReceipt>; }What the host registers.
recipientssupplies the visible part of the catalog for the editor's recipient picker, synchronously or asynchronously;submitturns the answers into a case and rejects by throwing. The addresses themselves stay in the host's backend.SubmissionRecipienttypeinterface SubmissionRecipient { id: string; label: string; }One row in the editor's recipient picker, and never more. The guide stores only the id; the address is not in the type and so never reaches the client.
SubmissionRecordtypeinterface SubmissionRecord { question: string; answer: AnswerValue; variable: string | null; }One line of the submission's readable summary (
records): the question's heading as the visitor read it, the answer and the variable name — the same lines the review step shows.submissionSchemafunctionfunction submissionSchema(graph: GraphData): SubmissionSchemaDerives the submission schema from a guide: the keys are the editor's variable names, the type follows the field type and the label is the question per language. For a host that already has the graph and wants to build or validate its receiver against it; the same schema is stamped into
meta.submissionSchemaon export.pro-bundlesaboutflowweaver-pro-viewer ← src/entries/pro-viewer.ts (global FlowWeaverProViewer) flowweaver-pro-editor ← src/entries/pro-editor.ts (global FlowWeaverProEditor) // each: export * from the open entry, plus the submission exports aboveFlowWeaver PRO is built as two bundles of its own that export everything the open ones do plus the submission exports here. The PRO viewer sends: the submission and e-mail result nodes hand the answers to the host's receiver. The open viewer draws a sending step as a plain result and sends nothing; the PRO editor adds the sending node types, the recipient picker, the health rules and the schema stamped on export. The file format is the same, so one guide works in both.