Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3ad2c95
Refactor document content rendering in workspace screen
albert-gee Aug 21, 2026
a893ef6
Add multi-select sidebar actions
albert-gee Aug 21, 2026
e0ea98b
Add professional document workflows
albert-gee Aug 21, 2026
0937f16
Add offline MathJax document support
albert-gee Aug 21, 2026
aa42600
Fix multi-document safety and recovery
albert-gee Aug 21, 2026
91670c7
Fix math editing and rendering regressions
albert-gee Aug 21, 2026
63ee144
Remove AI privacy notice
albert-gee Aug 21, 2026
f517c7e
Support multiple AI providers
albert-gee Aug 22, 2026
28451ec
Harden workspace persistence
albert-gee Aug 22, 2026
97c4cc1
Preserve WYSIWYG math structure
albert-gee Aug 22, 2026
38fe7da
Filter AI provider choices
albert-gee Aug 22, 2026
a2a5a6b
Add per-request AI model selection
albert-gee Aug 22, 2026
fb9b321
Compact document format status
albert-gee Aug 22, 2026
85fa566
Persist recovery before session state
albert-gee Aug 22, 2026
ee79a31
Add Writerside video support
albert-gee Aug 22, 2026
38f03ad
Preserve inline math source integrity
albert-gee Aug 22, 2026
b3532a6
Embed Writerside video players
albert-gee Aug 22, 2026
c088bb1
Remove document format badges
albert-gee Aug 22, 2026
3c2253a
Add Writerside admonitions
albert-gee Aug 22, 2026
f75150d
Add Writerside collapsible elements
albert-gee Aug 22, 2026
e708534
Add Writerside math and diagram syntax
albert-gee Aug 22, 2026
4b93f38
Fix tab safety and math round trips
albert-gee Aug 22, 2026
d046d3b
Fix YouTube video playback
albert-gee Aug 22, 2026
ce06a91
Show hosted video posters
albert-gee Aug 22, 2026
fb3e797
Fix workspace persistence races
albert-gee Aug 22, 2026
2e89a3a
Fix welcome navigation restore loop
albert-gee Aug 22, 2026
7821587
Polish new projects and text selection
albert-gee Aug 22, 2026
ea4c83d
Fix document write lifecycle races
albert-gee Aug 22, 2026
1baf9b0
Remove Docker from Writerside PDF export
albert-gee Aug 22, 2026
52bd4ad
Convert BusyMarkGroupedTextEntry to StatefulWidget and enhance text s…
albert-gee Aug 23, 2026
2679c2f
Enhance link handling in WYSIWYG editor by adding support for existin…
albert-gee Aug 23, 2026
0717467
Update Snap confinement to classic and enhance documentation for Git …
albert-gee Aug 24, 2026
5c0742d
Replace `ScaffoldMessenger` with `BusyMarkToastOverlay` throughout th…
albert-gee Aug 24, 2026
27d0de1
Bump version to 0.3.1 and update release metadata across relevant files
albert-gee Aug 24, 2026
b7d7887
Prepare v0.3.1 release
albert-gee Aug 28, 2026
0514bb6
Downgrade dependencies in `pubspec.lock` to resolve version conflicts…
albert-gee Aug 28, 2026
a4dda7a
Align dependency lock with Flutter 3.47
albert-gee Aug 28, 2026
7306568
Format tests with Dart 3.13
albert-gee Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/flutter-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ jobs:
sudo apt-get install -y weston xvfb

- name: Install strict Snap
run: sudo snap install --dangerous "${{ steps.snapcraft.outputs.snap }}"
run: >-
sudo snap install --dangerous
"${{ steps.snapcraft.outputs.snap }}"

