chore: added editor specs
This commit is contained in:
@@ -30,6 +30,7 @@ use "./i18n.allium" as i18n -- Split localization (UI vs content
|
|||||||
use "./layout.allium" as layout -- App shell, activity bar, status bar, panels
|
use "./layout.allium" as layout -- App shell, activity bar, status bar, panels
|
||||||
use "./tabs.allium" as tabs -- Tab system, editor routing, preview/pin
|
use "./tabs.allium" as tabs -- Tab system, editor routing, preview/pin
|
||||||
use "./sidebar_views.allium" as sidebar -- 10 sidebar views, content, behaviour
|
use "./sidebar_views.allium" as sidebar -- 10 sidebar views, content, behaviour
|
||||||
|
use "./editors.allium" as editors -- Editor view layouts, dashboard, chat panel
|
||||||
|
|
||||||
-- Integration
|
-- Integration
|
||||||
use "./git.allium" as git -- Git operations, LFS, reconciliation
|
use "./git.allium" as git -- Git operations, LFS, reconciliation
|
||||||
|
|||||||
415
specs/editors.allium
Normal file
415
specs/editors.allium
Normal file
@@ -0,0 +1,415 @@
|
|||||||
|
-- allium: 1
|
||||||
|
-- bDS Editor Views
|
||||||
|
-- Scope: UI content area (all waves + extensions)
|
||||||
|
-- Distilled from: PostEditor.tsx, MediaEditor.tsx, Editor.tsx (Dashboard),
|
||||||
|
-- SettingsView.tsx, StyleView.tsx, ChatPanel.tsx, TagsView
|
||||||
|
|
||||||
|
-- Describes the layout and behaviour of each editor view rendered in
|
||||||
|
-- the main content area when a tab is active (or dashboard when no tab).
|
||||||
|
-- Tab routing is in tabs.allium. Sidebar navigation is in sidebar_views.allium.
|
||||||
|
|
||||||
|
use "./tabs.allium" as tabs
|
||||||
|
use "./post.allium" as post
|
||||||
|
use "./media.allium" as media
|
||||||
|
use "./i18n.allium" as i18n
|
||||||
|
|
||||||
|
-- ─── Dashboard (no tab active) ───────────────────────────────
|
||||||
|
|
||||||
|
-- Shown as default/welcome view when no entity tab is active.
|
||||||
|
-- Single centered column.
|
||||||
|
|
||||||
|
value Dashboard {
|
||||||
|
title: String
|
||||||
|
subtitle: String
|
||||||
|
stats: DashboardStats
|
||||||
|
timeline: DashboardTimeline
|
||||||
|
tag_cloud: DashboardTagCloud
|
||||||
|
category_cloud: DashboardCategoryCloud
|
||||||
|
recent_posts: List<DashboardRecentPost>
|
||||||
|
}
|
||||||
|
|
||||||
|
value DashboardStats {
|
||||||
|
-- Three stat cards side by side:
|
||||||
|
total_posts: Integer
|
||||||
|
published_count: Integer
|
||||||
|
draft_count: Integer
|
||||||
|
archived_count: Integer -- shown only if > 0
|
||||||
|
media_count: Integer
|
||||||
|
image_count: Integer
|
||||||
|
total_media_size: String -- formatted B/KB/MB/GB
|
||||||
|
tag_count: Integer
|
||||||
|
category_count: Integer
|
||||||
|
}
|
||||||
|
|
||||||
|
value DashboardTimeline {
|
||||||
|
-- Bar chart of posts over time
|
||||||
|
-- Last 12 months that have data
|
||||||
|
-- Each bar: count label on top, month abbreviation + year below
|
||||||
|
-- Bar height proportional to max count
|
||||||
|
months: List<DashboardTimelineMonth>
|
||||||
|
}
|
||||||
|
|
||||||
|
value DashboardTimelineMonth {
|
||||||
|
label: String -- month abbreviation
|
||||||
|
year: Integer
|
||||||
|
count: Integer
|
||||||
|
}
|
||||||
|
|
||||||
|
value DashboardTagCloud {
|
||||||
|
-- Up to 40 tags, sorted alphabetically
|
||||||
|
-- Font size scaled 11px-22px based on post count
|
||||||
|
-- Tags with colours get coloured background with contrast text
|
||||||
|
-- Hover title shows post count
|
||||||
|
-- "and N more" text if > 40 tags
|
||||||
|
tags: List<DashboardTag>
|
||||||
|
overflow_count: Integer?
|
||||||
|
}
|
||||||
|
|
||||||
|
value DashboardTag {
|
||||||
|
name: String
|
||||||
|
count: Integer
|
||||||
|
color: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
value DashboardCategoryCloud {
|
||||||
|
-- All categories as badge-like tags
|
||||||
|
-- Each shows category name + count
|
||||||
|
categories: List<DashboardCategory>
|
||||||
|
}
|
||||||
|
|
||||||
|
value DashboardCategory {
|
||||||
|
name: String
|
||||||
|
count: Integer
|
||||||
|
}
|
||||||
|
|
||||||
|
value DashboardRecentPost {
|
||||||
|
-- Last 5 posts by updatedAt descending
|
||||||
|
-- Each row: title (or "Untitled"), status badge (draft/published), date
|
||||||
|
-- Single-click: preview tab, double-click: pin tab
|
||||||
|
post_id: String
|
||||||
|
title: String
|
||||||
|
status: String
|
||||||
|
date: String
|
||||||
|
}
|
||||||
|
|
||||||
|
config {
|
||||||
|
dashboard_max_tags: Integer = 40
|
||||||
|
dashboard_tag_min_font: Integer = 11
|
||||||
|
dashboard_tag_max_font: Integer = 22
|
||||||
|
dashboard_recent_count: Integer = 5
|
||||||
|
dashboard_timeline_months: Integer = 12
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ─── Post editor ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Single vertical column. Three regions: header, body, footer.
|
||||||
|
|
||||||
|
-- Header bar:
|
||||||
|
-- Left: post title (or "Untitled") with dirty indicator dot
|
||||||
|
-- Right: status badge, auto-save indicator, Quick Actions dropdown, Publish/Delete buttons
|
||||||
|
-- Quick Actions:
|
||||||
|
-- AI Analysis (suggests title, excerpt, slug)
|
||||||
|
-- Translate Post (opens translation modal)
|
||||||
|
|
||||||
|
-- Collapsible Metadata section (starts expanded when title is empty):
|
||||||
|
-- Two-column layout. Left column: metadata fields. Right column: linked media panel.
|
||||||
|
-- Left column fields:
|
||||||
|
-- Title (text input), Tags (autocomplete chip input), Author (text input),
|
||||||
|
-- Language (select + AI detect button), Do Not Translate (checkbox),
|
||||||
|
-- Slug (read-only), Categories (chip input), Template (select, if templates exist),
|
||||||
|
-- PostLinks (backlinks and outlinks)
|
||||||
|
-- Right column:
|
||||||
|
-- LinkedMediaPanel (media items linked to this post)
|
||||||
|
|
||||||
|
-- Language flags bar (inline with metadata toggle):
|
||||||
|
-- Row of flag emoji buttons for canonical language + each translation
|
||||||
|
-- Styles: status class (draft/published), active indicator
|
||||||
|
-- Clicking switches editor content to that language's draft
|
||||||
|
|
||||||
|
-- Collapsible Excerpt section:
|
||||||
|
-- Excerpt textarea (4 rows)
|
||||||
|
|
||||||
|
-- Editor Body:
|
||||||
|
-- Toolbar: "Content" label | mode toggle (Visual/Markdown/Preview) | action buttons
|
||||||
|
-- Action buttons (markdown mode only): Gallery, Insert Post Link, Insert Media
|
||||||
|
-- Visual mode: rich-text WYSIWYG editor
|
||||||
|
-- Markdown mode: code editor with markdown-with-macros language
|
||||||
|
-- Highlights [[macro ...]] double-bracket syntax
|
||||||
|
-- Word wrap on, minimap off, 14px font
|
||||||
|
-- Preview mode: iframe showing rendered preview
|
||||||
|
-- Supports drag-and-drop of images (imports media, inserts markdown)
|
||||||
|
|
||||||
|
-- Footer:
|
||||||
|
-- Three date stamps: Created, Updated, Published (published only if publishedAt exists)
|
||||||
|
-- Locale-formatted dates
|
||||||
|
|
||||||
|
-- Modals:
|
||||||
|
-- InsertModal for link search (Ctrl+K) and media search
|
||||||
|
-- AISuggestionsModal for AI-generated title/excerpt/slug
|
||||||
|
-- Translation modal: select target language, shows existing status
|
||||||
|
-- Lightbox for content images
|
||||||
|
|
||||||
|
value PostEditorView {
|
||||||
|
post_id: String
|
||||||
|
header: PostEditorHeader
|
||||||
|
metadata_expanded: Boolean
|
||||||
|
excerpt_expanded: Boolean
|
||||||
|
editor_mode: String -- visual | markdown | preview
|
||||||
|
}
|
||||||
|
|
||||||
|
value PostEditorHeader {
|
||||||
|
title: String -- post title or "Untitled"
|
||||||
|
is_dirty: Boolean
|
||||||
|
status: String -- draft | published | archived
|
||||||
|
is_auto_saving: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
config {
|
||||||
|
post_auto_save_delay: Integer = 3000
|
||||||
|
-- milliseconds of idle before auto-save triggers
|
||||||
|
}
|
||||||
|
|
||||||
|
invariant PostAutoSave {
|
||||||
|
-- Auto-saves after 3 seconds of idle in the editor
|
||||||
|
-- Also auto-saves on unmount/tab switch
|
||||||
|
-- Ctrl/Cmd+S triggers immediate save
|
||||||
|
}
|
||||||
|
|
||||||
|
invariant PostDirtyTracking {
|
||||||
|
-- Compares canonical draft + translation drafts against saved state
|
||||||
|
-- Dirty indicator shown in header and tab bar
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ─── Media editor ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Single column: preview area on top, metadata form below.
|
||||||
|
|
||||||
|
-- Header bar:
|
||||||
|
-- Media display name
|
||||||
|
-- Actions: Quick Actions dropdown, Replace File, Save, Delete
|
||||||
|
-- Quick Actions:
|
||||||
|
-- AI Image Analysis (image/* types only) - suggests title, alt, caption
|
||||||
|
-- Detect Language (from metadata text)
|
||||||
|
-- Translate Metadata (opens translation target modal)
|
||||||
|
|
||||||
|
-- Preview area:
|
||||||
|
-- Images: rendered via bds-media:// protocol with cache-busting timestamp
|
||||||
|
-- Non-images: SVG file icon placeholder + original filename text
|
||||||
|
|
||||||
|
-- Metadata form fields:
|
||||||
|
-- File Name (read-only), MIME Type (read-only),
|
||||||
|
-- Size (formatted as KB) + Dimensions (W x H, only if width/height exist),
|
||||||
|
-- Title (editable), Alt Text (editable), Caption (textarea 3 rows),
|
||||||
|
-- Tags (comma-separated text input), Author, Language (select)
|
||||||
|
|
||||||
|
-- Translations section (shown only when language is set):
|
||||||
|
-- List of existing translations: flag + language name + title
|
||||||
|
-- Per-translation: click to edit, refresh button, delete button
|
||||||
|
-- "No translations" message when empty
|
||||||
|
|
||||||
|
-- Linked Posts section:
|
||||||
|
-- "Link to Post" button opens post picker overlay
|
||||||
|
-- Post picker: search input, up to 10 unlinked posts, "and N more" if >10
|
||||||
|
-- List of currently linked posts (clickable -> navigates), unlink button
|
||||||
|
-- "Not linked to any posts" when empty
|
||||||
|
|
||||||
|
value MediaEditorView {
|
||||||
|
media_id: String
|
||||||
|
is_image: Boolean
|
||||||
|
file_name: String -- originalName, read-only
|
||||||
|
mime_type: String
|
||||||
|
file_size: String -- formatted
|
||||||
|
dimensions: String? -- "W x H" if available
|
||||||
|
title: String?
|
||||||
|
alt_text: String?
|
||||||
|
caption: String?
|
||||||
|
tags: String? -- comma-separated
|
||||||
|
author: String?
|
||||||
|
language: String?
|
||||||
|
translations: List<MediaTranslationItem>
|
||||||
|
linked_posts: List<LinkedPostItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
value MediaTranslationItem {
|
||||||
|
language: String
|
||||||
|
title: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
value LinkedPostItem {
|
||||||
|
post_id: String
|
||||||
|
title: String
|
||||||
|
}
|
||||||
|
|
||||||
|
-- No auto-save; explicit Save button required.
|
||||||
|
-- Replace File opens a native file dialog.
|
||||||
|
-- Delete shows confirmation with references (linked posts).
|
||||||
|
|
||||||
|
-- ─── Settings view ────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- VS Code-style settings page with header + search bar + scrollable sections.
|
||||||
|
-- Search filters sections by keyword match. "No results" with clear button.
|
||||||
|
|
||||||
|
-- 8 sections (style is a separate tab, not a settings section):
|
||||||
|
|
||||||
|
-- 1. Project Settings:
|
||||||
|
-- Project Name (text), Project Description (textarea 3 rows),
|
||||||
|
-- Data Path (text + Browse + Reset), Public URL (url),
|
||||||
|
-- Main Language (select), Blog Languages (checkboxes, main disabled),
|
||||||
|
-- Default Author (text), Max Posts Per Page (number 1-500 default 50),
|
||||||
|
-- Blogmark Category (select), Blogmark Bookmarklet (copy button), Save
|
||||||
|
|
||||||
|
-- 2. Editor Settings:
|
||||||
|
-- Default Editor Mode (select: WYSIWYG/Markdown/Preview),
|
||||||
|
-- Diff View Style (select: Inline/Side-by-side),
|
||||||
|
-- Wrap Long Lines (checkbox), Hide Unchanged Regions (checkbox)
|
||||||
|
|
||||||
|
-- 3. Content Settings (Categories):
|
||||||
|
-- Table: Category | Title | Render in Lists | Show Titles | Post Template | List Template | Remove
|
||||||
|
-- Protected categories: article, aside, page, picture (cannot be deleted)
|
||||||
|
-- Add Category: text input + Add button. Reset to Defaults button
|
||||||
|
|
||||||
|
-- 4. AI Assistant Settings:
|
||||||
|
-- API keys: OpenCode, Mistral (masked display + Change, or password input + Save)
|
||||||
|
-- Local providers: Ollama enable checkbox + capabilities table,
|
||||||
|
-- LM Studio enable checkbox + capabilities table
|
||||||
|
-- Offline mode: enable checkbox (disabled if no local providers),
|
||||||
|
-- selectors for chat/title/image models (local only)
|
||||||
|
-- Default Model (select with optgroup by provider + refresh),
|
||||||
|
-- shows: max output tokens, context window, pricing
|
||||||
|
-- Title Model (select), Image Analysis Model (vision-capable only)
|
||||||
|
-- System Prompt (textarea 12 rows + Save + Reset to Default)
|
||||||
|
|
||||||
|
-- 5. Technology Settings:
|
||||||
|
-- Python Runtime Mode (select: Web Worker/Main Thread)
|
||||||
|
-- Semantic Similarity (checkbox)
|
||||||
|
|
||||||
|
-- 6. Publishing Settings (SSH):
|
||||||
|
-- Info: SSH key auth only
|
||||||
|
-- SSH Mode (select: scp/rsync), SSH Host, SSH Username, SSH Remote Path
|
||||||
|
-- Save + Clear buttons
|
||||||
|
|
||||||
|
-- 7. Data Maintenance:
|
||||||
|
-- Rebuild buttons (6): Posts from Files, Media from Files, Scripts from Files,
|
||||||
|
-- Templates from Files, Links, Regenerate Missing Thumbnails
|
||||||
|
-- Open Data Folder button
|
||||||
|
|
||||||
|
-- 8. MCP Server Settings:
|
||||||
|
-- Status badge (port or "Not running")
|
||||||
|
-- Per-agent rows: Claude Code, Claude Desktop, GitHub Copilot, Gemini CLI,
|
||||||
|
-- OpenCode, Mistral Vibe, OpenAI Codex — each with Add/Remove toggle
|
||||||
|
|
||||||
|
-- ─── Style view ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Simple vertical layout: header, theme picker, controls row, preview.
|
||||||
|
|
||||||
|
-- Theme Picker: grid of theme buttons (one per Pico CSS theme).
|
||||||
|
-- Each button: swatch with 3 colour tones (accent, light bg, dark bg), theme name.
|
||||||
|
-- Selected theme: highlighted with aria-pressed.
|
||||||
|
|
||||||
|
-- Controls row: Preview Mode dropdown (Auto/Light/Dark), Apply Theme button.
|
||||||
|
|
||||||
|
-- Preview: iframe of style preview page at 127.0.0.1:4123/__style-preview
|
||||||
|
-- with theme and mode query params. Updates live on selection change.
|
||||||
|
|
||||||
|
-- Apply Theme persists to project metadata.
|
||||||
|
|
||||||
|
-- ─── Tags view ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Three sections controlled by sidebar TagsNav:
|
||||||
|
|
||||||
|
-- 1. Cloud: tag cloud visualization. Tags sized by post count,
|
||||||
|
-- coloured by tag colour. Clickable to select for editing.
|
||||||
|
|
||||||
|
-- 2. Manage (Create/Edit): tag name input, colour picker, template select.
|
||||||
|
-- Edit existing tag or create new. Delete button with confirmation.
|
||||||
|
|
||||||
|
-- 3. Merge: source tag select, target tag select, Merge button.
|
||||||
|
-- Merges all posts from source tag into target, removes source.
|
||||||
|
|
||||||
|
-- ─── Chat panel ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Three-region vertical layout: header, message area, input area.
|
||||||
|
|
||||||
|
-- API Key Required screen (shown when no API key):
|
||||||
|
-- Key icon, title, description, "Open Settings" button
|
||||||
|
|
||||||
|
-- Header:
|
||||||
|
-- Left: conversation title (or "New Chat"), CSS ellipsis on overflow
|
||||||
|
-- Right: model selector button + dropdown. Groups models by provider.
|
||||||
|
|
||||||
|
-- Message area:
|
||||||
|
-- Welcome screen (no messages, not streaming): robot icon, tips
|
||||||
|
-- Message list: each has avatar (user=person, assistant=robot), role label, text
|
||||||
|
-- User messages: plain text
|
||||||
|
-- Assistant messages: rendered as GFM Markdown
|
||||||
|
-- External images blocked (CSP), shown as links
|
||||||
|
-- Tool markers: checkmark/dot, tool name, truncated args (30 char strings)
|
||||||
|
-- Streaming: accumulating markdown + tool markers, thinking dots animation
|
||||||
|
-- Auto-scrolls to bottom on new messages
|
||||||
|
|
||||||
|
-- Input area:
|
||||||
|
-- Abort/Stop button (only during streaming, square stop icon)
|
||||||
|
-- Auto-growing textarea (max 200px). Enter sends, Shift+Enter newline
|
||||||
|
-- Send button (up-arrow icon), disabled when empty or streaming
|
||||||
|
|
||||||
|
-- Token usage tracked per conversation (displayed in status bar, not chat panel).
|
||||||
|
-- Per-conversation model override via header selector.
|
||||||
|
|
||||||
|
config {
|
||||||
|
chat_tool_args_max_length: Integer = 30
|
||||||
|
chat_input_max_height: Integer = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ─── Git diff view ────────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Renders diff for a file (working tree vs HEAD) or a commit.
|
||||||
|
-- File diff: id = "git-diff:{filePath}"
|
||||||
|
-- Commit diff: id = "git-diff:commit:{commitHash}"
|
||||||
|
-- Supports inline and side-by-side diff display modes (from editor settings).
|
||||||
|
|
||||||
|
-- ─── Menu editor view ────────────────────────────────────────
|
||||||
|
|
||||||
|
-- Visual editor for the OPML navigation menu (meta/menu.opml).
|
||||||
|
-- Drag-and-drop reordering of menu items.
|
||||||
|
-- Item types: page, submenu, category_archive, home.
|
||||||
|
-- Add/remove items, edit labels and targets.
|
||||||
|
-- See menu.allium for data model.
|
||||||
|
|
||||||
|
-- ─── Metadata diff view ──────────────────────────────────────
|
||||||
|
|
||||||
|
-- Shows DB vs filesystem differences for all entity types.
|
||||||
|
-- Each diff entry: entity type, field name, DB value, file value.
|
||||||
|
-- Actions: apply file value to DB, or apply DB value to file.
|
||||||
|
-- See metadata_diff.allium for diff field definitions.
|
||||||
|
|
||||||
|
-- ─── Documentation views ─────────────────────────────────────
|
||||||
|
|
||||||
|
-- documentation: renders DOCUMENTATION.md as styled HTML
|
||||||
|
-- api_documentation: renders API.md as styled HTML
|
||||||
|
|
||||||
|
-- ─── Validation views ────────────────────────────────────────
|
||||||
|
|
||||||
|
-- site_validation: checks generated site for broken links, missing assets
|
||||||
|
-- translation_validation: checks translation completeness across languages
|
||||||
|
|
||||||
|
-- ─── Find duplicates view ────────────────────────────────────
|
||||||
|
|
||||||
|
-- Uses embedding vectors to find semantically similar posts.
|
||||||
|
-- Shows candidate pairs with similarity score.
|
||||||
|
-- See embedding.allium for vector details.
|
||||||
|
|
||||||
|
-- ─── Script editor view ──────────────────────────────────────
|
||||||
|
|
||||||
|
-- Code editor for Lua scripts (macros, utilities, transforms).
|
||||||
|
-- Header: script title, kind badge, enabled toggle.
|
||||||
|
-- Editor: code editor with Lua syntax highlighting.
|
||||||
|
-- Toolbar: Run button, entrypoint selector.
|
||||||
|
-- See script.allium for entity model.
|
||||||
|
|
||||||
|
-- ─── Template editor view ─────────────────────────────────────
|
||||||
|
|
||||||
|
-- Code editor for Liquid templates (post, list, not_found, partial).
|
||||||
|
-- Header: template title, kind badge, enabled toggle, version.
|
||||||
|
-- Editor: code editor with Liquid syntax highlighting.
|
||||||
|
-- Preview: rendered template output in iframe.
|
||||||
|
-- See template.allium for entity model and Liquid subset.
|
||||||
@@ -33,13 +33,20 @@ config {
|
|||||||
-- SidebarEntityList: reusable pattern for scripts, templates, chat, import.
|
-- SidebarEntityList: reusable pattern for scripts, templates, chat, import.
|
||||||
-- Shows: header with title + create button, scrollable item list,
|
-- Shows: header with title + create button, scrollable item list,
|
||||||
-- empty state with message + action CTA.
|
-- empty state with message + action CTA.
|
||||||
|
-- All titles and names are CSS-truncated with ellipsis when they overflow
|
||||||
|
-- the sidebar width. Hard JS truncation limits are noted per view.
|
||||||
|
|
||||||
-- Date formatting for sidebar items:
|
-- Relative date formatting for entity list items (chat, scripts, templates, import):
|
||||||
-- Today -> time only (e.g. "2:30 PM")
|
-- Same day (diffDays=0) -> time only via toLocaleTimeString (e.g. "2:30 PM")
|
||||||
-- Yesterday -> i18n "Yesterday"
|
-- Yesterday (diffDays=1) -> i18n key sidebar.chat.yesterday
|
||||||
-- <7 days -> short weekday (e.g. "Mon")
|
-- <7 days -> short weekday via toLocaleDateString weekday:short (e.g. "Mon")
|
||||||
-- Older -> "Mon DD" format
|
-- >=7 days -> short month + day via toLocaleDateString month:short day:numeric (e.g. "Feb 10")
|
||||||
-- Locale map: en->en-US, de->de-DE, fr->fr-FR, it->it-IT, es->es-ES
|
-- Locale map: en->en-US, de->de-DE, fr->fr-FR, it->it-IT, es->es-ES
|
||||||
|
-- Fallback locale: en-US
|
||||||
|
|
||||||
|
-- Post/page date formatting (different from relative dates above):
|
||||||
|
-- toLocaleDateString(locale, { month: 'short', day: 'numeric', year: 'numeric' })
|
||||||
|
-- Example: "Feb 10, 2026"
|
||||||
|
|
||||||
-- ─── 1. Posts view ────────────────────────────────────────────
|
-- ─── 1. Posts view ────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -64,12 +71,18 @@ value PostsView {
|
|||||||
value PostListItem {
|
value PostListItem {
|
||||||
post_id: String
|
post_id: String
|
||||||
type_icon: String -- derived from categories (below)
|
type_icon: String -- derived from categories (below)
|
||||||
title: String -- fallback "Untitled"
|
title: String -- post.title, fallback "Untitled"; CSS ellipsis on overflow
|
||||||
language_count: Integer? -- badge shown when availableLanguages.count > 1
|
language_count: Integer? -- badge shown when availableLanguages.count > 1
|
||||||
date: String -- drafts/archived: updatedAt; published: publishedAt ?? updatedAt
|
date: String -- locale-formatted (month day, year)
|
||||||
active: Boolean -- highlighted when activeTabId = post.id
|
active: Boolean -- highlighted when activeTabId = post.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Per-item data shown (left to right):
|
||||||
|
-- [type_icon] [title ...ellipsis] [lang badge]
|
||||||
|
-- [date]
|
||||||
|
-- Title is CSS-truncated (white-space:nowrap, text-overflow:ellipsis).
|
||||||
|
-- Date source: drafts/archived use updatedAt; published use publishedAt ?? updatedAt.
|
||||||
|
|
||||||
-- Post type icon derivation from categories (first match wins, case-insensitive):
|
-- Post type icon derivation from categories (first match wins, case-insensitive):
|
||||||
-- picture/photo/image -> camera icon
|
-- picture/photo/image -> camera icon
|
||||||
-- aside/note/quick -> notepad icon
|
-- aside/note/quick -> notepad icon
|
||||||
@@ -106,13 +119,24 @@ value MediaView {
|
|||||||
|
|
||||||
value MediaGridItem {
|
value MediaGridItem {
|
||||||
media_id: String
|
media_id: String
|
||||||
has_thumbnail: Boolean -- image: custom protocol bds-thumb://{id}; non-image: file icon
|
has_thumbnail: Boolean -- image: bds-thumb://{id} protocol; non-image: file icon SVG
|
||||||
name: String -- title truncated 60 chars, fallback originalName
|
name: String -- title truncated to 60 chars + "..." if over; fallback originalName (no truncation)
|
||||||
file_size: String -- formatted (B / KB / MB)
|
file_size: String -- formatted (B / KB / MB)
|
||||||
tooltip: String -- caption ?? originalName
|
tooltip: String -- caption ?? originalName
|
||||||
active: Boolean -- highlighted when activeTabId = media.id
|
active: Boolean -- highlighted when activeTabId = media.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Per-item data shown in grid cell:
|
||||||
|
-- [thumbnail or file icon]
|
||||||
|
-- [name ...CSS ellipsis]
|
||||||
|
-- [file_size]
|
||||||
|
-- Name: JS hard limit of 60 chars on title (substring + "..."), originalName not truncated.
|
||||||
|
-- CSS also applies text-overflow:ellipsis as a safety net.
|
||||||
|
|
||||||
|
config {
|
||||||
|
media_title_max_length: Integer = 60
|
||||||
|
}
|
||||||
|
|
||||||
rule MediaListClick {
|
rule MediaListClick {
|
||||||
when: MediaGridItemClicked(media_id, click_type)
|
when: MediaGridItemClicked(media_id, click_type)
|
||||||
if click_type = single:
|
if click_type = single:
|
||||||
@@ -133,11 +157,15 @@ value ScriptsView {
|
|||||||
|
|
||||||
value ScriptListItem {
|
value ScriptListItem {
|
||||||
script_id: String
|
script_id: String
|
||||||
title: String
|
title: String -- CSS ellipsis on overflow
|
||||||
date: String -- relative date format
|
date: String -- relative date format (see shared patterns)
|
||||||
active: Boolean
|
active: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Per-item data shown:
|
||||||
|
-- [title ...CSS ellipsis] [delete x]
|
||||||
|
-- [relative date]
|
||||||
|
|
||||||
rule ScriptListClick {
|
rule ScriptListClick {
|
||||||
when: ScriptListItemClicked(script_id, click_type)
|
when: ScriptListItemClicked(script_id, click_type)
|
||||||
if click_type = single:
|
if click_type = single:
|
||||||
@@ -161,11 +189,13 @@ value TemplatesView {
|
|||||||
|
|
||||||
value TemplateListItem {
|
value TemplateListItem {
|
||||||
template_id: String
|
template_id: String
|
||||||
title: String
|
title: String -- CSS ellipsis on overflow
|
||||||
date: String
|
date: String -- relative date format
|
||||||
active: Boolean
|
active: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Per-item data shown: same layout as ScriptListItem.
|
||||||
|
|
||||||
rule TemplateListClick {
|
rule TemplateListClick {
|
||||||
when: TemplateListItemClicked(template_id, click_type)
|
when: TemplateListItemClicked(template_id, click_type)
|
||||||
if click_type = single:
|
if click_type = single:
|
||||||
@@ -238,10 +268,14 @@ value ChatView {
|
|||||||
|
|
||||||
value ChatListItem {
|
value ChatListItem {
|
||||||
conversation_id: String
|
conversation_id: String
|
||||||
title: String -- live-updated via onTitleUpdated events
|
title: String -- live-updated via onTitleUpdated; CSS ellipsis on overflow
|
||||||
date: String -- relative date format
|
date: String -- relative date format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Per-item data shown:
|
||||||
|
-- [title ...CSS ellipsis] [delete x]
|
||||||
|
-- [relative date]
|
||||||
|
|
||||||
rule ChatListClick {
|
rule ChatListClick {
|
||||||
when: ChatListItemClicked(conversation_id)
|
when: ChatListItemClicked(conversation_id)
|
||||||
ensures: OpenTabRequested(type: chat, id: conversation_id, intent: pin)
|
ensures: OpenTabRequested(type: chat, id: conversation_id, intent: pin)
|
||||||
@@ -260,8 +294,8 @@ value ImportView {
|
|||||||
|
|
||||||
value ImportListItem {
|
value ImportListItem {
|
||||||
definition_id: String
|
definition_id: String
|
||||||
name: String -- live-updated via onNameUpdated events
|
name: String -- live-updated via onNameUpdated; CSS ellipsis on overflow
|
||||||
date: String
|
date: String -- relative date format
|
||||||
}
|
}
|
||||||
|
|
||||||
rule ImportListClick {
|
rule ImportListClick {
|
||||||
@@ -296,17 +330,19 @@ value GitStatusFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
value GitHistoryEntry {
|
value GitHistoryEntry {
|
||||||
short_hash: String
|
short_hash: String -- 7 chars
|
||||||
subject: String
|
subject: String -- wraps (word-break), not truncated
|
||||||
author: String
|
author: String
|
||||||
date: String -- locale-formatted
|
date: String -- locale-formatted
|
||||||
sync_status: String -- synced, local_only, remote_only
|
sync_status: String -- synced, local_only, remote_only (coloured dots)
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Action buttons: fetch, pull, push, prune_lfs. All disabled while any action is loading.
|
-- Action buttons: fetch, pull, push, prune_lfs. All disabled while any action is loading.
|
||||||
-- Changes section: file count, commit message input, commit button, file list.
|
-- Changes section: file count, commit message input, commit button, file list.
|
||||||
|
-- File paths in changes list: CSS ellipsis on overflow.
|
||||||
-- Changes list polls every 2 seconds in background.
|
-- Changes list polls every 2 seconds in background.
|
||||||
-- Remote state refreshes every 30 seconds with auto-fetch.
|
-- Remote state refreshes every 30 seconds with auto-fetch.
|
||||||
|
-- History entries: subject + shortHash + author + date; sync status with coloured legend dots.
|
||||||
|
|
||||||
rule GitFileClick {
|
rule GitFileClick {
|
||||||
when: GitStatusFileClicked(file_path, click_type)
|
when: GitStatusFileClicked(file_path, click_type)
|
||||||
|
|||||||
@@ -146,24 +146,35 @@ rule ResolveEditorRoute {
|
|||||||
-- Horizontal strip with overflow scroll (left/right arrow buttons, 150px per click).
|
-- Horizontal strip with overflow scroll (left/right arrow buttons, 150px per click).
|
||||||
-- Auto-scrolls to bring active tab into view (10px padding).
|
-- Auto-scrolls to bring active tab into view (10px padding).
|
||||||
|
|
||||||
|
config {
|
||||||
|
tab_min_width: Integer = 100
|
||||||
|
tab_max_width: Integer = 160
|
||||||
|
tab_scroll_step: Integer = 150
|
||||||
|
chat_title_max_length: Integer = 18
|
||||||
|
git_hash_display_length: Integer = 7
|
||||||
|
}
|
||||||
|
|
||||||
value TabBarItem {
|
value TabBarItem {
|
||||||
title: String
|
title: String -- resolved per type (see below); CSS ellipsis at tab_max_width
|
||||||
is_active: Boolean
|
is_active: Boolean
|
||||||
is_transient: Boolean -- italic title when true
|
is_transient: Boolean -- italic title when true
|
||||||
is_dirty: Boolean -- dot indicator, only for post tabs
|
is_dirty: Boolean -- dot indicator, only for post tabs
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Tab title resolution:
|
-- Tab title resolution:
|
||||||
-- post: post.title from DB (listens post-updated events)
|
-- post: post.title from DB (listens post-updated events); no JS truncation
|
||||||
-- media: media.originalName
|
-- media: media.originalName; no JS truncation
|
||||||
-- scripts: script.title from DB (listens scripts-changed)
|
-- scripts: script.title from DB (listens scripts-changed); no JS truncation
|
||||||
-- templates: template.title from DB (listens templates-changed)
|
-- templates: template.title from DB (listens templates-changed); no JS truncation
|
||||||
-- chat: conversation.title, truncated to 18 chars
|
-- chat: conversation.title, JS-truncated to 18 chars + "..." if over limit
|
||||||
-- import: definition.name (listens name-updated)
|
-- import: definition.name (listens name-updated); no JS truncation
|
||||||
-- git_diff file: filename from path (last path segment)
|
-- git_diff file: filename only (last path segment); no JS truncation
|
||||||
-- git_diff commit: "{shortHash} {subject}" from git history
|
-- git_diff commit: "{shortHash} {subject}" (shortHash = 7 chars); fallback: 7-char hash only
|
||||||
-- singletons: i18n key lookup (common.settings, tabBar.style, etc.)
|
-- singletons: i18n key lookup (common.settings, tabBar.style, etc.)
|
||||||
-- fallback: i18n:tabBar.unknown
|
-- fallback: i18n:tabBar.unknown
|
||||||
|
--
|
||||||
|
-- All tab titles are additionally CSS-truncated (text-overflow:ellipsis, white-space:nowrap)
|
||||||
|
-- within the tab's max-width of 160px.
|
||||||
|
|
||||||
-- ─── Tab interactions ─────────────────────────────────────────
|
-- ─── Tab interactions ─────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user