S

SideButton Dashboard Knowledge Module

Settings — SideButton Dashboard Knowledge Module

The Settings page is the configuration hub for SideButton's AI context system and LLM provider. It has two top-level tabs: **AI Context** (managing persona, roles, targets, integrations, and inline c…

Available free v0.4.0 Browser
$ sidebutton install sidebutton.local
Download ZIP
/settings 96% confidence Verified 2026-02-21

What This Is

The Settings page is the configuration hub for SideButton's AI context system and LLM provider. It has two top-level tabs: AI Context (managing persona, roles, targets, integrations, and inline contexts that get injected into every LLM call) and LLM Provider (configuring which AI model to use). The AI Context tab has five sub-tabs, making this the most complex page in the dashboard.

URL Patterns

ViewURLNotes
Settings/settingsSingle URL — all tab/sub-tab state is component-local, not reflected in URL

No URL-based routing for individual tabs. Tab state resets on navigation away and back.

Page Structure

+--[Header: Back button + "Settings" heading]-------------+
|  [.error-banner] shown when error (red, below header)   |
|  [.success-banner] shown on save success (green)        |
|                                                          |
|  [Top Tabs: AI Context | LLM Provider]                  |
|                                                          |
|  === AI Context tab selected ===                         |
|  [Sub-tabs: Persona | Roles (N) | Targets (N) |         |
|             Integrations (N) | Inline (N)]               |
|                                                          |
|  [Tab content area — varies by sub-tab]                  |
|    - Persona: textarea + Save button                     |
|    - Roles: list with toggle/edit/delete per item        |
|    - Targets: list with toggle/edit/delete per item      |
|    - Integrations: provider cards with connector options  |
|    - Inline: LLM Contexts section + Env Variables section|
|                                                          |
|  === LLM Provider tab selected ===                       |
|  [.llm-form-row: two-column grid]                        |
|    Left col: Provider select, Base URL, API Key, Model   |
|              + Test Connection + Save buttons            |
|    Right col: Status panel (Configured/Provider/Model)  |
|               + test result message                      |
+----------------------------------------------------------+

Key Elements

Header

ElementSelectorNotes
Back button.back-btnSVG arrow + "Back" text. Calls navigateBack()
Settings headingh1 containing "Settings"Page title

Banners (below header, above content)