- name: Exercise strict Snap visualization and PDF paths under X11
run: |
Expand Down
49 changes: 39 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ projects.
- Edit and save local files.
- Read rendered Markdown without editing it.
- Render Mermaid, PlantUML, D2, and fenced OpenAPI content locally and offline.
- Typeset inline and display mathematics locally and offline with bundled
MathJax, including Writerside math forms and vector PDF output.
- Edit Markdown with free-form AI instructions, an explicit change target, and
explicitly selected context through Ollama, OpenAI, or Gemini, with
diff-before-apply review.
- Export Markdown documents as accessible, tagged PDF files.
- Navigate project files, table of contents, and document outline.
- Review, stage, commit, branch, synchronize, and inspect history in Git-backed
documentation workspaces using the user's normal Git configuration and tools.
- Run basic diagnostics.
- Reopen recent workspaces.
- Use native Linux desktop chrome with a GTK headerbar.
Expand Down Expand Up @@ -84,6 +88,16 @@ build settings, Markdown import, status, ID refactoring, instance groups,
conditional and reusable TOC sections, and cross-instance topic references.
See [Writerside instances](docs/writerside-instances.md) for behavior, safety
rules, an openable example, and the authoritative JetBrains references.
Writerside `<video>` elements are recognized in Markdown and XML topics, with
local preview images plus HTTPS YouTube and Vimeo sources. See
[Writerside videos](docs/videos.md) for syntax and preview/export behavior.
Writerside tip, note, warning, and quote blocks render as native document
callouts and can be inserted from the WYSIWYG toolbar. See
[Writerside admonitions](docs/admonitions.md) for supported syntax.
Writerside chapters, procedures, code blocks, and definition lists support
documented collapsed and expanded states in Preview and WYSIWYG views. See
[Writerside collapsible elements](docs/collapsible-elements.md) for syntax and
the ordinary-Markdown boundary.

Folder workspaces show all files and directories, including hidden project
files such as `.gitignore`. Unsupported and binary files remain visible but are
Expand All @@ -103,6 +117,12 @@ compiler; users do not install or configure a separate program. Local PNG,
JPEG, GIF, and safe SVG images are included. Remote images are deliberately not
downloaded during export and are represented by their alternative text.

Inline and display equations use the same bundled MathJax semantics in preview
and PDF export. Safe generated equations remain self-contained vector SVG, with
inline baseline metrics carried into Typst. See [mathematical expressions](docs/math.md)
for supported Markdown and Writerside forms, the scientific TeX package profile,
editing behavior, and offline security boundaries.

Mermaid and PlantUML fences are exported as vector diagrams. D2 uses normalized
SVG where possible and a local high-resolution raster fallback for browser-only
labels. OpenAPI fences become static, selectable API reference content. Failed
Expand All @@ -114,16 +134,13 @@ examples are in [demo/visualizations.md](demo/visualizations.md),
[demo/openapi-local-reference.md](demo/openapi-local-reference.md), and
[demo/plantuml-conformance.md](demo/plantuml-conformance.md).

Writerside PDF export builds one selected output instance with JetBrains'
official, versioned Writerside builder image. It supports generated settings or
an existing project `PDF.xml`, including orientation, keymap, cover page,
header, footer, and table-of-contents title. Docker is required, the large image
is downloaded only after confirmation, project sources stay read-only, and
builder network access is disabled unless explicitly enabled. See
[Writerside PDF export](docs/writerside-pdf-export.md) for setup, customization,
security boundaries, Snap limitations, and the authoritative JetBrains
references. An exportable configuration is included in
[demo/writerside-instances](demo/writerside-instances).
Writerside PDF export builds one selected output instance with BusyMark's
bundled native Typst, MathJax, and diagram pipeline. It follows the resolved
instance TOC and supports Writerside Markdown and `.topic` XML without Docker,
a Writerside installation, a daemon, or a network connection. See
[Writerside PDF export](docs/writerside-pdf-export.md) for supported layout
options, security boundaries, and native-export scope. An exportable example is
included in [demo/writerside-instances](demo/writerside-instances).

## AI editing

Expand Down Expand Up @@ -255,6 +272,18 @@ flutter test
Linux `.desktop` and AppStream metadata are localized in the repository. Snap
Store listing translations are managed outside `snap/snapcraft.yaml`.

