chore: added user as surface and spec'ed modals
This commit is contained in:
@@ -15,6 +15,44 @@ use "./post.allium" as post
|
|||||||
use "./media.allium" as media
|
use "./media.allium" as media
|
||||||
use "./i18n.allium" as i18n
|
use "./i18n.allium" as i18n
|
||||||
|
|
||||||
|
-- ─── External surfaces ──────────────────────────────────────
|
||||||
|
|
||||||
|
-- The user is the external actor who initiates all editor interactions
|
||||||
|
-- via clicks, keyboard shortcuts, and drag-and-drop. This surface
|
||||||
|
-- provides the triggers that editor action rules listen for.
|
||||||
|
|
||||||
|
surface User {
|
||||||
|
provides: PostAIAnalysisRequested(post_id)
|
||||||
|
provides: PostTranslateRequested(post_id, target_language)
|
||||||
|
provides: PostSaved(post_id)
|
||||||
|
provides: PostPublishRequested(post_id)
|
||||||
|
provides: PostDiscardRequested(post_id)
|
||||||
|
provides: PostDeleteRequested(post_id)
|
||||||
|
provides: PostInsertLinkRequested(post_id)
|
||||||
|
provides: PostInsertMediaRequested(post_id)
|
||||||
|
provides: PostGalleryRequested(post_id)
|
||||||
|
provides: ImageDroppedOnEditor(post_id, file_path)
|
||||||
|
provides: PostLanguageDetectRequested(post_id)
|
||||||
|
provides: MediaAIImageAnalysisRequested(media_id)
|
||||||
|
provides: MediaDetectLanguageRequested(media_id)
|
||||||
|
provides: MediaTranslateMetadataRequested(media_id, target_language)
|
||||||
|
provides: MediaReplaceFileRequested(media_id)
|
||||||
|
provides: MediaDeleteRequested(media_id)
|
||||||
|
provides: MediaLinkToPostRequested(media_id)
|
||||||
|
provides: MediaTranslationEditClicked(media_id, language)
|
||||||
|
provides: MediaTranslationRefreshClicked(media_id, language)
|
||||||
|
provides: MediaTranslationDeleteClicked(media_id, language)
|
||||||
|
provides: StyleThemeSelected(theme_name)
|
||||||
|
provides: StyleApplyRequested(theme_name)
|
||||||
|
provides: ScriptSaveRequested(script_id)
|
||||||
|
provides: ScriptRunRequested(script_id)
|
||||||
|
provides: ScriptDeleteRequested(script_id)
|
||||||
|
provides: TemplateSaveRequested(template_id)
|
||||||
|
provides: TemplateDeleteRequested(template_id)
|
||||||
|
provides: ImportAnalyzeRequested(definition_id, file_path)
|
||||||
|
provides: ImportExecuteRequested(definition_id)
|
||||||
|
}
|
||||||
|
|
||||||
-- ─── Dashboard (no tab active) ───────────────────────────────
|
-- ─── Dashboard (no tab active) ───────────────────────────────
|
||||||
|
|
||||||
-- Shown as default/welcome view when no entity tab is active.
|
-- Shown as default/welcome view when no entity tab is active.
|
||||||
@@ -188,7 +226,13 @@ invariant PostDirtyTracking {
|
|||||||
-- Used by PostAIAnalysis and MediaAIImageAnalysis.
|
-- Used by PostAIAnalysis and MediaAIImageAnalysis.
|
||||||
value AISuggestionsModal {
|
value AISuggestionsModal {
|
||||||
fields: List<AISuggestionField>
|
fields: List<AISuggestionField>
|
||||||
-- Confirm applies only accepted fields; Cancel discards all
|
-- 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
|
||||||
}
|
}
|
||||||
|
|
||||||
value AISuggestionField {
|
value AISuggestionField {
|
||||||
@@ -196,6 +240,187 @@ value AISuggestionField {
|
|||||||
current_value: String
|
current_value: String
|
||||||
suggested_value: String
|
suggested_value: String
|
||||||
accepted: Boolean -- checkbox, default true
|
accepted: Boolean -- checkbox, default true
|
||||||
|
locked: Boolean -- if true, checkbox disabled (e.g. published slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ─── Modals and overlays ────────────────────────────────────
|
||||||
|
|
||||||
|
-- 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.
|
||||||
|
|
||||||
|
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)
|
||||||
|
-- Results list: post title + status badge (draft/published/archived)
|
||||||
|
-- If semantic similarity enabled: results ranked by vector similarity
|
||||||
|
-- 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>
|
||||||
|
}
|
||||||
|
|
||||||
|
value InsertLinkResult {
|
||||||
|
post_id: String
|
||||||
|
title: String
|
||||||
|
status: String -- draft | published | archived
|
||||||
|
canonical_url: String -- /YYYY/MM/DD/slug
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
|
||||||
|
value InsertMediaResult {
|
||||||
|
media_id: String
|
||||||
|
title: String
|
||||||
|
original_name: String
|
||||||
|
is_image: Boolean
|
||||||
|
thumbnail_url: String? -- bds-thumb:// for images, null for others
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
|
||||||
|
value LanguageTarget {
|
||||||
|
code: String
|
||||||
|
name: String
|
||||||
|
flag_emoji: String
|
||||||
|
has_existing_translation: Boolean
|
||||||
|
existing_status: String? -- draft | published, if translation exists
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Native confirm dialogs (via rfd crate) are NOT modelled as values.
|
||||||
|
-- They are simple yes/no system dialogs with a message string.
|
||||||
|
-- Used by: PostDelete, PostDiscard, TemplateDelete (with references).
|
||||||
|
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
|
||||||
|
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?
|
||||||
|
}
|
||||||
|
|
||||||
|
value ColourPickerPopover {
|
||||||
|
-- Inline popover positioned below tag colour field (tags view manage).
|
||||||
|
-- Not a modal — does not dim backdrop, no ESC/backdrop close.
|
||||||
|
-- Closes when clicking outside popover or selecting a colour.
|
||||||
|
-- Grid of 17 preset colour swatches (4x4 + 1).
|
||||||
|
-- Below grid: custom hex input field (#RRGGBB).
|
||||||
|
-- Selection is immediate — no confirm/cancel buttons.
|
||||||
|
-- Choosing a colour updates the tag form's colour field live.
|
||||||
|
presets: List<String> -- 17 hex colour values
|
||||||
|
custom_hex: String?
|
||||||
|
selected: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
config {
|
||||||
|
colour_picker_preset_count: Integer = 17
|
||||||
|
}
|
||||||
|
|
||||||
|
value PostPickerOverlay {
|
||||||
|
-- Inline overlay positioned near "Link to Post" button (media editor).
|
||||||
|
-- Not a modal — positioned as dropdown below trigger button.
|
||||||
|
-- Search input filtering posts by title (only unlinked posts shown).
|
||||||
|
-- Up to 10 results displayed. "and N more" text if total exceeds 10.
|
||||||
|
-- Click result: links media to selected post, closes overlay.
|
||||||
|
search_query: String
|
||||||
|
results: List<PostPickerResult>
|
||||||
|
overflow_count: Integer? -- shown as "and N more" if > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
value PostPickerResult {
|
||||||
|
post_id: String
|
||||||
|
title: String
|
||||||
|
}
|
||||||
|
|
||||||
|
value ModelSelectorDropdown {
|
||||||
|
-- Dropdown in chat panel header for per-conversation model override.
|
||||||
|
-- Groups models by provider using section headers.
|
||||||
|
-- Each entry: model display name.
|
||||||
|
-- Expandable details per model: context window, max output tokens, pricing.
|
||||||
|
-- Selected model persisted with conversation record.
|
||||||
|
-- Changing model mid-conversation applies to subsequent messages only.
|
||||||
|
groups: List<ModelProviderGroup>
|
||||||
|
selected_model_id: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
value ModelProviderGroup {
|
||||||
|
provider_name: String -- e.g. "OpenAI", "Ollama", "LM Studio"
|
||||||
|
models: List<ModelEntry>
|
||||||
|
}
|
||||||
|
|
||||||
|
value ModelEntry {
|
||||||
|
model_id: String
|
||||||
|
display_name: String
|
||||||
|
context_window: Integer
|
||||||
|
max_output_tokens: Integer
|
||||||
}
|
}
|
||||||
|
|
||||||
rule PostAIAnalysis {
|
rule PostAIAnalysis {
|
||||||
|
|||||||
@@ -17,6 +17,59 @@ use "./post.allium" as post
|
|||||||
use "./media.allium" as media
|
use "./media.allium" as media
|
||||||
use "./search.allium" as search
|
use "./search.allium" as search
|
||||||
|
|
||||||
|
-- ─── External surfaces ──────────────────────────────────────
|
||||||
|
|
||||||
|
-- User-initiated navigation and entity management actions.
|
||||||
|
-- These triggers originate from sidebar clicks, tab operations,
|
||||||
|
-- dashboard interactions, and save/delete actions in editors.
|
||||||
|
|
||||||
|
surface UserNavigation {
|
||||||
|
provides: SidebarItemClicked(entity_type, entity_id, click_type)
|
||||||
|
provides: SidebarCreateRequested(entity_type)
|
||||||
|
provides: SidebarDeleteRequested(entity_type, entity_id)
|
||||||
|
provides: DashboardPostClicked(post_id, click_type)
|
||||||
|
provides: PostSaved(post_id, updated_post)
|
||||||
|
provides: PostStatusTransitioned(post_id, old_status, new_status)
|
||||||
|
provides: PostDeletedFromEditor(post_id)
|
||||||
|
provides: MediaSavedFromEditor(media_id, updated_media)
|
||||||
|
provides: MediaDeletedFromEditor(media_id)
|
||||||
|
provides: SettingsRebuildCompleted(entity_type, new_data)
|
||||||
|
provides: TransientTabBeingReplaced(old_tab, new_tab)
|
||||||
|
provides: TabClosed(tab)
|
||||||
|
provides: AISuggestionRequested(entity_type, entity_id)
|
||||||
|
provides: PostAutoTranslateCompleted(post_id, language)
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Engine-level events emitted after backend operations complete.
|
||||||
|
-- These are NOT direct user actions — they fire as side-effects
|
||||||
|
-- of user operations processed by the engine layer.
|
||||||
|
|
||||||
|
surface Engine {
|
||||||
|
provides: PostCreated(post)
|
||||||
|
provides: PostUpdated(post, changes)
|
||||||
|
provides: PostPublished(post)
|
||||||
|
provides: PostDeleted(post)
|
||||||
|
provides: PostChangesDiscarded(post)
|
||||||
|
provides: MediaImported(media)
|
||||||
|
provides: MediaUpdated(media, changes)
|
||||||
|
provides: MediaFileReplaced(media, new_file)
|
||||||
|
provides: MediaDeleted(media)
|
||||||
|
provides: TemplateCreated(template)
|
||||||
|
provides: TemplateUpdated(template, changes)
|
||||||
|
provides: TemplatePublished(template)
|
||||||
|
provides: TemplateDeleted(template, force)
|
||||||
|
provides: TagDeleted(tag)
|
||||||
|
provides: TagRenamed(old_name, new_name)
|
||||||
|
provides: TagsMerged(source_tags, target_tag)
|
||||||
|
provides: ProjectMetadataUpdated(metadata)
|
||||||
|
provides: CategoryAdded(name)
|
||||||
|
provides: CategoryRemoved(name)
|
||||||
|
provides: PublishingPreferencesUpdated(prefs)
|
||||||
|
provides: PostTranslationUpserted(translation, source_post)
|
||||||
|
provides: MediaTranslationUpserted(translation, media)
|
||||||
|
provides: MediaTranslationDeleted(media, language)
|
||||||
|
}
|
||||||
|
|
||||||
-- ═══════════════════════════════════════════════════════════════
|
-- ═══════════════════════════════════════════════════════════════
|
||||||
-- PART 1: UI DATA FLOW MODEL
|
-- PART 1: UI DATA FLOW MODEL
|
||||||
-- ═══════════════════════════════════════════════════════════════
|
-- ═══════════════════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user