ElementSelectorNotes
Error banner.error-bannerRed background (#fef2f2), red text (#b91c1c). Shown when API call fails
Success banner.success-bannerGreen background (#f0fdf4), green text (#15803d). Auto-hides after 3s via setTimeout

Top-Level Tabs

ElementSelectorNotes
AI Context tabbutton "AI Context"Default active. Shows context sub-tabs
LLM Provider tabbutton "LLM Provider"Shows LLM configuration form

AI Context Sub-Tabs

ElementSelectorNotes
Persona tabbutton "Persona"Textarea editor for persona markdown
Roles tabbutton "Roles"Shows count badge (e.g., "73"). All roles including system roles
Targets tabbutton "Targets"Shows count badge for user-editable targets only (provider-managed targets excluded)
Integrations tabbutton "Integrations"Count badge = number of connected providers
Inline tabbutton "Inline"Count badge = total user_contexts count (LLM + env vars combined)

Persona Sub-Tab

ElementSelectorNotes
Persona textareatextbox "Describe who you are..."Monospace, min-height 300px. Pre-filled with persona.md content
Character countright-aligned text near textareaShows "{N} chars"
Save Persona buttonbutton "Save Persona"Disabled when no changes (dirty tracking inside PersonaEditor component)

Roles Sub-Tab

ElementSelectorNotes
Add Role button.btn-add containing "+ Add Role"Opens RoleTargetModal in role mode
Role item (normal).context-item (without .deleting)Per-role card row
Role item (inactive).context-item.inactiveopacity: 0.5 when role.enabled === false
Role item (deleting).context-item.deletingReplaces card with inline confirm UI; red bg (#fef2f2)
Role name.item-name spanBold, font-size 0.9rem
System badge.tag.systemGray badge with text "system". Shown when filename.startsWith('_')
Match pattern tags.tag.matchOne per match pattern (e.g., "@engineering"). Gray-blue background
Item actions.item-actionsContains Edit + × buttons. opacity: 0 by default, opacity: 1 on .context-item:hover
Edit button.btn-editText "Edit". Opens RoleTargetModal pre-filled
Delete (×) button.btn-deleteShows "×" character. Not rendered for system roles (filename starts with _)
Toggle switchbutton.toggle-switchCustom button component, NOT a native checkbox/toggle
Toggle on statebutton.toggle-switch.onGreen background (#22c55e). title="Enabled"
Toggle off statebutton.toggle-switch (no .on)Gray background (#d1d5db). title="Disabled"
Toggle knob.toggle-knob span inside toggleWhite circle that slides; transform: translateX(16px) when on

Inline Delete Confirm UI (Roles/Targets)

When .btn-delete (×) is clicked, deletingFilename is set and the card transforms to:

ElementSelectorNotes
Confirm container.context-item.deletingRed-tinted card replacing normal item
Confirm messagep inside .context-item.deletingText: Delete "{name}"? in red (#b91c1c)
Cancel button.btn-cancel-smWhite button, text "Cancel". Clears deletingFilename
Confirm delete button.btn-delete-confirmRed (#ef4444) button, text "Delete". Executes API delete

Targets Sub-Tab

ElementSelectorNotes
Add Target button.btn-add containing "+ Add Target"Opens RoleTargetModal in target mode
Target itemsSame .context-item structure as rolesIdentical layout and delete/toggle pattern
Provider-managed targetsNot shown hereTargets with a provider field are filtered out of this tab

The delete inline confirm pattern is identical to Roles: .context-item.deleting with .btn-cancel-sm + .btn-delete-confirm.

Integrations Sub-Tab

ElementSelectorNotes
Provider card.provider-cardOne per provider; gray background, rounded border
Provider name.item-name inside .provider-infoBold text (e.g., "Jira", "Slack")
Provider type badge.tag.match inside .provider-infoText format: "{type} provider". Multi-type providers (e.g., GitHub) render multiple .tag.match badges — one per type string in the array
Status: connected.tag.provider-connectedGreen (#dcfce7 bg, #15803d text). Text: "Connected"
Status: connector set but not ready.tag.provider-missingYellow (#fef3c7 bg, #92400e text). Text: "Not available"
Status: no connector.tag.provider-not-configuredGray (#f3f4f6 bg, #6b7280 text). Text: "Not connected"
Connector row.connector-rowOne per connector option
Active connector row.connector-row.activeBlue-tinted border (#93c5fd) + background (#eff6ff)
Connector radio inputinput[type="radio"]Wrapped in .connector-radio label. onchangehandleConnectorChange() — auto-saves immediately
Connector name.connector-name spane.g., "REST API", "CLI (acli)", "Browser"
Active badge.tag.connector-activeBlue (#dbeafe bg, #1d4ed8 text). Text: "active"
Feature level.connector-level spanText: "{level} features" (e.g., "Full features", "Basic features")
Ready status.req-met spanGreen text. Text: "Ready"
Error/missing status.req-missing spanOrange-brown text, monospace. Text: error message (e.g., "Missing: JIRA_URL")
Setup toggle button.btn-setup-toggleText toggles between "Setup" and "Hide setup"
Setup instructions panel.setup-instructionsShown when expandedSetup === setupKey. Contains <p> with conn.setupInstructions text
Env var requirements.env-requirements div inside .setup-instructionsFlex row of code.env-var-check elements
Env var code badgecode.env-var-checkMonospace, cyan (#e0f2fe bg, #0369a1 text). One per required env var

Inline Sub-Tab

The inline tab has two sub-sections rendered inside .context-sections:

ElementSelectorNotes
Add button.btn-add containing "+ Add"Opens ContextModal
Sections wrapper.context-sectionsFlex column with 24px gap
LLM Contexts section.context-section (first)<h3>LLM Contexts</h3> heading
Env Variables section.context-section (second)<h3>Environment Variables</h3> heading
Env Variables empty state.context-section (second) when emptyText: "No environment variables configured" + "Add variables accessible via {{env.name}}"

LLM Context Items

ElementSelectorNotes
Context item.context-itemNormal card when not deleting
Context item deleting.context-item.deletingInline confirm: "Delete this context?" + Cancel + Delete
Industry tag.tag.industryBlue (#dbeafe bg, #1d4ed8 text). Shown when ctx.industry set
Domain tag.tag.domainPurple (#f3e8ff bg, #7c3aed text). Shown when ctx.domain set
Global tag.tag.globalGray (#e5e7eb bg, #6b7280 text). Shown when neither industry nor domain set
Context text.context-text p2-line clamp preview of context body
Item actions.item-actionsopacity: 0 default → opacity: 1 on .context-item:hover
Edit button.btn-editText "Edit". Opens ContextModal pre-filled
Delete button.btn-deleteShows "×". Calls confirmDelete(ctx.id) → sets deletingId

Environment Variable Items

ElementSelectorNotes
Env item container.context-item.env-itemHorizontal flex row (align-items: center, gap: 12px)
Env item deleting.context-item.env-item.deletingColumn layout with "Delete this variable?" + Cancel + Delete
Variable namecode.env-nameMonospace, cyan (#e0f2fe bg, #0369a1 text). e.g., "github_base_path"
Variable value.env-value spanMonospace, truncated with ellipsis. e.g., "/Users/me/Documents/GitHub"
Item actions.item-actionsopacity: 0 default → opacity: 1 on .env-item:hover

Inline Delete Confirm UI (Inline Tab)

Same visual pattern as Roles/Targets but uses deletingId state (separate from deletingFilename):

ElementSelectorNotes
LLM context confirm textp in .context-item.deletingText: "Delete this context?"
Env var confirm textp in .context-item.env-item.deletingText: "Delete this variable?"
Cancel.btn-cancel-smClears deletingId
Delete.btn-delete-confirmExecutes delete then calls handleSave() to persist

LLM Provider Tab

ElementSelectorNotes
Layout wrapper.llm-form-rowCSS grid, grid-template-columns: 1fr 1fr. Collapses to single column below 700px
Provider selectselect#providerNative <select>. Options: OpenAI, Anthropic, Ollama. onchange calls handleProviderChange()
Base URL inputinput#base-urlText input. Auto-filled by handleProviderChange()
API Key labellabel[for="api-key"]Contains "API Key" text + .toggle-visibility button
Show/Hide toggle.toggle-visibility buttonInside the label. Text: "Show" or "Hide". Toggles type="password"type="text"
API Key inputinput#api-keyPassword field by default. Placeholder: "sk-..."
API Key action button... button inside API Key inputSmall "..." button on right side of field. Purpose: additional input action
Base URL action button... button inside Base URL inputSmall "..." button on right side of field. Same pattern as API Key
Model inputinput#modelText input. Auto-filled by handleProviderChange(). Placeholder: "gpt-4o"
Test Connection button.btn-secondaryText "Test Connection" / "Testing..." when active. Disabled during test
Save button.btn-primaryText "Save" / "Saving..." when active. Disabled during save
Status panel.status-sectionRight column. Shows LLM Configured, Provider, Model values
LLM Configured value.status-value.yes (green) / .status-value.no (orange)"Yes" when apiKey truthy, "No" when empty
Test result.test-result.success or .test-result.errorAppears after Test Connection. Auto-hides after 4s

Modals

ModalTriggerFields
RoleTargetModal (role mode)"+ Add Role" or Edit on a roleName (required), Match patterns (comma-separated @tags), Body (markdown textarea)
RoleTargetModal (target mode)"+ Add Target" or Edit on a targetName (required), Match patterns (comma-separated domains/globs), Body (markdown textarea)
ContextModal (LLM context)"+ Add" in Inline tab, Edit on LLM contextType selector, Industry dropdown, Domain input, Context textarea
ContextModal (env variable)"+ Add" in Inline tab, Edit on env varType selector, Variable name, Variable value

Data Model

Persona

FieldTypeNotes
bodystringMarkdown content of persona file

Role

FieldTypeNotes
filenamestringFile identifier (e.g., "ai-tools.md", "_system.md")
namestringDisplay name (e.g., "AI Tools")
matchstring[]Pattern tags (e.g., ["@engineering"])
bodystringMarkdown instructions
enabledbooleanWhether role is active. Treated as true when undefined (only === false is disabled)

Target

FieldTypeNotes
filenamestringFile identifier
namestringDisplay name (e.g., "GitHub")
matchstring[]Domain patterns (e.g., ["github.com", "github_*"])
bodystringMarkdown instructions
enabledbooleanWhether target is active
providerstringOptional. If set, target is provider-managed (filtered from Targets tab, shown in Integrations)

LLM Config (stored as settings.llm)

FieldTypeNotes
providerstring"openai", "anthropic", or "ollama"
base_urlstringAPI endpoint URL
api_keystringAPI key (masked as password in UI by default)
modelstringModel identifier string

LLM Provider Defaults (auto-filled on provider change)

Provider IDDisplay NameDefault Base URLDefault Model
openaiOpenAIhttps://api.openai.com/v1gpt-4o
anthropicAnthropichttps://api.anthropic.comclaude-3-5-sonnet-latest
ollamaOllamahttp://localhost:11434/v1llama3.2

Provider Status

FieldTypeNotes
idstringProvider identifier
namestringDisplay name
typestring or string[]Provider type(s); rendered as "{type} provider"
connectedbooleanTrue if active connector is ready
activeConnectorstring or nullCurrently selected connector ID
connectorsarrayAvailable connector definitions (id, name, featureLevel, setupInstructions, requiredEnvVars)
connectorStatusesarrayStatus per connector (active, available, error)

Known Providers (observed live, 2026-03-25)

ProviderType(s)Connectors
Jiraissues providerREST API, CLI (acli), Browser
Slackchat providerBot Token API, Browser
GitHubgit, issues providerCLI (gh), Browser
Bitbucketgit providerREST API 2.0

Note: GitHub shows two type badges ("git" and "issues provider") because its type is an array. Bitbucket is a single-type provider.

User Context (Inline tab)

FieldTypeNotes
idstringUnique identifier
type"llm" or "env"Determines which sub-section it renders in
industrystring(LLM type) Optional. Drives .tag.industry display
domainstring(LLM type) Optional. Drives .tag.domain display
contextstring(LLM type) The LLM instruction text
namestring(env type) Variable name
valuestring(env type) Variable value

States

StateTriggerVisual Indicator
Default (AI Context > Persona)Page loadPersona textarea with content, Save Persona disabled
Persona dirtyEdit textareaSave Persona button enabled
Role/target inactiveenabled === false.context-item.inactive — opacity: 0.5 (0.7 on hover)
Inline delete pendingClick × on role/target.context-item.deleting replaces card with red confirm UI
Inline delete pending (inline tab)Click × on context/env varSame .context-item.deleting pattern, uses deletingId
Item actions visibleHover over .context-item.item-actions opacity transitions from 0 → 1
LLM Provider tabClick LLM Provider tabTwo-column grid form + status panel
Connector activeRadio selected for connector.connector-row.active — blue-tinted
Setup expandedClick "Setup" button.setup-instructions panel visible below connector row
SavingClick Save.btn-primary shows "Saving...", disabled
TestingClick Test Connection.btn-secondary shows "Testing...", disabled
Test resultAfter test completes.test-result.success or .test-result.error — auto-hides after 4s
Save successAPI save succeeds.success-banner appears above content — auto-hides after 3s
ErrorAny API failure.error-banner appears above content
LoadingInitial page load.loading div with "Loading settings..." text

Common Tasks

  1. Edit persona: Navigate to Settings → Persona sub-tab (default) → edit textarea → click "Save Persona"
  2. Add role: Roles sub-tab → click "+ Add Role" → fill Name, Match patterns, Body in modal → submit
  3. Toggle role on/off: Roles sub-tab → hover role item → click button.toggle-switch — auto-saves via API
  4. Edit role: Roles sub-tab → hover role item → click Edit (.btn-edit) → modify in modal → submit
  5. Delete role: Roles sub-tab → hover role item → click × (.btn-delete) → click "Delete" in inline confirm — system roles (filename starts with _) have no × button
  6. Add target: Targets sub-tab → click "+ Add Target" → fill form → submit
  7. Toggle target: Same as role toggle pattern
  8. Change integration connector: Integrations sub-tab → click radio input (input[type="radio"]) on desired connector → auto-saves immediately (no separate save button)
  9. View setup instructions: Integrations sub-tab → click "Setup" on connector row → reads .setup-instructions panel
  10. Add inline LLM context: Inline sub-tab → click "+ Add" → ContextModal opens → choose type/industry/domain/context → submit
  11. Add env variable: Inline sub-tab → click "+ Add" → ContextModal opens → select env type → fill name + value → submit
  12. Delete inline context/env var: Inline sub-tab → hover item → click × → inline confirm appears → click "Delete"
  13. Configure LLM provider: LLM Provider tab → select provider from select#provider (auto-fills URL + model) → enter API key → click "Save"
  14. Test LLM connection: LLM Provider tab → fill config → click "Test Connection" → result appears for 4s

Tips

  • Tab badges show counts: Roles = all roles count; Targets = user-editable targets only; Integrations = connected provider count; Inline = total user_contexts
  • Hover to see actions: Edit and × buttons are invisible (opacity: 0) until hovering the card — snapshot alone will not capture them
  • Persona is global: Injected into every LLM call regardless of context
  • Roles vs Targets: Roles define behavior for @category tags (situations/topics). Targets define behavior for domain URL patterns (specific sites)
  • System roles are protected: Roles with filenames starting with _ show a .tag.system "system" badge and have no × delete button rendered
  • Provider-managed targets hidden from Targets tab: Targets with a provider field are filtered out — managed by integrations
  • LLM Provider auto-fills on provider change: Selecting a different provider in the dropdown immediately sets Base URL and Model to defaults
  • Connector radio is a toggle: Clicking an already-active connector's radio calls handleConnectorChange(prov.id, null) — deactivating it
  • Scroll position preserved: Toggle and edit operations on roles/targets use preserveScroll() to maintain scroll position
  • Test Connection is format-only: Does NOT make a real API call. Only checks apiKey non-empty and OpenAI keys start with "sk-"

Gotchas

  • No URL-based tab state: All tab/sub-tab selection is component-local. Navigating away and back always resets to AI Context > Persona
  • Delete uses INLINE confirm, not native confirm(): Clicking × does NOT open a browser confirm() dialog. It sets deletingFilename (roles/targets) or deletingId (inline tab), transforming the card into .context-item.deleting with .btn-cancel-sm and .btn-delete-confirm buttons in-place
  • Item actions hidden by default: .item-actions has opacity: 0 at rest. Must hover .context-item to reveal Edit and × buttons — accessibility snapshot may not show them
  • Toggle switch is a custom button: button.toggle-switch, NOT a native <input type="checkbox"> or <input type="toggle">. Use .click() or ref-based click, not select_option()
  • Provider select is native: select#provider IS a native <select> element — use select_option() or keyboard interaction, not click-based dropdown
  • Success banner auto-hides in 3s: Must screenshot immediately after save action to capture it
  • Test result auto-hides in 4s: Same — screenshot immediately after Test Connection
  • inactive items at half opacity: .context-item.inactive (disabled roles/targets) renders at opacity: 0.5 — may be harder to see in screenshots
  • Inline tab count includes both LLM and env contexts: The badge on the Inline sub-tab is userContexts.length (combined), not separate counts
  • Integrations badge is connected count: Shows only providers where connected === true, not total provider count
  • SPA 404 on direct navigation: /settings returns 404 JSON if loaded directly — must navigate through SPA root first
  • Connector radio deselects when re-clicked: Clicking an active connector's radio sends null as connectorId, disconnecting it