251
specs/modals.allium
Normal file
251
specs/modals.allium
Normal file
@@ -0,0 +1,251 @@
|
||||
-- allium: 1
|
||||
-- bDS Shared Modals
|
||||
-- Scope: UI overlays used across multiple editor views
|
||||
-- Distilled from: PostEditor.tsx, MediaEditor.tsx
|
||||
|
||||
-- Shared modal components used by multiple editors.
|
||||
-- Editor-specific modals live in their respective editor spec files.
|
||||
|
||||
use "./i18n.allium" as i18n
|
||||
|
||||
-- ─── AI Suggestions Modal ────────────────────────────────────
|
||||
|
||||
-- Shared modal for presenting AI suggestions with per-field accept/reject.
|
||||
-- Used by PostAIAnalysis (editor_post.allium) and
|
||||
-- MediaAIImageAnalysis (editor_media.allium).
|
||||
|
||||
value AISuggestionsModal {
|
||||
fields: List<AISuggestionField>
|
||||
-- Layout: title bar ("AI Suggestions"), scrollable field list, button row
|
||||
-- Each field rendered as a row:
|
||||
-- Left: checkbox (accept/reject), label
|
||||
-- Center: current value (read-only, muted), arrow, suggested value (highlighted)
|
||||
-- Special: slug field checkbox disabled if post was ever published
|
||||
-- Buttons: Cancel (secondary), Apply Selected (primary)
|
||||
-- Cancel discards all; Apply writes only accepted fields to entity
|
||||
}
|
||||
|
||||
surface AISuggestionsModalSurface {
|
||||
context modal: AISuggestionsModal
|
||||
|
||||
exposes:
|
||||
modal.fields.count
|
||||
}
|
||||
|
||||
value AISuggestionField {
|
||||
label: String
|
||||
current_value: String
|
||||
suggested_value: String
|
||||
accepted: Boolean -- checkbox, default true
|
||||
locked: Boolean -- if true, checkbox disabled (e.g. published slug)
|
||||
}
|
||||
|
||||
-- ─── Insert Post Link Modal ──────────────────────────────────
|
||||
|
||||
value InsertPostLinkModal {
|
||||
-- Two-tab modal opened by Ctrl/Cmd+K in post editor (markdown mode).
|
||||
-- Tab 1 - Internal:
|
||||
-- Search input (debounced 300ms, queries post titles via FTS)
|
||||
--
|
||||
-- Empty query state (search_query < 2 chars):
|
||||
-- If semanticSimilarityEnabled: shows up to 5 related posts via
|
||||
-- FindSimilar(current_post, 5) ranked by embedding similarity
|
||||
-- Else: shows nothing (empty results)
|
||||
--
|
||||
-- Active query state (search_query >= 2 chars):
|
||||
-- Results from FTS title search
|
||||
-- If semanticSimilarityEnabled: each result augmented with similarity
|
||||
-- score from ComputeSimilarities(current_post, result_post_ids)
|
||||
-- Scores displayed as visual indicator per result row
|
||||
-- Results list: post title + status badge + optional similarity score
|
||||
--
|
||||
-- Click result: inserts [title](/YYYY/MM/DD/slug) at cursor, closes modal
|
||||
-- "Create Post" row at bottom of results:
|
||||
-- Creates new post with search query as title, inserts link to it
|
||||
-- Tab 2 - External:
|
||||
-- URL input field (required)
|
||||
-- Display text input field (optional)
|
||||
-- Insert button: inserts [text](url) or bare url if no text, closes modal
|
||||
active_tab: String -- internal | external
|
||||
search_query: String
|
||||
results: List<InsertLinkResult>
|
||||
related_posts: List<InsertLinkResult> -- similarity-based, shown when query empty
|
||||
}
|
||||
|
||||
surface InsertPostLinkModalSurface {
|
||||
context modal: InsertPostLinkModal
|
||||
|
||||
exposes:
|
||||
modal.active_tab
|
||||
modal.search_query
|
||||
modal.results.count
|
||||
modal.related_posts.count
|
||||
}
|
||||
|
||||
value InsertLinkResult {
|
||||
post_id: String
|
||||
title: String
|
||||
status: String -- draft | published | archived
|
||||
canonical_url: String -- /YYYY/MM/DD/slug
|
||||
similarity_score: Decimal? -- 0.0-1.0, present when embeddings enabled
|
||||
}
|
||||
|
||||
-- ─── Insert Media Modal ──────────────────────────────────────
|
||||
|
||||
value InsertMediaModal {
|
||||
-- Grid modal for inserting media references into post content.
|
||||
-- Search input filtering by media title and original filename.
|
||||
-- Grid of media items: bds-thumb:// thumbnail (medium 400px), title below.
|
||||
-- Click item:
|
||||
-- Images: inserts  at cursor
|
||||
-- Non-images: inserts [originalName](bds-media://id) at cursor
|
||||
-- Closes modal after insertion.
|
||||
search_query: String
|
||||
results: List<InsertMediaResult>
|
||||
}
|
||||
|
||||
surface InsertMediaModalSurface {
|
||||
context modal: InsertMediaModal
|
||||
|
||||
exposes:
|
||||
modal.search_query
|
||||
modal.results.count
|
||||
}
|
||||
|
||||
value InsertMediaResult {
|
||||
media_id: String
|
||||
title: String
|
||||
original_name: String
|
||||
is_image: Boolean
|
||||
thumbnail_url: String? -- bds-thumb:// for images, null for others
|
||||
}
|
||||
|
||||
-- ─── Language Picker Modal ───────────────────────────────────
|
||||
|
||||
value LanguagePickerModal {
|
||||
-- Shown for Translate Post and Translate Media Metadata actions.
|
||||
-- Lists all configured blogLanguages except source language.
|
||||
-- Each row: flag emoji, language name, status badge if translation exists.
|
||||
-- Existing translations show "(draft)" or "(published)" badge.
|
||||
-- Click selects target language and initiates translation flow.
|
||||
-- Cancel closes without action.
|
||||
source_language: String
|
||||
available_targets: List<LanguageTarget>
|
||||
}
|
||||
|
||||
surface LanguagePickerModalSurface {
|
||||
context modal: LanguagePickerModal
|
||||
|
||||
exposes:
|
||||
modal.source_language
|
||||
modal.available_targets.count
|
||||
}
|
||||
|
||||
value LanguageTarget {
|
||||
code: String
|
||||
name: String
|
||||
flag_emoji: String
|
||||
has_existing_translation: Boolean
|
||||
existing_status: String? -- draft | published, if translation exists
|
||||
}
|
||||
|
||||
-- ─── Confirm Delete Modal ────────────────────────────────────
|
||||
|
||||
value ConfirmDeleteModal {
|
||||
-- Custom styled modal for destructive operations with reference info.
|
||||
-- Used by: MediaDelete (shows linked posts), TagDelete (shows post count).
|
||||
-- Layout: warning icon, title, entity name, reference section, buttons.
|
||||
-- Reference section: "This item is referenced by:" + bulleted list.
|
||||
-- Buttons: Cancel (secondary), Delete (destructive red).
|
||||
entity_name: String
|
||||
entity_type: String -- media | tag
|
||||
reference_count: Integer
|
||||
reference_list: List<String> -- titles of referencing entities
|
||||
}
|
||||
|
||||
surface ConfirmDeleteModalSurface {
|
||||
context modal: ConfirmDeleteModal
|
||||
|
||||
exposes:
|
||||
modal.entity_name
|
||||
modal.entity_type
|
||||
modal.reference_count
|
||||
modal.reference_list
|
||||
}
|
||||
|
||||
-- ─── Confirm Dialog ──────────────────────────────────────────
|
||||
|
||||
value ConfirmDialog {
|
||||
-- Custom styled modal for non-delete confirmations.
|
||||
-- Used by: TagMerge ("Merge N tags into {target}? Cannot be undone.").
|
||||
-- Layout: title, descriptive message, buttons.
|
||||
-- Buttons: Cancel (secondary), Confirm (primary).
|
||||
title: String
|
||||
message: String
|
||||
}
|
||||
|
||||
surface ConfirmDialogSurface {
|
||||
context modal: ConfirmDialog
|
||||
|
||||
exposes:
|
||||
modal.title
|
||||
modal.message
|
||||
}
|
||||
|
||||
-- System confirm dialogs are NOT modelled as values.
|
||||
-- They are simple yes/no system dialogs with a message string.
|
||||
-- Used by: PostDelete, PostDiscard, TemplateDelete (with references).
|
||||
|
||||
-- ─── Gallery Overlay ─────────────────────────────────────────
|
||||
|
||||
value GalleryOverlay {
|
||||
-- Full-screen overlay showing all media linked to a post.
|
||||
-- Opened from Gallery button in post editor toolbar (markdown mode).
|
||||
-- Image grid: bds-thumb:// thumbnails (medium 400px), 3-4 columns.
|
||||
-- Click image: opens LightboxView for that image.
|
||||
-- Close: X button or ESC key.
|
||||
post_id: String
|
||||
images: List<GalleryImage>
|
||||
}
|
||||
|
||||
surface GalleryOverlaySurface {
|
||||
context overlay: GalleryOverlay
|
||||
|
||||
exposes:
|
||||
overlay.post_id
|
||||
overlay.images.count
|
||||
}
|
||||
|
||||
value GalleryImage {
|
||||
media_id: String
|
||||
thumbnail_url: String -- bds-thumb://media_id
|
||||
alt_text: String?
|
||||
}
|
||||
|
||||
value LightboxView {
|
||||
-- Full-screen image viewer, sub-view of GalleryOverlay.
|
||||
-- Shows single image at full resolution via bds-media:// protocol.
|
||||
-- Navigation: left/right arrow buttons, keyboard left/right arrow keys.
|
||||
-- Close: X button, ESC key, or click outside image area.
|
||||
-- Header: image title or filename, index counter "3 of 12".
|
||||
current_index: Integer
|
||||
total_count: Integer
|
||||
media_id: String
|
||||
image_url: String -- bds-media://media_id
|
||||
alt_text: String?
|
||||
}
|
||||
|
||||
surface LightboxViewSurface {
|
||||
context view: LightboxView
|
||||
|
||||
exposes:
|
||||
view.current_index
|
||||
view.total_count
|
||||
view.media_id
|
||||
view.image_url
|
||||
view.alt_text when view.alt_text != null
|
||||
}
|
||||
|
||||
-- All modals rendered as centered overlay with backdrop dimming.
|
||||
-- ESC key or backdrop click closes modal (cancel semantics).
|
||||
-- Overlays (PostPicker, ColourPicker) are positioned inline near trigger.
|
||||
Reference in New Issue
Block a user