B2-1..B2-9: distill minor code behaviors into specs (post/project/template/media_processing/generation/dashboard)
This commit is contained in:
@@ -148,6 +148,16 @@ surface DashboardSurface {
|
||||
-- Single-click: preview tab. Double-click: pin tab.
|
||||
}
|
||||
|
||||
rule ComputeDashboardData {
|
||||
when: DashboardRequested(project)
|
||||
-- stats: post counts grouped by status (total = sum of all statuses),
|
||||
-- plus media/image counts, total media size, tag and category counts.
|
||||
-- timeline: posts grouped by (year, month) of created_at, newest first,
|
||||
-- limited to the most recent config.dashboard_timeline_months with data.
|
||||
-- tag_cloud / category_cloud / recent_posts populated per their guarantees.
|
||||
ensures: Dashboard
|
||||
}
|
||||
|
||||
-- ─── Menu editor view ────────────────────────────────────────
|
||||
|
||||
-- Visual editor for the OPML navigation menu (meta/menu.opml).
|
||||
|
||||
@@ -24,7 +24,15 @@ surface GenerationRuntimeSurface {
|
||||
|
||||
provides:
|
||||
PageRenderRequested(template, context)
|
||||
GenerationProgressReported(current, total, label)
|
||||
GenerateSiteCompleted(generation)
|
||||
|
||||
@guarantee ProgressReporting
|
||||
-- Generation, reindex, and site-validation run as background tasks and
|
||||
-- emit count-based progress (current/total + label, e.g. "Processing
|
||||
-- N of M posts") via the task progress channel (see task.allium
|
||||
-- ReportProgress / ProgressThrottled). Multi-phase work (validation)
|
||||
-- maps each phase onto a fixed fraction of the 0.0..1.0 bar.
|
||||
}
|
||||
|
||||
value GenerationSection {
|
||||
|
||||
@@ -18,6 +18,7 @@ surface MediaProcessingControlSurface {
|
||||
TagMediaRequested(media, tags)
|
||||
DeleteMediaRequested(media)
|
||||
ValidateMediaRequested(project)
|
||||
RegenerateMissingThumbnailsRequested(project)
|
||||
}
|
||||
|
||||
surface MediaProcessingRuntimeSurface {
|
||||
@@ -131,6 +132,18 @@ rule GenerateThumbnails {
|
||||
)
|
||||
}
|
||||
|
||||
rule RegenerateMissingThumbnails {
|
||||
when: RegenerateMissingThumbnailsRequested(project)
|
||||
-- Maintenance sweep over the project's raster images (images, excluding SVG),
|
||||
-- oldest first. For each, any thumbnail file absent from disk is regenerated
|
||||
-- from the original binary; images with a full set are skipped.
|
||||
-- Runs as a background task reporting progress (current/total).
|
||||
-- Returns counts: processed, generated, failed.
|
||||
for media in project.media where is_image(media.mime_type) and not is_svg(media.mime_type):
|
||||
if any_thumbnail_missing(media):
|
||||
ensures: ThumbnailsRegenerated(media)
|
||||
}
|
||||
|
||||
-- Thumbnail generation algorithm
|
||||
value ThumbnailGeneration {
|
||||
-- 1. Load source image
|
||||
|
||||
@@ -56,6 +56,8 @@ surface PostControlSurface {
|
||||
DeletePostRequested(post)
|
||||
ArchivePostRequested(post)
|
||||
DiscardPostChangesRequested(post)
|
||||
SyncPostFromFileRequested(post)
|
||||
ImportOrphanPostFileRequested(project, relative_path)
|
||||
}
|
||||
|
||||
surface PostFilePathSurface {
|
||||
@@ -122,6 +124,10 @@ entity Post {
|
||||
-- Slug changes only allowed before first publish
|
||||
content_location: if status = published: file_path else: content
|
||||
-- Published: body in filesystem. Draft: body in DB field.
|
||||
editor_body: if content != null: content else: read_markdown_body(file_path)
|
||||
-- Resolver used by editors: prefer the in-DB draft content, else read
|
||||
-- the markdown body from the post's file. Empty string when neither
|
||||
-- exists. The same resolver applies to PostTranslation records.
|
||||
|
||||
transitions status {
|
||||
draft -> published
|
||||
@@ -228,6 +234,25 @@ rule ArchivePost {
|
||||
ensures: post.status = archived
|
||||
}
|
||||
|
||||
rule SyncPostFromFile {
|
||||
when: SyncPostFromFileRequested(post)
|
||||
requires: post.file_path != ""
|
||||
-- Single-post reimport: re-reads the post's own .md file and upserts the DB
|
||||
-- record from it (the filesystem is treated as truth for that one post).
|
||||
-- Re-syncs the post's link graph. Errors out if the file is missing.
|
||||
ensures: PostFieldsUpdated(post, parse_post_file(post.file_path))
|
||||
ensures: PostLinksUpdated(post)
|
||||
}
|
||||
|
||||
rule ImportOrphanPostFile {
|
||||
when: ImportOrphanPostFileRequested(project, relative_path)
|
||||
-- Imports a .md file that exists on disk but has no DB record (an orphan,
|
||||
-- e.g. surfaced by RunMetadataDiff). Rejects non-markdown / missing files.
|
||||
ensures:
|
||||
let new_post = Post.created(parse_post_file(relative_path))
|
||||
SearchIndexUpdated(new_post)
|
||||
}
|
||||
|
||||
rule DiscardPostChanges {
|
||||
when: DiscardPostChangesRequested(post)
|
||||
requires: post.file_path != ""
|
||||
|
||||
@@ -55,6 +55,10 @@ entity Project {
|
||||
-- Linux: $XDG_CONFIG_HOME/bds (default ~/.config/bds)
|
||||
-- Windows: %APPDATA%\\bds
|
||||
-- See PrivateArtifactsLiveInOsAppDir.
|
||||
cache_dir: private_dir + "/projects/" + id
|
||||
-- Per-project subtree of private_dir for that project's regenerable
|
||||
-- artifacts (embeddings index + sidecar). Overridable in tests via the
|
||||
-- :project_cache_root setting; never the repo or the project folder.
|
||||
}
|
||||
|
||||
surface ProjectSurface {
|
||||
|
||||
@@ -175,6 +175,12 @@ rule RebuildTemplatesFromFiles {
|
||||
let parsed = parse_template_file(file)
|
||||
ensures: Template.created(parsed)
|
||||
-- or updated if slug already exists
|
||||
-- Prune stale published templates: a published Template whose file_path is
|
||||
-- neither among the scanned files nor present on disk is deleted, clearing
|
||||
-- its references first (posts' template_slug, tags' post_template_slug).
|
||||
for template in project.templates where status = published and file_path != "":
|
||||
if template.file_path not in scanned_files and not file_exists(template.file_path):
|
||||
ensures: not exists template
|
||||
}
|
||||
|
||||
-- Exact Liquid subset required (distilled from bundled starter templates)
|
||||
|
||||
Reference in New Issue
Block a user