The Snap uses strict confinement and bundles Git and OpenSSH. If Git cannot
find an author identity while committing, BusyMark opens a native form, saves
the chosen repository or BusyMark-wide identity, and retries the commit. Install
it with:

```bash
sudo snap install busymark
```

The [Snap confinement notes](docs/snap-confinement.md) document supported
interfaces and the limits of host Git integration.

## Build Linux Locally

Source builds require the libhandy and WebKitGTK 4.1 development headers,
Expand Down
95 changes: 95 additions & 0 deletions assets/export/markdown.typ
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,59 @@
#show heading.where(level: 2): set text(size: 17pt, weight: "bold")
#show heading.where(level: 3): set text(size: 13.5pt, weight: "bold")
#show heading.where(level: 4): set text(size: 11.5pt, weight: "bold")
#show heading.where(level: 5): set text(size: 10.5pt, weight: "bold")
#show heading.where(level: 6): set text(size: 10.5pt, weight: "bold")
#show link: set text(fill: rgb("2563a5"))

#let value-or(item, key, default) = item.at(key, default: default)

#let render-math(item, inline: false) = {
let asset = value-or(item, "asset", "")
let source = value-or(item, "text", "")
if asset == "" {
if inline { raw(source) } else {
block(
width: 100%,
fill: rgb("fff4e5"),
inset: 7pt,
radius: 3pt,
raw(source, block: true),
)
}
} else {
let natural-width = float(value-or(item, "width", "1")) * 1pt
let natural-height = float(value-or(item, "height", "1")) * 1pt
let depth = float(value-or(item, "depth", "0")) * 1pt
if inline {
box(
width: natural-width,
height: natural-height,
baseline: depth,
image(
asset,
width: natural-width,
height: natural-height,
fit: "contain",
alt: source,
),
)
} else {
block(
width: 100%,
above: 0.8em,
below: 0.8em,
breakable: false,
align(center, layout(size => image(
asset,
width: calc.min(natural-width, size.width),
fit: "contain",
alt: source,
))),
)
}
}
}

