feat: added doc rendering
This commit is contained in:
474
DOCUMENTATION.md
Normal file
474
DOCUMENTATION.md
Normal file
@@ -0,0 +1,474 @@
|
|||||||
|
# bDS2 User Guide
|
||||||
|
|
||||||
|
## In this article
|
||||||
|
|
||||||
|
- [Who this guide is for](#who-this-guide-is-for)
|
||||||
|
- [How bDS2 works](#how-bds2-works)
|
||||||
|
- [Getting started](#getting-started)
|
||||||
|
- [Understanding the interface](#understanding-the-interface)
|
||||||
|
- [Working with posts](#working-with-posts)
|
||||||
|
- [Working with pages](#working-with-pages)
|
||||||
|
- [Working with media](#working-with-media)
|
||||||
|
- [Working with translations](#working-with-translations)
|
||||||
|
- [Using macros](#using-macros)
|
||||||
|
- [Using scripting](#using-scripting)
|
||||||
|
- [Using the AI assistant](#using-the-ai-assistant)
|
||||||
|
- [Organizing with tags](#organizing-with-tags)
|
||||||
|
- [Using blogmarks](#using-blogmarks)
|
||||||
|
- [Importing from WordPress (WXR)](#importing-from-wordpress-wxr)
|
||||||
|
- [Using Git (Source Control)](#using-git-source-control)
|
||||||
|
- [Configuring settings](#configuring-settings)
|
||||||
|
- [Checking and repairing metadata](#checking-and-repairing-metadata)
|
||||||
|
- [Managing templates](#managing-templates)
|
||||||
|
- [Generating and publishing](#generating-and-publishing)
|
||||||
|
- [Typical editorial workflows](#typical-editorial-workflows)
|
||||||
|
- [Working fully offline](#working-fully-offline)
|
||||||
|
- [Troubleshooting and recovery](#troubleshooting-and-recovery)
|
||||||
|
- [Team conventions](#team-conventions)
|
||||||
|
|
||||||
|
## Who this guide is for
|
||||||
|
|
||||||
|
This guide is for people who use bDS2 day to day to create, edit, organize, translate, generate, and publish blog content. It is written for editors, content managers, and project owners who need reliable guidance on what each part of the application does and how to use it safely.
|
||||||
|
|
||||||
|
If you need implementation notes, project architecture, or development setup, use the repository README. This guide stays focused on end-user operation and editorial decisions.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- bDS2 documentation should help with real editorial work, not only isolated clicks.
|
||||||
|
- Each chapter explains purpose first, then usage.
|
||||||
|
- Safe content handling and recoverability matter throughout the application.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How bDS2 works
|
||||||
|
|
||||||
|
bDS2 is a local-first writing and publishing workspace. You can draft, revise, structure, preview, and publish content on your machine without depending on constant internet access. Optional remote Git synchronization and AI-assisted workflows extend that model, but they do not replace it.
|
||||||
|
|
||||||
|
Three states matter in day-to-day work. A draft is your in-progress state. Publishing marks a local content state as published inside your project. A Git commit creates a recoverable snapshot that can be reviewed, synchronized, and restored. These actions are related, but they are not the same operation.
|
||||||
|
|
||||||
|
The recommended sequence remains simple: edit in draft, publish when the content is ready, then commit immediately. That is the safest pattern for protecting work and keeping project history understandable.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- bDS2 is designed for local reliability first.
|
||||||
|
- Publish and commit are different actions and both matter.
|
||||||
|
- The safe default lifecycle is: Draft -> Publish -> Commit.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
Before you begin editorial work, confirm that the project context is correct. Open bDS2 and select the right project. If this is a new project, create it and define its identity early, including project name and description.
|
||||||
|
|
||||||
|
Next, open Settings and verify the project data path and Public Base URL. The data path should match your backup strategy. The Public Base URL should be set early because sitemap and feed generation depend on it.
|
||||||
|
|
||||||
|
Finally, define language and author defaults. These defaults reduce repetitive edits and keep output consistent when multiple contributors work in the same project.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Set project identity, data location, and Public Base URL at the beginning.
|
||||||
|
- Configure language and author defaults before regular editing starts.
|
||||||
|
- Early setup decisions reduce later cleanup.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Understanding the interface
|
||||||
|
|
||||||
|
The bDS2 interface is organized around workflows rather than isolated forms. The Activity Bar on the left moves between major areas such as Posts, Pages, Media, Tags, Import, Source Control, and Settings. The Sidebar changes with the active area and helps with filtering, selection, and navigation. The Editor area is where most work happens and supports tabbed editing for content, configuration, and analysis views.
|
||||||
|
|
||||||
|
The bottom panel and status area matter during longer operations such as imports, rebuild actions, metadata scans, and media work. Toasts provide quick feedback. The Output panel provides deeper detail when something needs attention.
|
||||||
|
|
||||||
|
Tab behavior is optimized for quick scanning and focused editing. Single click often opens a transient tab. Double click or explicit actions pin a tab for longer work.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Use the Activity Bar for section-level context switching.
|
||||||
|
- Use the Sidebar for finding and narrowing content.
|
||||||
|
- Pin tabs when you move from inspection to editing.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Working with posts
|
||||||
|
|
||||||
|
The Posts section is for chronological content such as articles, notes, and recurring updates. In most editorial teams, Posts are the primary outward-facing stream.
|
||||||
|
|
||||||
|
A post combines title, body content, category, tags, excerpt, and status. Titles establish topic. Body content carries the narrative. Categories provide broad structure. Tags support finer discovery. Status should be used intentionally so collaborative workflows stay clear.
|
||||||
|
|
||||||
|
A reliable post workflow is: draft to completion, review structure and metadata, preview the result, publish when editorially ready, then commit immediately.
|
||||||
|
|
||||||
|
When you want help refining post metadata, use Quick Actions in the post editor and review AI suggestions for title, summary, and slug. Treat this as editorial assistance, not an automatic rewrite.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Use Posts for date-oriented and regularly updated content.
|
||||||
|
- Categories and tags serve different purposes: broad grouping versus precise discovery.
|
||||||
|
- Publish only when editorially ready, then commit right away.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Working with pages
|
||||||
|
|
||||||
|
Pages are for durable, non-chronological content such as About, Contact, legal notices, and other structural information. Use Pages when content should stay stable in navigation and should not be interpreted as part of a time-based feed.
|
||||||
|
|
||||||
|
Because pages are revisited over longer periods, naming consistency matters. Keep titles and slugs predictable, avoid unnecessary structural churn, and follow your project navigation conventions.
|
||||||
|
|
||||||
|
The working pattern is similar to posts: draft, review, preview, publish, commit. The difference is editorial intent: pages prioritize clarity and long-term maintainability over release cadence.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Use Pages for stable structural content.
|
||||||
|
- Keep titles and slugs consistent for maintainability.
|
||||||
|
- Apply the same safe lifecycle: Draft -> Publish -> Commit.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Working with media
|
||||||
|
|
||||||
|
The Media section is where you import, describe, and maintain assets used by posts and pages. It is not only a file list; it is also where accessibility and descriptive quality are enforced through metadata.
|
||||||
|
|
||||||
|
When importing media, add metadata while context is still fresh. Alt text should describe meaning for accessibility. Captions should support reader understanding. Media tags should help later retrieval and reuse.
|
||||||
|
|
||||||
|
You can also drag image files into the post editor or paste screenshots from the clipboard. bDS2 imports the image into the media library, links it to the current post, and inserts the Markdown image at the cursor position.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Media management includes metadata quality, not only file import.
|
||||||
|
- Add alt text and captions during import, not as a postponed task.
|
||||||
|
- Commit content and related media in the same change when possible.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Working with translations
|
||||||
|
|
||||||
|
bDS2 supports translating both posts and media metadata into multiple languages. Translations are stored separately from canonical content so localized variants do not drift into unrelated records.
|
||||||
|
|
||||||
|
### Post translations
|
||||||
|
|
||||||
|
Each post has a canonical language and can have translations for additional languages. Translations keep their own title, excerpt, and content, while canonical metadata such as category, tags, slug, and publish state stays centralized.
|
||||||
|
|
||||||
|
The post editor shows the current language, existing translations, and missing languages. Posts marked Do Not Translate are excluded from automatic translation and from alternate language trees during site generation.
|
||||||
|
|
||||||
|
Published translation body content follows the same filesystem rule as published posts: the body lives in the file, not in the database.
|
||||||
|
|
||||||
|
### Media translations
|
||||||
|
|
||||||
|
Media items can have translated title, alt text, and caption values per language. The binary asset stays shared; only descriptive text varies by language.
|
||||||
|
|
||||||
|
### Automatic translation cascade
|
||||||
|
|
||||||
|
When blog languages are configured, bDS2 can fill missing translations for posts and linked media. Automatic translation respects airplane mode and the configured AI runtime. If an automatic action cannot run in the current AI mode, bDS2 reports that through the UI instead of silently inventing a result.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Post translations store title, excerpt, and content separately from the canonical post.
|
||||||
|
- Media translations store translated descriptive text while the asset stays shared.
|
||||||
|
- Automatic translation keeps posts and linked media aligned across configured languages.
|
||||||
|
- Do Not Translate excludes content from multi-language workflows.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using macros
|
||||||
|
|
||||||
|
Macros let you insert dynamic content blocks directly inside Markdown by using `[[macro_name ...]]` syntax. bDS2 expands these macros during preview and generated output using local assets only.
|
||||||
|
|
||||||
|
Built-in macros include YouTube, Vimeo, gallery, photo archive, and tag cloud helpers. Use them when you want reusable rich blocks without dropping into raw HTML.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Macros are inserted directly in Markdown and expanded during preview and publishing.
|
||||||
|
- Use macro parameters to control behavior without leaving the editor.
|
||||||
|
- Built-in macros remain the first choice for common embedded content blocks.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using scripting
|
||||||
|
|
||||||
|
Scripts in bDS2 are Lua files stored in your project's `scripts/` directory. Published scripts are written as `.lua` files with frontmatter metadata, so they stay portable and Git-reviewable.
|
||||||
|
|
||||||
|
Each script has a Kind (`macro`, `transform`, or `utility`) and an Entrypoint. Utility and transform scripts typically default to `main`. Macro scripts default to `render`.
|
||||||
|
|
||||||
|
### Transform scripts
|
||||||
|
|
||||||
|
Transform scripts run during blogmark import to normalize or enrich incoming post data before the post is created. The entrypoint receives a post table and can optionally receive a context table.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function main(post, context)
|
||||||
|
local title = (post.title or ""):gsub("^%s+", ""):gsub("%s+$", "")
|
||||||
|
|
||||||
|
if title ~= "" and not title:match("^%[Clipped%]") then
|
||||||
|
post.title = "[Clipped] " .. title
|
||||||
|
end
|
||||||
|
|
||||||
|
post.categories = { "Inbox", "Research" }
|
||||||
|
return post
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`context.source` identifies the import source. `context.url` contains the original bookmarked URL when that information exists.
|
||||||
|
|
||||||
|
### Macro scripts
|
||||||
|
|
||||||
|
Macro scripts let you create custom `[[macro_name ...]]` blocks that expand during preview and generation. The entrypoint receives a context table and the current post table.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function render(context, post)
|
||||||
|
local params = context.params or {}
|
||||||
|
local title = (post and post.title) or "Unknown"
|
||||||
|
local label = params.label or ""
|
||||||
|
|
||||||
|
return {
|
||||||
|
html = "<p>" .. title .. ": " .. label .. "</p>"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Built-in macros take priority over custom Lua macros that reuse the same slug.
|
||||||
|
|
||||||
|
### API access
|
||||||
|
|
||||||
|
Lua scripts can call the application API through `bds`. The in-app API tab is rendered from the live Lua capability map, and [API.md](API.md) is generated from the same source.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local result = bds.posts.get("post-id")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Scripts in bDS2 are Lua files, not Python files.
|
||||||
|
- Published scripts are stored as `.lua` files with frontmatter metadata.
|
||||||
|
- `main` is the usual entrypoint for utility and transform scripts; `render` is the usual entrypoint for macros.
|
||||||
|
- The scripting API is documented with Lua examples and kept in sync with the live runtime.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using the AI assistant
|
||||||
|
|
||||||
|
The AI assistant is integrated into bDS2 to help with editorial tasks such as search, analysis, metadata suggestions, translation, and structured content inspection.
|
||||||
|
|
||||||
|
The assistant works on your project data. Depending on configuration, requests can run against the configured online endpoint or the airplane-mode endpoint. Automatic AI actions remain gated by airplane mode rules in the app, and bDS2 surfaces status through toasts and the Output area instead of silently bypassing that policy.
|
||||||
|
|
||||||
|
The assistant can present results as text, tables, cards, charts, metrics, lists, forms, and tabbed views. Ask plainly for the result you need, or request a specific presentation when that helps your workflow.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- The assistant works with your project content and metadata.
|
||||||
|
- AI configuration can be online or airplane-mode based, depending on your setup.
|
||||||
|
- Automatic AI actions respect airplane mode and report availability through the UI.
|
||||||
|
- Ask for a table, chart, list, or form when a specific shape is useful.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Organizing with tags
|
||||||
|
|
||||||
|
Tags are your precision taxonomy tool. Over time, even well-managed projects accumulate near-duplicate tags, naming inconsistencies, and labels that no longer help readers or editors. Use the Tags area to keep taxonomy useful.
|
||||||
|
|
||||||
|
After significant taxonomy cleanup, create a focused commit that captures the change clearly.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Tags improve discovery only if naming stays consistent.
|
||||||
|
- Merge and rename operations should be deliberate and reviewed.
|
||||||
|
- Commit taxonomy changes in focused snapshots.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using blogmarks
|
||||||
|
|
||||||
|
Blogmarks provide a quick way to save links from the browser directly into bDS2 as new posts. Generate the bookmarklet from Settings, add it to your browser bar, and click it when you want to capture a page into the current project.
|
||||||
|
|
||||||
|
Transform scripts can normalize incoming blogmark posts before creation. Use them for title cleanup, default tags, or source-specific formatting.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Blogmarks turn the browser into a one-click content capture tool.
|
||||||
|
- Generate the bookmarklet from Settings and add it to your browser bar.
|
||||||
|
- Use transform scripts to enrich incoming posts automatically.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Importing from WordPress (WXR)
|
||||||
|
|
||||||
|
The Import section supports structured migration from WordPress exports. Treat import as a staged workflow: analyze first, adjust mappings, then execute. For larger sites, iterative passes are usually safer than a single rigid import.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Treat WXR import as analyze, adjust, execute.
|
||||||
|
- Iterative passes are safer than one large import.
|
||||||
|
- Validate representative output before committing migrated content.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using Git (Source Control)
|
||||||
|
|
||||||
|
Source Control in bDS2 is the foundation for reliable recovery and collaboration. Publishing marks local editorial state, but Git commits provide durable history.
|
||||||
|
|
||||||
|
In a normal cycle, synchronize first, complete editorial changes, publish when ready, commit with a specific message, then push when you want to share the result.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Git provides recoverable history; publishing alone does not.
|
||||||
|
- A stable rhythm is: sync, edit, publish, commit, push.
|
||||||
|
- Specific commit messages improve teamwork and recovery.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuring settings
|
||||||
|
|
||||||
|
Settings define how the project behaves. Project settings control identity, paths, public URL context, and render languages. Editor settings shape day-to-day working defaults. AI settings are optional and should enhance, not define, your editorial workflow.
|
||||||
|
|
||||||
|
Maintenance actions such as rebuilds and diff scans are repair tools for specific situations, not part of routine editing.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Settings affect long-term consistency across the project.
|
||||||
|
- Optional integrations should not replace the core workflow.
|
||||||
|
- Rebuild actions are corrective tools, not daily habits.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checking and repairing metadata
|
||||||
|
|
||||||
|
Over time, metadata stored in the database and metadata stored on disk can drift apart, especially after external edits, merges, or file operations. The Metadata Diff tool detects these inconsistencies and lets you repair them without rebuilding everything.
|
||||||
|
|
||||||
|
The scan covers posts, media, scripts, and templates. Results are grouped by entity type, and field pills let you focus on one kind of difference at a time.
|
||||||
|
|
||||||
|
Use DB to File when the database is correct. Use File to DB when the filesystem is correct.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Metadata Diff compares database records against files on disk.
|
||||||
|
- Field pills help you bulk-repair one difference type at a time.
|
||||||
|
- Use it after external changes, not as part of routine editing.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Managing templates
|
||||||
|
|
||||||
|
Templates control the Liquid layout used when bDS2 generates HTML pages. Template kinds determine where they are used: `post`, `list`, `not-found`, and `partial`.
|
||||||
|
|
||||||
|
Templates are stored as files with frontmatter metadata in the project data directory, so they are portable and Git-reviewable.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Templates define the generated HTML layout.
|
||||||
|
- Four template kinds cover page, list, not-found, and reusable partial rendering.
|
||||||
|
- Templates are filesystem-backed and Git-friendly.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Generating and publishing
|
||||||
|
|
||||||
|
Publishing in bDS2 is a staged process: publish content locally, generate or validate-and-apply site changes, commit the result, then deploy when ready.
|
||||||
|
|
||||||
|
Full generation builds the entire static site. Site validation detects missing, extra, and updated routes so bDS2 can re-render only what changed. This is the practical incremental workflow for most daily editorial changes.
|
||||||
|
|
||||||
|
When blog languages are configured, generation produces language-aware route trees, per-language feeds, and alternate language metadata.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Full generation produces the complete site.
|
||||||
|
- Validate and Apply is the efficient daily workflow for incremental publishing.
|
||||||
|
- Public Base URL must be set before generation.
|
||||||
|
- Commit generated output before deploying for recoverability.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Typical editorial workflows
|
||||||
|
|
||||||
|
Short link posts benefit from a lightweight workflow: create, add concise context, classify, preview once, publish, commit. Long-form articles benefit from a fuller cycle: draft thoroughly, add media, review metadata, preview carefully, publish, commit content and media together.
|
||||||
|
|
||||||
|
Across both patterns, the safety baseline stays the same: Draft -> Publish -> Commit.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Use a lightweight workflow for short notes and links.
|
||||||
|
- Use a fuller workflow for long-form content with media.
|
||||||
|
- Keep the same safety baseline in both cases.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Working fully offline
|
||||||
|
|
||||||
|
bDS2 is designed so core editorial work can continue without network access. You can create and revise content, manage metadata, preview locally, and publish within local project state while offline.
|
||||||
|
|
||||||
|
When AI is involved, airplane mode determines which automatic actions are allowed and which endpoint class is used. Keep local commits frequent even when you are not pushing to a remote.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Core editing and publishing workflows work offline.
|
||||||
|
- Local commits still matter when no remote is available.
|
||||||
|
- Reconnect and synchronize in a controlled order.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting and recovery
|
||||||
|
|
||||||
|
If content looks correct locally but is missing for collaborators, the usual cause is that changes were published but not committed and pushed. Check repository status, create a commit, then push to the expected remote.
|
||||||
|
|
||||||
|
If content lists or references become inconsistent after manual file changes, start with Metadata Diff. If broader inconsistency remains, use rebuild tools to realign database and filesystem state.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Most missing remote content issues are commit or push gaps.
|
||||||
|
- Metadata Diff is the first repair tool after external file changes.
|
||||||
|
- Frequent meaningful commits are the strongest safety net.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Team conventions
|
||||||
|
|
||||||
|
Shared conventions reduce ambiguity and merge friction. Teams should agree on category definitions, tag naming rules, publish-readiness criteria, and commit message patterns.
|
||||||
|
|
||||||
|
A practical minimum rule is simple: any content considered published should be committed promptly.
|
||||||
|
|
||||||
|
### Key takeaways
|
||||||
|
|
||||||
|
- Explicit conventions improve speed and reduce avoidable conflict.
|
||||||
|
- Start with a small rule set and enforce it consistently.
|
||||||
|
- Minimum standard: published content should be committed promptly.
|
||||||
|
|
||||||
|
[↑ Back to In this article](#in-this-article)
|
||||||
12
lib/bds/desktop/external_links.ex
Normal file
12
lib/bds/desktop/external_links.ex
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
defmodule BDS.Desktop.ExternalLinks do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
@github_url "https://github.com/rfc1437/bDS2"
|
||||||
|
@github_issues_url "#{@github_url}/issues"
|
||||||
|
|
||||||
|
@spec github_url() :: String.t()
|
||||||
|
def github_url, do: @github_url
|
||||||
|
|
||||||
|
@spec github_issues_url() :: String.t()
|
||||||
|
def github_issues_url, do: @github_issues_url
|
||||||
|
end
|
||||||
@@ -2,7 +2,7 @@ defmodule BDS.Desktop.MenuBar do
|
|||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
use BDS.Desktop.MenuCompat
|
use BDS.Desktop.MenuCompat
|
||||||
alias BDS.Desktop.{ShellData, Shutdown, UILocale}
|
alias BDS.Desktop.{ExternalLinks, ShellData, Shutdown, UILocale}
|
||||||
alias BDS.UI.Commands
|
alias BDS.UI.Commands
|
||||||
alias BDS.UI.MenuBar, as: ShellMenuBar
|
alias BDS.UI.MenuBar, as: ShellMenuBar
|
||||||
alias Desktop.OS
|
alias Desktop.OS
|
||||||
@@ -59,7 +59,7 @@ defmodule BDS.Desktop.MenuBar do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("view_on_github", menu) do
|
def handle_event("view_on_github", menu) do
|
||||||
OS.launch_default_browser("https://github.com/rfc1437/bDS")
|
OS.launch_default_browser(ExternalLinks.github_url())
|
||||||
{:noreply, menu}
|
{:noreply, menu}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ defmodule BDS.Desktop.MenuBar do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("report_issue", menu) do
|
def handle_event("report_issue", menu) do
|
||||||
OS.launch_default_browser("https://github.com/rfc1437/bDS/issues")
|
OS.launch_default_browser(ExternalLinks.github_issues_url())
|
||||||
{:noreply, menu}
|
{:noreply, menu}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
|
|
||||||
alias BDS.{AI, BoundedAtoms}
|
alias BDS.{AI, BoundedAtoms}
|
||||||
alias BDS.CliSync.Watcher
|
alias BDS.CliSync.Watcher
|
||||||
alias BDS.Desktop.{FolderPicker, ShellData, UILocale}
|
alias BDS.Desktop.{ExternalLinks, FolderPicker, ShellData, UILocale}
|
||||||
|
|
||||||
alias BDS.Desktop.ShellLive.{
|
alias BDS.Desktop.ShellLive.{
|
||||||
Bridges,
|
Bridges,
|
||||||
@@ -786,12 +786,12 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp handle_socket_menu_action(socket, :view_on_github) do
|
defp handle_socket_menu_action(socket, :view_on_github) do
|
||||||
OS.launch_default_browser("https://github.com/rfc1437/bDS")
|
OS.launch_default_browser(ExternalLinks.github_url())
|
||||||
socket
|
socket
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_socket_menu_action(socket, :report_issue) do
|
defp handle_socket_menu_action(socket, :report_issue) do
|
||||||
OS.launch_default_browser("https://github.com/rfc1437/bDS/issues")
|
OS.launch_default_browser(ExternalLinks.github_issues_url())
|
||||||
socket
|
socket
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -421,7 +421,7 @@
|
|||||||
<% @current_tab.type == :import -> %>
|
<% @current_tab.type == :import -> %>
|
||||||
<.live_component module={ImportEditor} id={"import-editor-#{@current_tab.id}"} current_tab={@current_tab} offline_mode={@offline_mode} project_id={@projects.active_project_id} />
|
<.live_component module={ImportEditor} id={"import-editor-#{@current_tab.id}"} current_tab={@current_tab} offline_mode={@offline_mode} project_id={@projects.active_project_id} />
|
||||||
|
|
||||||
<% @current_tab.type in [:site_validation, :metadata_diff, :translation_validation, :find_duplicates, :git_diff] -> %>
|
<% @current_tab.type in [:site_validation, :metadata_diff, :translation_validation, :find_duplicates, :git_diff, :documentation, :api_documentation] -> %>
|
||||||
<.live_component module={MiscEditor} id={"misc-editor-#{@current_tab.type}-#{@current_tab.id}"} current_tab={@current_tab} tab_meta={@tab_meta} project_id={@projects.active_project_id} />
|
<.live_component module={MiscEditor} id={"misc-editor-#{@current_tab.type}-#{@current_tab.id}"} current_tab={@current_tab} tab_meta={@tab_meta} project_id={@projects.active_project_id} />
|
||||||
|
|
||||||
<% true -> %>
|
<% true -> %>
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
|||||||
|
|
||||||
use Phoenix.LiveComponent
|
use Phoenix.LiveComponent
|
||||||
|
|
||||||
|
import Phoenix.HTML, only: [raw: 1]
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias BDS.{Embeddings, Generation, Git, Posts, Repo}
|
alias BDS.{Embeddings, Generation, Git, HelpDocs, Posts, Repo}
|
||||||
alias BDS.MapUtils
|
alias BDS.MapUtils
|
||||||
alias BDS.Settings.Setting
|
alias BDS.Settings.Setting
|
||||||
use Gettext, backend: BDS.Gettext
|
use Gettext, backend: BDS.Gettext
|
||||||
@@ -13,6 +14,8 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
|||||||
embed_templates("misc_editor_html/*")
|
embed_templates("misc_editor_html/*")
|
||||||
|
|
||||||
@misc_routes [
|
@misc_routes [
|
||||||
|
:documentation,
|
||||||
|
:api_documentation,
|
||||||
:site_validation,
|
:site_validation,
|
||||||
:metadata_diff,
|
:metadata_diff,
|
||||||
:translation_validation,
|
:translation_validation,
|
||||||
@@ -264,12 +267,28 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
|||||||
# ── Public helper functions (used by template) ─────────────────────────────
|
# ── Public helper functions (used by template) ─────────────────────────────
|
||||||
|
|
||||||
@spec misc_class(atom()) :: String.t()
|
@spec misc_class(atom()) :: String.t()
|
||||||
|
def misc_class(:documentation), do: "help-doc-view"
|
||||||
|
def misc_class(:api_documentation), do: "help-doc-view"
|
||||||
def misc_class(:site_validation), do: "site-validation-view"
|
def misc_class(:site_validation), do: "site-validation-view"
|
||||||
def misc_class(:metadata_diff), do: "metadata-diff-view"
|
def misc_class(:metadata_diff), do: "metadata-diff-view"
|
||||||
def misc_class(:translation_validation), do: "translation-validation-view"
|
def misc_class(:translation_validation), do: "translation-validation-view"
|
||||||
def misc_class(:find_duplicates), do: "duplicates-view"
|
def misc_class(:find_duplicates), do: "duplicates-view"
|
||||||
def misc_class(:git_diff), do: "git-diff-view"
|
def misc_class(:git_diff), do: "git-diff-view"
|
||||||
|
|
||||||
|
@spec markdown_html(String.t()) :: Phoenix.HTML.safe()
|
||||||
|
def markdown_html(content) do
|
||||||
|
html =
|
||||||
|
case Earmark.as_html(content || "", escape: true) do
|
||||||
|
{:ok, rendered, _messages} -> rendered
|
||||||
|
{:error, rendered, _messages} -> rendered
|
||||||
|
end
|
||||||
|
|
||||||
|
raw(html)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec refreshable?(atom()) :: boolean()
|
||||||
|
def refreshable?(kind), do: kind not in [:documentation, :api_documentation]
|
||||||
|
|
||||||
@spec summary_items(map()) :: [{String.t(), any()}]
|
@spec summary_items(map()) :: [{String.t(), any()}]
|
||||||
def summary_items(%{summary: summary}) when is_map(summary), do: Enum.to_list(summary)
|
def summary_items(%{summary: summary}) when is_map(summary), do: Enum.to_list(summary)
|
||||||
def summary_items(_misc), do: []
|
def summary_items(_misc), do: []
|
||||||
@@ -369,6 +388,8 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
|||||||
payload = Map.get(meta, :payload, %{})
|
payload = Map.get(meta, :payload, %{})
|
||||||
|
|
||||||
case type do
|
case type do
|
||||||
|
:documentation -> build_help_doc(type, meta)
|
||||||
|
:api_documentation -> build_help_doc(type, meta)
|
||||||
:site_validation -> build_site_validation(meta, payload)
|
:site_validation -> build_site_validation(meta, payload)
|
||||||
:metadata_diff -> build_metadata_diff(assigns, meta, payload)
|
:metadata_diff -> build_metadata_diff(assigns, meta, payload)
|
||||||
:translation_validation -> build_translation_validation(meta, payload)
|
:translation_validation -> build_translation_validation(meta, payload)
|
||||||
@@ -379,6 +400,18 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
|||||||
|
|
||||||
defp do_build(_assigns), do: nil
|
defp do_build(_assigns), do: nil
|
||||||
|
|
||||||
|
defp build_help_doc(type, meta) do
|
||||||
|
help_doc = HelpDocs.fetch(type)
|
||||||
|
|
||||||
|
%{
|
||||||
|
kind: type,
|
||||||
|
title: Map.get(meta, :title, help_doc.title),
|
||||||
|
subtitle: Map.get(meta, :subtitle, help_doc.subtitle),
|
||||||
|
summary: %{},
|
||||||
|
markdown: help_doc.markdown
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
defp build_site_validation(meta, payload) do
|
defp build_site_validation(meta, payload) do
|
||||||
summary = Map.get(payload, :summary, %{})
|
summary = Map.get(payload, :summary, %{})
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
<p><%= @misc_editor.subtitle %></p>
|
<p><%= @misc_editor.subtitle %></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="misc-editor-actions">
|
<div class="misc-editor-actions">
|
||||||
|
<%= if refreshable?(@misc_editor.kind) do %>
|
||||||
<button class="secondary" type="button" phx-click="rerun_misc_editor" phx-target={@myself}><%= dgettext("ui", "Refresh") %></button>
|
<button class="secondary" type="button" phx-click="rerun_misc_editor" phx-target={@myself}><%= dgettext("ui", "Refresh") %></button>
|
||||||
|
<% end %>
|
||||||
<%= if @misc_editor.kind == :site_validation do %>
|
<%= if @misc_editor.kind == :site_validation do %>
|
||||||
<button class="primary" type="button" phx-click="apply_site_validation" phx-target={@myself} disabled={Enum.empty?(@misc_editor.missing_url_paths) and Enum.empty?(@misc_editor.extra_url_paths) and Enum.empty?(@misc_editor.updated_post_url_paths)}><%= dgettext("ui", "Apply") %></button>
|
<button class="primary" type="button" phx-click="apply_site_validation" phx-target={@myself} disabled={Enum.empty?(@misc_editor.missing_url_paths) and Enum.empty?(@misc_editor.extra_url_paths) and Enum.empty?(@misc_editor.updated_post_url_paths)}><%= dgettext("ui", "Apply") %></button>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -23,6 +25,16 @@
|
|||||||
|
|
||||||
<div class="misc-editor-content">
|
<div class="misc-editor-content">
|
||||||
<%= case @misc_editor.kind do %>
|
<%= case @misc_editor.kind do %>
|
||||||
|
<% :documentation -> %>
|
||||||
|
<article class="misc-card help-doc-markdown" data-testid="help-documentation">
|
||||||
|
<%= markdown_html(@misc_editor.markdown) %>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<% :api_documentation -> %>
|
||||||
|
<article class="misc-card help-doc-markdown" data-testid="help-api-documentation">
|
||||||
|
<%= markdown_html(@misc_editor.markdown) %>
|
||||||
|
</article>
|
||||||
|
|
||||||
<% :site_validation -> %>
|
<% :site_validation -> %>
|
||||||
<div class="misc-columns">
|
<div class="misc-columns">
|
||||||
<section class="misc-card"><h3><%= dgettext("ui", "Missing URLs") %></h3><%= if Enum.empty?(@misc_editor.missing_url_paths) do %><p><%= dgettext("ui", "None found") %></p><% end %><ul><%= for path <- @misc_editor.missing_url_paths do %><li><%= path %></li><% end %></ul></section>
|
<section class="misc-card"><h3><%= dgettext("ui", "Missing URLs") %></h3><%= if Enum.empty?(@misc_editor.missing_url_paths) do %><p><%= dgettext("ui", "None found") %></p><% end %><ul><%= for path <- @misc_editor.missing_url_paths do %><li><%= path %></li><% end %></ul></section>
|
||||||
|
|||||||
54
lib/bds/help_docs.ex
Normal file
54
lib/bds/help_docs.ex
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
defmodule BDS.HelpDocs do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
alias BDS.Scripting.ApiDocs
|
||||||
|
use Gettext, backend: BDS.Gettext
|
||||||
|
|
||||||
|
@documentation_path Path.expand("../../DOCUMENTATION.md", __DIR__)
|
||||||
|
|
||||||
|
@type help_kind :: :documentation | :api_documentation
|
||||||
|
|
||||||
|
@spec fetch(help_kind()) :: %{title: String.t(), subtitle: String.t(), markdown: String.t()}
|
||||||
|
def fetch(:documentation) do
|
||||||
|
%{
|
||||||
|
title: dgettext("ui", "Documentation"),
|
||||||
|
subtitle:
|
||||||
|
dgettext(
|
||||||
|
"ui",
|
||||||
|
"End-user guidance for editorial workflows, media, templates, translation, and publishing in bDS2."
|
||||||
|
),
|
||||||
|
markdown: documentation_markdown()
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch(:api_documentation) do
|
||||||
|
%{
|
||||||
|
title: dgettext("ui", "API"),
|
||||||
|
subtitle:
|
||||||
|
dgettext(
|
||||||
|
"ui",
|
||||||
|
"Current Lua scripting contract rendered from the live bDS2 runtime."
|
||||||
|
),
|
||||||
|
markdown: ApiDocs.render()
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec documentation_markdown() :: String.t()
|
||||||
|
def documentation_markdown do
|
||||||
|
case File.read(@documentation_path) do
|
||||||
|
{:ok, contents} -> contents
|
||||||
|
{:error, _reason} -> fallback_documentation()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fallback_documentation do
|
||||||
|
[
|
||||||
|
"# bDS2 User Guide",
|
||||||
|
"",
|
||||||
|
"The project-level DOCUMENTATION.md file is missing.",
|
||||||
|
"",
|
||||||
|
"Use the repository root documentation file to provide the in-app user guide."
|
||||||
|
]
|
||||||
|
|> Enum.join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1035,6 +1035,42 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert html =~ ~s(class="tab active transient")
|
assert html =~ ~s(class="tab active transient")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "workbench session restore renders documentation tab content" do
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
session_payload =
|
||||||
|
Workbench.new()
|
||||||
|
|> Workbench.open_tab(:documentation, "documentation", :pin)
|
||||||
|
|> Session.serialize()
|
||||||
|
|
||||||
|
_html = render_hook(view, "restore_workbench_session", %{"session" => session_payload})
|
||||||
|
|
||||||
|
assert has_element?(view, ".tab[data-tab-type='documentation'] .tab-title", "Documentation")
|
||||||
|
assert has_element?(view, "[data-testid='help-documentation']")
|
||||||
|
assert render(view) =~ "bDS2 User Guide"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "workbench session restore renders api documentation tab content" do
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
session_payload =
|
||||||
|
Workbench.new()
|
||||||
|
|> Workbench.open_tab(:api_documentation, "api_documentation", :pin)
|
||||||
|
|> Session.serialize()
|
||||||
|
|
||||||
|
_html = render_hook(view, "restore_workbench_session", %{"session" => session_payload})
|
||||||
|
|
||||||
|
assert has_element?(
|
||||||
|
view,
|
||||||
|
".tab[data-tab-type='api_documentation'] .tab-title",
|
||||||
|
"Api Documentation"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert has_element?(view, "[data-testid='help-api-documentation']")
|
||||||
|
assert render(view) =~ "API Documentation"
|
||||||
|
assert render(view) =~ "local result = bds.posts.get"
|
||||||
|
end
|
||||||
|
|
||||||
test "workbench session restore rehydrates chat tab titles from stored conversations" do
|
test "workbench session restore rehydrates chat tab titles from stored conversations" do
|
||||||
assert {:ok, conversation} = AI.start_chat(%{title: "Editorial Plan"})
|
assert {:ok, conversation} = AI.start_chat(%{title: "Editorial Plan"})
|
||||||
|
|
||||||
|
|||||||
@@ -169,6 +169,11 @@ defmodule BDS.DesktopTest do
|
|||||||
assert_receive :quit_requested
|
assert_receive :quit_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "desktop external links point at the bDS2 GitHub project and issue tracker" do
|
||||||
|
assert BDS.Desktop.ExternalLinks.github_url() == "https://github.com/rfc1437/bDS2"
|
||||||
|
assert BDS.Desktop.ExternalLinks.github_issues_url() == "https://github.com/rfc1437/bDS2/issues"
|
||||||
|
end
|
||||||
|
|
||||||
test "icon menu quit requests app-owned shutdown" do
|
test "icon menu quit requests app-owned shutdown" do
|
||||||
previous_module = Application.get_env(:bds, :desktop_shutdown_module)
|
previous_module = Application.get_env(:bds, :desktop_shutdown_module)
|
||||||
previous_pid = Application.get_env(:bds, :desktop_shutdown_test_pid)
|
previous_pid = Application.get_env(:bds, :desktop_shutdown_test_pid)
|
||||||
|
|||||||
Reference in New Issue
Block a user