#let render-inlines(items) = {
for item in items {
let kind = item.kind
Expand Down Expand Up @@ -74,6 +123,8 @@
} else {
image(asset, width: 1.25em, height: 1.25em, fit: "contain", alt: alt)
}
} else if kind == "math" {
render-math(item, inline: true)
} else if kind == "softBreak" {
text(" ")
} else if kind == "hardBreak" {
Expand Down Expand Up @@ -190,8 +241,30 @@
raw(value-or(block-data, "text", ""), block: true, lang: language)
},
)
} else if kind == "math" {
render-math(block-data)
} else if kind == "list" {
render-list(block-data, render-block)
} else if kind == "admonition" {
let style = value-or(block-data, "style", "note")
let fill = if style == "warning" {
rgb("fff4d6")
} else if style == "tip" {
rgb("e8f7ed")
} else {
rgb("eaf2fb")
}
block(
width: 100%,
fill: fill,
inset: 9pt,
radius: 4pt,
if children.len() > 0 {
for child in children { render-block(child) }
} else {
render-inlines(inlines)
},
)
} else if kind == "blockquote" {
quote(
block: true,
Expand Down Expand Up @@ -221,6 +294,28 @@
} else {
link(destination, image-body)
})
} else if kind == "video" {
let asset = value-or(block-data, "asset", "")
let source = value-or(block-data, "source", "")
let body = if asset == "" {
block(
width: 100%,
fill: rgb("f7f7f8"),
inset: 10pt,
radius: 3pt,
align(center, emph("Video: " + source)),
)
} else {
block(
breakable: false,
align(center, image(asset, width: 100%, fit: "contain", alt: "Video")),
)
}
block(
above: 0.8em,
below: 0.8em,
if source.starts-with("https://") { link(source, body) } else { body },
)
} else if kind == "table" {
render-table(block-data)
} else if kind == "visualization" {
Expand Down
44 changes: 44 additions & 0 deletions docs/admonitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Writerside admonitions

BusyMark recognizes Writerside tips, notes, warnings, and neutral quote blocks
in Writerside Markdown and XML `.topic` files.

In Writerside Markdown, a blockquote is a tip by default:

```markdown
> Try the safer command first.
```

Use a trailing `style` attribute for the other supported presentations:

```markdown
> This limitation applies to all releases.
{style="note"}

> This operation deletes existing data.
{style="warning"}

> Documentation is a product feature.
{style="quote"}
```

BusyMark also recognizes Writerside semantic elements in Markdown and XML
topics:

```xml
<tip>Try the safer command first.</tip>
<note>This limitation applies to all releases.</note>
<warning>This operation deletes existing data.</warning>
<quote>Documentation is a product feature.</quote>
```

The WYSIWYG editing toolbar shows an **Admonition** menu for Writerside
Markdown topics. Select **Tip**, **Note**, **Warning**, or **Quote** to convert
the active block or selected blocks. BusyMark emits Writerside-compatible
blockquote syntax and preserves existing semantic-element syntax when changing
the type of an element-based admonition.

Ordinary Markdown blockquotes remain ordinary quotes. BusyMark does not apply
Writerside's default-tip behavior outside a Writerside Markdown topic.

The supported forms follow JetBrains' [Writerside admonition documentation](https://www.jetbrains.com/help/writerside/admonitions.html).
68 changes: 68 additions & 0 deletions docs/collapsible-elements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Writerside collapsible elements

BusyMark renders Writerside collapsible chapters, procedures, code blocks, and
definition lists in Writerside Markdown topics and XML `.topic` files. These
extensions are not interpreted in ordinary Markdown workspaces.

## Chapters

In Writerside Markdown, add the attribute to a heading:

```markdown
## Advanced details {collapsible="true"}

This section is hidden until it is expanded.
```

In semantic markup, use a chapter element:

```xml
<chapter title="Advanced details" collapsible="true">
<p>This section is hidden until it is expanded.</p>
</chapter>
```

The collapsible chapter owns the content up to the next heading at the same or
a higher level.

## Procedures and code

Procedures use the semantic attribute:

```xml
<procedure title="Build the project" collapsible="true">
<step>Compile the sources.</step>
<step>Package the result.</step>
</procedure>
```

For a Markdown code fence, put its attribute block directly after the fence:

````markdown
```kotlin
data class Person(val name: String)
```
{collapsible="true" collapsed-title="Person.kt"}
````

Semantic `code-block` elements support the same `collapsible` and
`collapsed-title` attributes. Without `collapsed-title`, BusyMark uses the
first non-empty code line as the collapsed label.

## Definition lists

Set `collapsible="true"` on the definition list. Each definition becomes an
independent disclosure item:

```xml
<deflist collapsible="true">
<def title="Open initially" default-state="expanded"><p>Text.</p></def>
<def title="Closed initially" default-state="collapsed"><p>Text.</p></def>
</deflist>
```

Collapsible content is closed initially unless its element has
`default-state="expanded"`. BusyMark preserves the original source and passes
the same documented syntax to Writerside's official build pipeline.

The syntax follows JetBrains' [Writerside collapsible-elements documentation](https://www.jetbrains.com/help/writerside/collapsible-elements.html).
8 changes: 4 additions & 4 deletions docs/local-ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ application bundle. A desktop process must still read the key to make a direct
BYOK request, so OS credential storage protects the key at rest; it does not
make a compromised desktop process trustworthy.

Under strict Snap confinement, BusyMark uses libsecret's password API through
the desktop interface. Libsecret selects the per-Snap Secret Portal backend;
BusyMark does not request the broad `password-manager-service` interface or
access other applications' keyring entries.
BusyMark uses libsecret's password API through the desktop secret service.
Access depends on the credential service made available to the packaged
application. BusyMark does not write API keys into settings, workspaces, logs,
or its application bundle.

Choose **Automatic model selection** to let BusyMark route the task among the
approved models of the selected provider, or **Fixed model** to use only the
Expand Down
Loading
Loading