Repository navigation
Releases: markii-org/markii
Release list
v0.16.0
Install
The renderer pulls the rest in as dependencies:
npm install @markii/react@0.16.0
All eight packages, if you want them explicitly:
npm install @markii/core@0.16.0
npm install @markii/stdlib@0.16.0
npm install @markii/bundle@0.16.0
npm install @markii/pack@0.16.0
npm install @markii/runtime@0.16.0
npm install @markii/lua@0.16.0
npm install @markii/react@0.16.0
npm install @markii/html@0.16.0
Downloads
The attached tarballs are the packed npm packages. The names are
stable across releases, so the latest of each is always at a fixed URL:
https://github.com/markii-org/markii/releases/latest/download/markii-core.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-stdlib.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-bundle.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-pack.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-runtime.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-lua.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-react.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-html.tgz
v0.15.0
The @markii/* packages and the VS Code extension ship this release as
0.15.0, the Obsidian plugin as 0.12.0.
Added
-
@markii/ansi: a terminal renderer. A third platform engine beside
@markii/reactand@markii/html, for a note that is read in a shell:
a command line tool, a CI log, a pager, a file someone willcat. It
consumes the same sanitized tree the other engines do and emits a plain
string, withrenderMarkToAnsi,renderMarkNodeToAnsiand
renderMarkInlineToAnsimirroring their counterparts. The whole standard
component set is there, drawn with box-drawing characters and block
characters: framed cards and tables, a bar-and-sparkline chart, side by
side row cells that stack when the terminal is narrow. The package depends
on@markii/coreand@markii/stdliband nothing else, so the ANSI
escapes, the display-width measurement and the box drawing are all its own.Color is the caller's decision, not the engine's. It never reads
process, standard output, or the environment, so the same render is
correct going to a terminal, to a log file, and to a test;detectColorLevel
is exported for a caller that wants detection and honorsNO_COLOR,
FORCE_COLOR,TERMandCOLORTERM. Colors come from a theme that maps
every Tier 1doc.csstoken, with a coverage test that fails when a new
token has no entry, the same rule the two host theme layers follow.Author text can never put an escape sequence into a reader's terminal.
Every string is stripped of control characters before it is printed, so the
only escapes in the output are the engine's own, and a probe suite proves it
by stripping the permitted sequences and asserting nothing is left. -
markii, a command line tool (apps/cli, not published yet; build it
from source).markii viewrenders a note, a directory bundle or a.mkz
to standard output, running its scripts first when it is talking to a real
terminal.markii exportwrites HTML through the same path the editor
hosts export with, the terminal rendering, or a plain CommonMark downgrade.
markii runruns a note's scripts once at the manual tier, prompting on the
terminal for each host the closure scan finds. It runs scripts through the
same shared, security-critical run path both editor hosts use, in a Node
worker thread with the same watchdog, and stores grants per device under the
platform's own configuration directory. There is no unattended-grant flag.
Removed
- This repository's own GitHub Pages deployment of
apps/playground.
The vault playground (markii-vault, https://sadigaxund.github.io/markii-vault/)
is now the one deployed playground;apps/playgroundstays a local dev
harness only.
Install
The renderer pulls the rest in as dependencies:
npm install @markii/react@0.15.0
All eight packages, if you want them explicitly:
npm install @markii/core@0.15.0
npm install @markii/stdlib@0.15.0
npm install @markii/bundle@0.15.0
npm install @markii/pack@0.15.0
npm install @markii/runtime@0.15.0
npm install @markii/lua@0.15.0
npm install @markii/react@0.15.0
npm install @markii/html@0.15.0
Downloads
The attached tarballs are the packed npm packages. The names are
stable across releases, so the latest of each is always at a fixed URL:
https://github.com/markii-org/markii/releases/latest/download/markii-core.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-stdlib.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-bundle.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-pack.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-runtime.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-lua.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-react.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-html.tgz
v0.14.0
The @markii/* packages and the VS Code extension ship this release as
0.14.0, the Obsidian plugin as 0.11.0.
Added
@markii/stdlib: an./editorsubpath. The pure editor helpers move
here from the private@markii/host, so a third-party editor host can
install the behavior the two reference hosts have. It holds directive
completion and hover (completionAt,hoverAt, the directive-context
scanner), the Insert Component skeleton builder and the standard-set
catalog, and container fence lengthening. The package still has zero
dependencies, so a host that only wants to complete the standard
components does not pull in pack loading, a bundler, or the script
runtime.@markii/hostre-exports all of it unchanged.@markii/stdlib/editor:CompletionContext.tokenStart. The offset
where a directive name begins, after the colon run, beside the existing
replaceStart. A completion widget that filters candidates against typed
text anchors ontokenStartand edits fromreplaceStart; both reference
hosts previously derived this value themselves.@markii/stdlib/editor:closesOpenContainerFence. Tells a host's
completion trigger that a bare colon run closes a container already open
above it rather than starting a new one, so accepting a suggestion cannot
insert a component into a closing fence.completionAtis unchanged and
still offers the catalog for a bare opener.@markii/stdlib:INTERACTIVE_ATTRIBUTEand the diagnostic envelope.
INTERACTIVE_ATTRIBUTE(data-mk-interactive) is the one spelling both
renderers, and packs, mark a genuinely interactive element with.
DiagnosticEvent,DiagnosticKind,OnDiagnosticandreportDiagnostic
are the neutral shape behind both renderers'onDiagnosticoption;
reportDiagnostickeeps a throwing host callback from breaking a render.@markii/core:isRecognizedTextDirective. The post-parse word-start
rule that decides whether a tokenized inline directive is kept or demoted
back to plain text, exported for an editor grammar that has to make the
same call. It is the demotion rule only: the colon-run and bracket rules
live in the upstream directive parser.@markii/core:ScriptBlock.permissions. An optional list of
hostnames a script fence declares through apermissionsattribute. It is
display only, never an input to what is granted, mirroring how a bundle
manifest'spermissions.netis already treated.@markii/reactand@markii/html:resolveHref. The link twin of
resolveImageSrc, applied to an ordinary markdown link'shref, sharing
the same dangerous-scheme refusal rather than a second copy of it.@markii/reactand@markii/html: whole-document rendering.
renderMarkNodeandrenderMarkNodeToHtmlnow accept theRootparse
returns as well as a single node, so a host does not loop over top-level
children itself.@markii/reactand@markii/html:renderMarkInlineand
renderMarkInlineToHtml. Renders source that is exactly one paragraph
holding one inline directive without the paragraph wrapper; any other
source falls back to the ordinary render unchanged.@markii/reactand@markii/html:onDiagnostic. Called once for
each quiet marker a render produces for a value it recognized and
declined, with the kind, directive, attribute, and the marker's own
wording, so a host can route it to its diagnostics surface. An unknown
attribute name never triggers it.@markii/reactand@markii/html:storeandvaulton the render
options. The same values the third and fourth positional arguments take,
available on the options object instead. The option wins when both forms
are given.@markii/reactand@markii/html: a quiet marker for a recognized and
declined value. A known attribute whose value is outside its closed set,
and a figure source refused as unsafe, still render, now carrying a
data-mk-noticeattribute and atitleexplaining why instead of
discarding the value in silence.@markii/reactand@markii/html:data-mk-interactiveon tabs'
buttons, a details summary, and the collapsed script marker's summary.
Changed
@markii/react:loadPackreturns{ registry, dropped? }instead
of a bareRegistry, andinstallPacks's success result carries
dropped: { name, engine }[]. A pack built for another renderer was
previously discarded with no record a caller could read. This is a
breaking change toloadPack's return type.@markii/lua: an ungranted capability denies instead of vanishing.
With no grants at all,netand its methods exist and record a denial
through the same non-spoofable channel a wrong-host call already used, so
the run's outcome is a capability failure rather than a Lua nil-index
error. Observable: a script that feature-detects withif net thennow
always sees a table.
Fixed
@markii/pack: a.mkpzipped as a folder says so. An archive whose
entries all sit under one top-level directory is reported by naming that
directory, instead of claiming there is nopack.jsonwhen there is one a
level down.__MACOSXentries are ignored when making that call.doc.css: analign=wrapper no longer collapses a block inside a
:::row. The automatic inline margin is cancelled for a row grid item
that carries nowidth=preset, so the item keeps its column instead of
shrinking to its content.doc.css: a host that renders into its own container keeps the derived
tokens. The Tier 2 derivations and the other rules that were scoped to
.doc(vertical rhythm, base typography, tables, code, task lists) now
also apply under[data-mk-root], which a host adds to its own container.
The table rule is the scroll container a wide table needs, so a non-.doc
host was losing that too.
VS Code extension (0.14.0)
- Directive completion no longer opens on a line that closes a container,
and the popup filters on the directive name rather than on the colon run. - Insert Component and completion insert no empty
{}for a component with
no required attributes, and put the cursor in the body of a container or
after the name of a leaf. - Add Pack Folder validates the folder before adding it, says what it found,
and writes the reason for anything skipped to the Markii output channel. - Completion, hover and Insert Component only offer components from packs
built for this renderer. - A render's quiet markers reach the Markii output channel, deduplicated,
instead of living only in a tooltip.
Obsidian plugin (0.11.0)
- The Markii Preview header action appears on a freshly created
.mk.md
note, without the view having to be split first. - The Markii Preview pane converts wikilinks and image embeds the way
Reading view does, so a note renders the same on both surfaces. - Directive completion no longer opens on a line that closes a container.
- Insert Component and completion offer only components from packs built for
this renderer. - A render's quiet markers reach the console, deduplicated, alongside the
plugin's other diagnostics.
Install
The renderer pulls the rest in as dependencies:
npm install @markii/react@0.14.0
All eight packages, if you want them explicitly:
npm install @markii/core@0.14.0
npm install @markii/stdlib@0.14.0
npm install @markii/bundle@0.14.0
npm install @markii/pack@0.14.0
npm install @markii/runtime@0.14.0
npm install @markii/lua@0.14.0
npm install @markii/react@0.14.0
npm install @markii/html@0.14.0
Downloads
The attached tarballs are the packed npm packages. The names are
stable across releases, so the latest of each is always at a fixed URL:
https://github.com/markii-org/markii/releases/latest/download/markii-core.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-stdlib.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-bundle.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-pack.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-runtime.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-lua.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-react.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-html.tgz
v0.13.0
The @markii/* packages and the VS Code extension ship this release as
0.13.0, the Obsidian plugin as 0.10.0.
Added
-
@markii/lua: ajsontable for scripts.json.decode(text)parses
JSON into ordinary Lua data, andjson.encode(value)converts a Lua value
back into JSON text. Both are present on every trigger tier and need no
capability grant, because neither does any I/O. They reuse the decoder and
the marshal walk the runtime already has rather than adding a second copy
of either, sojson.decodeis bound by the same size, depth, and
node-count budgetnet.fetch_jsonapplies, andjson.encoderejects a
cycle, a function, a non-string table key, or a non-finite number exactly
as a script's own return value is rejected. -
@markii/bundle:createMemoryBundleStorage(files). A third
BundleStorageform beside the zip and directory forms, backed by a plain
map of bundle-relative paths to strings or bytes. It routes every path
through the package's own jail and is exported from the browser-safe main
entry, so a host that already holds a bundle's files as values does not
need Node to use them. -
@markii/reactand@markii/html: aresolveImageSrcrender option.
renderMarkandrenderMarkNode, andrenderMarkToHtmland
renderMarkNodeToHtml, accept an optional function that turns a relative
<img src>into a URL the host can load. It covers both an ordinary
markdown image and a component that builds its own<img>, such as the
standard figure, and it is never asked about a source that already carries
a scheme. A resolver that returns nothing, or throws, leaves the source as
written. Ajavascript:orvbscript:result is refused, judged the way a
browser parses a URL rather than by the raw text, so a disguised scheme
cannot slip through.
Changed
- Both hosts resolve a note's images during the render instead of after
it. The VS Code preview and Obsidian's preview and Reading view pass
resolveImageSrcand no longer sweep the mounted DOM rewriting<img>
elements once React has finished. The images resolve the same way they did,
including Obsidian's report of a source that names nothing in the vault, but
the page no longer briefly requests the unresolved URL first.
Fixed
- Host-compiled packs no longer leave a
__markiiJSXglobal behind. The
pack builder passed its JSX shim through esbuild'sbanneroption, which
writes text outside theformat: 'iife'wrapper, so every pack the VS Code
extension compiled from source, and both hosts' bundledread,dash, and
preppacks, leaked that global. The shim is now an injected module inside
the wrapper. This also fixes a component that renders through a helper
module: because a banner cannot put the shim in scope of a non-entry
module, JSX in such a helper failed at render time. Packs rebuild once on
upgrade. @markii/bundle: the bundle write grant is spelledwrite:.cache/.
The writable directory is documented everywhere as.cache/, but the write
jail accepted only the undottedcache/prefix, sobundle.write(".cache/x")
was denied even with the grant while a write tocache/xlanded outside the
documented directory. The jail, the grant token, and the manifest grammar
now all use.cache/. Breaking for bundles: a manifest declaring the
retiredwrite:cache/no longer parses, and reports a diagnostic naming the
correct token instead of being silently accepted.
Install
The renderer pulls the rest in as dependencies:
npm install @markii/react@0.13.0
All eight packages, if you want them explicitly:
npm install @markii/core@0.13.0
npm install @markii/stdlib@0.13.0
npm install @markii/bundle@0.13.0
npm install @markii/pack@0.13.0
npm install @markii/runtime@0.13.0
npm install @markii/lua@0.13.0
npm install @markii/react@0.13.0
npm install @markii/html@0.13.0
Downloads
The attached tarballs are the packed npm packages. The names are
stable across releases, so the latest of each is always at a fixed URL:
https://github.com/markii-org/markii/releases/latest/download/markii-core.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-stdlib.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-bundle.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-pack.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-runtime.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-lua.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-react.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-html.tgz
v0.12.1
Added
doc.css: adata-mk-themeoverride on.doc. Setting
data-mk-theme="light"holds the light Tier 1 palette whatever the
reader's operating system prefers, anddata-mk-theme="dark"holds the
dark one. Both are attribute selectors, so an explicit attribute wins over
the automaticprefers-color-schemepalette and over a host's own token
mapping. The automatic palette's own selector is unchanged, so a host that
never sets the attribute behaves exactly as before, and
exportHtmlDocumentstill emits no attribute and still follows the
reader's preference.
Changed
- VS Code:
Markii: Export Packwrites a.mkparchive only. The
folder output and the format quick pick are gone. The command asks which
pack when more than one is configured, builds the archive, then opens a
save dialog prefilled with<name>-<version>.mkp, or<name>.mkpwhen
the manifest declares no version. Building before asking means a build
failure never costs the user a destination they would have to pick again,
and the save dialog owns the overwrite confirmation.@markii/host's
folder-writingexportPackstays: it is how the extension build lays out
its bundled packs, and the archive export composes through it.
Install
The renderer pulls the rest in as dependencies:
npm install @markii/react@0.12.1
All eight packages, if you want them explicitly:
npm install @markii/core@0.12.1
npm install @markii/stdlib@0.12.1
npm install @markii/bundle@0.12.1
npm install @markii/pack@0.12.1
npm install @markii/runtime@0.12.1
npm install @markii/lua@0.12.1
npm install @markii/react@0.12.1
npm install @markii/html@0.12.1
Downloads
The attached tarballs are the packed npm packages. The names are
stable across releases, so the latest of each is always at a fixed URL:
https://github.com/markii-org/markii/releases/latest/download/markii-core.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-stdlib.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-bundle.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-pack.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-runtime.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-lua.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-react.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-html.tgz
v0.12.0
The @markii/* packages and the VS Code extension ship this release as
0.12.0, the Obsidian plugin as 0.9.0.
Added
-
VS Code:
Markii: Install Pack from File…(markii.installPack).
Picks a.mkpfile, validates it, asks the user to confirm that
installing it will let its code run inside every Markii preview, asks
before replacing an already-installed pack of the same namespace, unzips
it into the extension's owninstalled-packsdirectory under
globalStorage, and adds that directory tomarkii.packs. A rejected
archive reports the reason on the Markii output channel and installs
nothing. An archive is the only way a pack enters a host this way: a
markii.packsentry always names a folder. -
VS Code:
Markii: Remove Installed Pack…
(markii.removeInstalledPack). Lists the packs installed under
globalStorage, each with the version its manifest declares, asks for
confirmation, then deletes the pack's directory and removes its
markii.packsentry in one step, so an installed pack can be taken back
out without finding the storage folder by hand. -
VS Code:
Markii: Export Packgains an archive output. Alongside
today's folder export, the command now offers a single.mkpfile,
built by@markii/host's newexportPackArchive(the same compile,
zipped withfflateinstead of written as separate files) and named
with@markii/pack'spackArchiveFileName. -
@markii/host:exportPackArchive, the archive-producing counterpart
ofexportPack: it compiles a pack through the same build cache and
returns a.mkpfile's bytes rather than writing a folder. -
Obsidian: three bundled component packs,
read,dash, andprep,
need no installation. Their prebuilt registration scripts and stylesheets
are compiled at plugin build time (reusing@markii/host's
buildPackRegistrationScript, the same compiler VS Code and this plugin's
own on-demand pack loading already use) and embedded intomain.jsas
base64, the sameesbuild.options.mjspattern the Run path's worker
bundle and wasmoon'sglue.wasmalready use, with the build failing
loudly if the substitution did not happen. That makes the three-file BRAT
install carry them, with no esbuild-wasm runtime required at load time.
They behave as ordinary packs: namespaced (read_source, and so on),
listed by a note'suses:, and shown in the Insert Component and
completion catalogs. They load before any installed pack, and an
installed pack that claims a namespace one of the three already holds is
refused at install time: the bundled pack wins the namespace, matching
the ordinary collision rule rather than making an exception to it. The pack contract itself is unchanged; only
apps/obsidian's build and pack-loading pipeline gained a bundled-pack
source ahead of its on-disk one. -
Obsidian: an app-managed pack store. Installed packs live in the
plugin's own directory, one folder per namespace, and the settings tab
lists them with the version each manifest declares, a Remove control that
deletes the folder and the device's trust entry together, and an Enable
control for a pack folder that is present without being installed here.
Which namespaces this device loads is recorded device-locally, never in
plugin data, so a synced or shared vault carries none of those decisions.
A pack folder that arrives another way, through Sync or a hand copy, sits
inert until it is enabled, and enabling asks the same consent question
installing asks. A folder whosepack.jsondeclares some other namespace
than the folder's own name is not loaded at all. -
Obsidian:
Reload Markii packs. Installing, removing, and enabling a
pack already reload every open preview and Reading view on the spot; this
command does the same on demand, and each reload writes what loaded and
what was skipped to the diagnostics console. -
A
tablestandard component, data-bound likestat,progress, and
chart. It handles four bound shapes: an array of objects (columns from
the union of keys, in first-seen order), an array of arrays (rows as
given), an array of primitives (one column), and a single object
(key/value rows).columns=picks and orders columns,limit=caps the
rows shown,caption=adds a caption, andformat=/decimals=apply to
numeric cells only. A missing, stale, or failed binding collapses to one
quiet empty state rather than an error. -
format=anddecimals=attributes on:value[...],stat,
progress, andtable.numbergroups thousands,compactshortens
them,percentreads a fraction as a percentage, anddateand
relativeread an ISO date or epoch milliseconds.plainstays the
default, so existing documents render unchanged. An invalidformator
decimalsis ignored rather than raised, and non-numeric input under a
numeric format shows unchanged. The logic lives in one@markii/stdlib
primitive both renderers call, so the two engines cannot format a value
differently. -
conformance/render/, a render-level fixture set: each.mk.mdinput
is paired with committed.htmlthat@markii/htmlis byte-diffed
against, with@markii/reactcross-checked for the same classes and
structure. The existing corpus pins the AST; this pins the rendered
output. -
docs/spec.md§9, a requirement-to-fixture map tying every literal
MUST and MUST NOT in the spec's core sections to the test that pins it. A
new test in@markii/coreparses the table and fails when a row cites a
fixture that does not exist, so the map cannot rot into decoration. -
A dark palette for exported documents.
doc.cssremaps its Tier 1
tokens underprefers-color-scheme: dark, so a standalone HTML export
opened by a viewer with a dark OS preference renders themed instead of a
light page glaring back. A host's own theme layer (VS Code, Obsidian)
still wins the cascade for its own preview, since it redeclares the same
tokens unconditionally afterdoc.cssloads: only a page with no such
layer after it, an export, ever resolves the new block. -
A print stylesheet for exported documents.
@markii/host's
EXPORT_PAGE_CSSnow sets@pagemargins, keeps a card, callout, or
table from splitting across a page break, and hides the collapsed script
marker and the VS Code webview's run marker when printing. -
An
index.htmlentry file for a cascade export.@markii/host's new
buildCascadeIndexHtmllists every note a cascade exported by title,
linking to its file, and both hosts write it into the archive alongside
the notes.assignCascadeFileNamestakes an optionalreservedNames
list so a walked note literally calledindexgets suffixed instead of
colliding with the archive's own index page. -
A
hideScriptBlocksexport option.buildNoteHtmlExport,
composeNoteHtmlExport, andbuildNoteExportaccept it, and
exportHtmlDocumentgrew a matchingdocClassNameoption to carry it
onto the.docwrapper. Both hosts pass their existing preview
preference (VS Code'smarkii.hideScriptBlocks, Obsidian's "Hide script
blocks" setting) into every export, so a note exported with scripts
hidden in the editor stays that way in the file. -
Obsidian:
.mk.mdnotes render inline in Reading view. A markdown
post processor renders the whole note through the same@markii/react
seam the Markii Preview pane uses, into the first Reading-view section
Obsidian calls it for, and leaves every later section empty, since
Obsidian's section splits do not line up with a Markii:::container's
span. Wikilinks and embeds are converted to ordinary markdown first,
resolved through Obsidian's metadata cache, so an unresolved link keeps
pointing at its own text instead of breaking the render. A run's values
update the rendered note without reopening it. A plain.mdfile is
never touched, and Live Preview stays untouched by design. The new
"Render components in Reading view" setting, on by default, turns this
off while leaving the Markii Preview pane exactly as it was. -
Obsidian: install a pack from a
.mkpfile. The new "Install Markii
pack from file" command validates the archive, asks in words that say the
pack's code will run in the preview, asks again before replacing a pack of
the same name, and writes nothing until all of that has passed. A rejected
archive is reported and leaves nothing behind. An entry in the device-local
pack list can also point straight at a.mkp, read where it sits and never
compiled, so it works on an install that carries no compiler. -
An optional
versionfield on a pack manifest (@markii/pack). A
pack'spack.jsonmay now declare a plain semverversionstring
(MAJOR.MINOR.PATCH, digits only, no leading zeros, no prerelease or
build suffix). Omitting it stays fully valid, with no warning. A present
but malformed value is a validation error, the same posturenamegets,
since a version a host cannot trust is worse than none. The three
bundled packs (read,dash,prep) now declare"version": "1.0.0". -
.mkppack archives: a reader in@markii/pack.openPackArchive
takes the raw bytes of a.mkpfile (a zip of a pack's prebuilt form,
files at the archive root:pack.json,webview.js,webview.csswhen
present,scripts/when present) and returns its validated manifest,
compiled script, optional stylesheet, and optional shared Lua modules, or
a structured error. It never compiles anything: an archive with no
webview.jsis rejected, since.mkpexists for hosts that may not
carry a compiler. It reuses@markii/bundle's zip reader and path jail
rather than a second implementation, so a.mkpgets the same
decompression-bomb guard and zip-slip rejection (../, an absolute
path) a.mkzbundle gets. `packArchi...
v0.11.0
Added
-
VS Code writes a run's script failures to the Markii output channel,
one line per failed script with its name, failure kind, and the short
reason the value marker's tooltip carries, so a failure behind a
collapsed or hidden marker is discoverable without hovering. Obsidian
already wrote the same lines to its console. -
Hide script blocks in the preview (VS Code extension, Obsidian
plugin). A named Lua fence normally renders as a collapsed marker you can
expand to read the source. Turn onmarkii.hideScriptBlocksin VS Code,
or "Hide script blocks" in the Obsidian settings, and the preview leaves
those markers out, for a note meant to be read rather than edited.The setting is cosmetic and hides the source blocks only. A script that
fails still marks the value it feeds, still flips the run marker to its
failed state in VS Code, still produces the failure notice a manual run
gets in Obsidian, and still writes its full reason to the host's
diagnostics surface. The renderer is untouched: both hosts put one class
on the document root and their own theme layer does the rest, so an
exported note and any other consumer of@markii/reactrender exactly
as before. -
Turn script execution off on this device (VS Code extension, Obsidian
plugin). Setmarkii.scriptsDisabledin VS Code, or turn on "Turn off
script execution on this device" in the Obsidian settings, and no note
runs its scripts: not an explicit Run, not run on open, and not a
scheduled refresh. Markii: Toggle Script Execution and Toggle
Markii script execution flip it from the command palette.A blocked Run says so in one line. A blocked automatic run is written to
the host's diagnostics surface instead of interrupting you on every note
you open, and a blocked scheduled run also stops that preview's timer, so
the reason is recorded once rather than every interval. Notes still
preview, still export, and still show whatever values their last run
produced.Grants are untouched in both directions. Turning execution off leaves
every stored network and bundle grant as it is, and turning it back on
re-authorizes nothing beyond what was already granted by hand. In VS Code
the setting is user-scope only, so a workspace cannot turn execution back
on for you; in Obsidian it is stored per device through local storage,
never in the vault, so one device's decision never travels with Sync or
with a shared vault. -
Values appear one at a time while a run is going (VS Code extension,
Obsidian plugin). A note's scripts run one after another in a single
isolate, so the first number is often ready seconds before the last one
is. The preview now shows each value the moment its script finishes: that
component switches from stale to fresh while every other component in the
note stays stale until its own value lands. The run still reports its
completion at the end, exactly as before, and a run that is cut short by
the wall-clock watchdog now keeps the values that already arrived instead
of coming back empty and clearing the note's last-known figures. -
@markii/runtime: anonValueoption onrunDocumentScripts. It is
called once per script, in document order, immediately after that
script's value is written to the store, and it is called for a failed
script as well as a successful one. The callback receives a copy of the
summary entry, cannot change what the run stores or reports, and anything
it throws is swallowed, sorunDocumentScriptsstill never throws. A
caller that does not pass it behaves exactly as before. -
A script can read the note it runs in (
@markii/runtime,
@markii/lua). Adoctable is now in scope for every script block:
doc.directives(filter)returns the note's directives in document order,
each as{name, form, attributes, text}with the inner markdown
stripped, anddoc.value(name)returns what a script ABOVE the caller
produced. That is enough for a note to collect what its author already
wrote: several:::prep_qblocks, one script that turns them into a
quiz, one component that renders it.filteraccepts{name = "..."};
a directive written inside another appears in the list right after its
parent, so filtering by name finds every match whatever wraps it.
doc.truncatedsays whether anything was left out to stay inside the
caps.The table is read-only and carries no authority, so it is not tier
gated: an auto or scheduled run sees exactly what a manual run sees, and
nothing new was granted. Reading a name that belongs to a script further
down the note (or to the caller itself) fails the run and the marker
reads "script error: reads "quiz", which runs later in the note", rather
than quietly returning a value whose meaning depends on where the reader
happened to be written. An unknown name, and a name whose script failed,
both read as nil. -
@markii/runtime:buildDirectiveListing,createDocViewSource,
laterScriptReadMessage,DEFAULT_DOC_LISTING_LIMITS,
EMPTY_DIRECTIVE_LISTING,sanitizeText,utf8ByteLength, plus the
DirectiveEntry,DirectiveListing,DocListingLimits,DocView,
DocValueRead,DocViewSource,DocumentContextand
DocumentTreeNodetypes.runDocumentScriptstakes a new optional
docoption (the listing the host built from the tree it already
parsed) andScriptExecutorreceives a new optionaldocinput, one
view per script. An executor written before this still satisfies the
type and behaves exactly as it did.The listing is capped: 512 KiB in total, 2,000 directives, 8 KiB of text
and 32 attributes per directive, 1 KiB per attribute value, and 200
levels of tree depth. Being over a cap shortens the listing and sets
truncated; it is never an error. Text and attribute values reach a
script with control characters and unpaired surrogates already removed. -
@markii/lua:buildDocand theDocConfig/DocRejectionstypes,
and adocoption onrunScript. The listing crosses into Lua as JSON
text and is decoded by the same trusted in-Lua decodernet.fetch_json
uses, so a script receives genuine Lua tables. Eachdoc.directives()
call decodes afresh, so an entry a script writes to cannot change what
the next call returns, and each script already runs in its own engine. A
refused read is recorded out of band, the way capability denials already
are, so the reported sentence is the clean one rather than a Lua stack
traceback, and a script cannot forge it.
Added
- Export a note and the notes it links to as one archive (VS Code
extension). Markii: Export as HTML cascade… follows the note-relative
markdown links out of the active note, exports each note it reaches the
same way the single-note export does, and writes one zip archive at a path
you pick. Links between exported notes are rewritten to the exported file
names, so the archive browses on its own, and a link to anything outside
your workspace or the note's own folder is never followed and stays exactly
as written. The walk is bounded at four hops and one hundred notes, and
every note that was skipped or could not be read is named on the Markii
output channel. This mirrors the Obsidian plugin's Export Markii note as
HTML cascade, and both hosts share the walk, the naming, the link
rewriting, and the archive itself.
Install
The renderer pulls the rest in as dependencies:
npm install @markii/react@0.11.0
All eight packages, if you want them explicitly:
npm install @markii/core@0.11.0
npm install @markii/stdlib@0.11.0
npm install @markii/bundle@0.11.0
npm install @markii/pack@0.11.0
npm install @markii/runtime@0.11.0
npm install @markii/lua@0.11.0
npm install @markii/react@0.11.0
npm install @markii/html@0.11.0
Downloads
The attached tarballs are the packed npm packages. The names are
stable across releases, so the latest of each is always at a fixed URL:
https://github.com/markii-org/markii/releases/latest/download/markii-core.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-stdlib.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-bundle.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-pack.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-runtime.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-lua.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-react.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-html.tgz
v0.10.0
Release Notes - Version 0.10.0
Release Date: September 1, 2026
Summary
This major update introduces cross-axis layout attributes, allowing wrappers to accept their complementary axis, and adds comprehensive text-alignment attributes across core components. It delivers powerful export features, including self-contained HTML generation with embedded images, Obsidian HTML cascades, and direct PDF printing. It also brings pack-declared attributes directly into directive completion and hover documentation, adds a new fit width preset, and improves nested container fence management.
Added
- A layout wrapper now takes the other axis as an attribute (
@markii/react,@markii/html,@markii/stdlib):widthsizes a block's box andalignplaces that box in the column, and each of the seven wrappers already sets one of those two by its name. It now accepts the other one as an attribute, so:::center{width=fit}and:::fit{align=center}say the same thing and render as ONE element carrying both classes (mk-layout mk-align-center mk-width-fit), not two nested wrappers. An attribute for the wrapper's own axis is ignored, because the name already decided it::::center{align=right}is simply centered. Invalid values are ignored as before. Nesting two wrappers still works and still means the same thing. Directive completion follows the same rule: an alignment wrapper offerswidthonly, a width wrapper offersalignonly. textaligns the content inside a component (@markii/stdlib,@markii/react,@markii/html):row,cell,card, andcalloutaccepttext=left|center|right. On a row it reaches every cell; a cell with its owntextoverrides it, and an alignment wrapper written inside a cell wins over both.textis an ordinary per-component attribute, not a reserved layout key: it reaches the component like any other. It is rendered through one new class set (mk-text-left/mk-text-center/mk-text-right) defined once indoc.css, and it is offered by directive completion and hover on those four components.@markii/stdlib:layoutWrapperAxis,otherLayoutAxis, and theLayoutAxistype: the one classification of which axis each wrapper name decides, plusTEXT_ALIGN_PRESETS,TEXT_ALIGN_ATTRIBUTE, andTEXT_ALIGN_COMPONENTSfor thetextattribute. Both renderers and the completion layer read these instead of keeping their own copies.- **
@markii/react:RegistryEntry.layoutand thelayoutClassNameprop;@markii/html:HtmlRegistryEntry.layoutandHtmlRenderContext.layoutClassName**: An entry that declareslayoutis a scope that already sets that axis by its name: the renderer drops that axis's attribute, resolves the other one, and hands the resulting class to the component instead of wrapping it in a second<div>. A component still never receiveswidthoralignamong its attributes. Every entry withoutlayoutbehaves exactly as before. - Export a note as a self-contained HTML file (VS Code extension, Obsidian plugin): Markii: Export as HTML… in VS Code asks where to save and writes one
.htmlfile; Export Markii note as HTML in Obsidian writes it beside the note in the vault. The file carries the shareddoc.cssinside it and needs nothing beside it. The note's last run is baked in, so a monitoring note exports with the figures it was showing; a note that has never been run exports with its usual empty states, and both hosts say so. Relative image sources are preserved, so a file written beside its note resolves them the same way the note does. - Pack components render in an exported file: When the exporting host has packs loaded, the export is rendered with the same merged registry and value store the preview uses, and each loaded pack's stylesheet is embedded after
doc.cssin the order the host injects it, so the file matches the preview. Obsidian renders directly; VS Code asks its preview webview and waits a bounded moment for the answer. With no packs loaded, or with no renderer to reach, the export falls back to@markii/html, the static string engine, where a pack directive comes out as the ordinary unknown-component fallback: a labeled box with the author's own content still rendered inside it. Neither outcome is treated as a failure, and the two are distinguishable on each host's diagnostics surface without changing what the notice says. - Exported files carry their own images: A local image a note references is embedded in the exported HTML as a
data:URI, so the file is self-contained: it opens with its pictures intact on a machine that has never seen the vault or the workspace. Remotehttpandhttpssources stay live URLs, since embedding them would change what the page fetches. Each host resolves an image the way its own preview does and reads it under its own jail, so an export can never pull in a file the preview itself could not show. An image above 2 MiB keeps its original path rather than inflating the file, and so does one whose extension the embedder does not recognize, or one that could not be read. Every such case is named on the host's diagnostics surface with its file and size. None of them is a failure and none of them reaches the notice. The Obsidian PDF export benefits automatically, since it prints exactly this document. - Export a linked set of notes as one archive (Obsidian plugin): Export Markii note as HTML cascade follows the links out of the active note, both wikilinks and markdown links that resolve to notes in the vault, exports every note it reaches as its own HTML file with images embedded, and writes the whole set as one zip beside the root note. Links between exported notes are rewritten to point at the sibling files, so the archive is navigable offline; a link to a note outside the exported set, or to something that is not a note, is left exactly as written. The walk detects cycles and stops at a depth and a note-count bound, and says on the diagnostics surface which bound stopped it. Cascade to PDF is not part of this release.
- Export a note straight to PDF (Obsidian plugin): Export Markii note as PDF prints exactly the document the HTML export would have written, through Electron's
printToPDFin a hidden window, and writes the.pdfbeside the note. Where that is unavailable, on a device with no reachable Electron window or a vault with no filesystem path, the command writes the HTML file instead and says which of the two happened. The reason goes to the console, this host's diagnostics surface, never into the notice. - **
@markii/pack: a component can declare its attributes inpack.json**: The object form of acomponentsentry takes an optionalattributeslist, each entry carrying anameplus an optionaldescription,requiredflag, closedvaluesset, anddefault. The field is optional and every existing manifest keeps parsing unchanged, so this is backward compatible.parsePackManifestvalidates the list strictly and rejects a manifest that gets it wrong: a bad attribute name, a duplicate name, a non-booleanrequired, avalueslist that is not a non-empty array of non-empty strings, or adefaultoutside its ownvalues. An unknown key inside an attribute entry stays a forward-compatible warning.resolvePackComponentnormalizes the same field leniently for callers reading a manifest that never went through validation, dropping what it cannot make sense of. New exports: thePackComponentAttributetype,PACK_ATTRIBUTE_NAME_PATTERN, andisPackAttributeName. dividertakes alabel-alignattribute (@markii/stdlib,@markii/react,@markii/html):::divider{label="Part 2" label-align=left}positions the label along the rule instead of centering it. The value is one ofleft,center, orright, and an absent or unrecognized value falls back tocenter, so every existing divider renders exactly as it did. It is a component-scoped attribute, distinct from the reservedwidthandalignlayout attributes, which are unchanged. Both renderers emit the samemk-divider--label-left/mk-divider--label-rightmodifier class, and only for the non-default values, sodoc.cssstill covers both.- A
fitwidth preset (@markii/stdlib,@markii/react,@markii/html):width=fitshrinks a block to its own content instead of filling the column, and the matching:::fitwrapper does the same for plain markdown that has no attributes to write on. The width scale now readsfit,narrow,normal,wide,full, andWIDTH_PRESETSis listed in that order, which is also the order a completion popup offers the values in. Pairing it with an alignment is the point:{width=fit align=right}hugs the content and sits right, because a box narrower than its container is what gives the alignment margins room to work. Both renderers pick upmk-width-fitfrom the shared preset list with no class map of their own to edit, anddoc.csscarries the one new rule. - Reworded the reserved
alignattribute's documentation (@markii/stdlib): Completion and hover now say thatalignplaces the block when it is narrower than the page, and that it should be paired with awidth. The behavior is unchanged. - Inserting a container inside a container lengthens the enclosing fences (VS Code extension, Obsidian plugin): Nesting needs the outer fence pair to carry more colons than the inner one, which until now the author had to widen by hand. Both hosts now do it in two places only: the Insert Component command, and accepting a container from the completion popup. Never while typing. The rewrite lands in the same undoable edit as the insertion, and cascades outward only as far as it has to, leaving an outer pair alone once it is already long enough. It is quiet either way: a document whose fences do not pair cleanly from the top, or that has a
:::line inside a code block or a...
v0.9.0
Release Notes - Version 0.9.0
Release Date: August 31, 2026
Summary
This release introduces comprehensive directive completion and hover support across both the VS Code extension and Obsidian plugin, adds a versatile new divider standard component, and centralizes layout attributes as data within @markii/stdlib. It also fixes an issue where prose colons were incorrectly parsed as text directives, ensuring clean text handling for times, ratios, and key-value pairs.
Added
- Directive completion and hover in both hosts: VS Code extension and Obsidian plugin; shared logic in the private host package. Typing an opener suggests component names in the forms the component's kind allows; inside the braces it suggests the component's attributes plus the reserved
widthandalign, and enumerated values for attributes that have them. Accepting a name inserts the same skeleton the Insert command builds. VS Code also shows hover documentation for a directive name. Pack components complete by name and description; declaringkindinpack.jsonnarrows which forms offer them. - A
dividerstandard component (@markii/stdlib,@markii/react,@markii/html): a leaf directive for a section break that carries a label or a chosen look:::divider,::divider{label="Part 2" variant="dots"}.variantis one ofline,dots, orornament, defaulting tolinewhen absent or unrecognized;labelis optional and, when present, is centered in the break and also carried as the separator'saria-label. Both renderers emit the samemk-dividerseparator markup, sodoc.csscovers both, and the insert catalog, directive completion, and hover documentation pick it up fromSTANDARD_COMPONENTSautomatically. A plain---thematic break keeps its own CommonMark meaning and is untouched. - **
@markii/stdlib: a neutrallayout.ts**: exporting the docs/format.md layout system as data —LAYOUT_ATTRIBUTE_KEYS(width,align),WIDTH_PRESETS(normal,narrow,wide,full),ALIGN_PRESETS(left,center,right), andLAYOUT_ATTRIBUTES, the same two attributes expressed asAttributeSchemas so a tool that documents or completes attributes reads them exactly like a component's own. This is the one source both renderers'layout.tsnow build their class maps from, replacing two independently hand-copied literals.
Changed
- **
@markii/reactand@markii/html: the reserved layout-attribute keys and preset values now come from@markii/stdlib**: instead of a hand-copied literal in each renderer's ownlayout.ts. Each renderer'swidth/alignclass map is now derived mechanically from@markii/stdlib's preset lists (mk-width-<preset>for every width preset butnormal,mk-align-<preset>for every align preset). No behavior change: the produced classes, thenormal-is-classless rule, and the null-prototype defense against a hostile value are exactly as before.
Fixed
@markii/core: a prose colon no longer parses as a text directive: Previously any single colon could open a text directive, so a clock time (12:34), a Bible verse (John 3:16), a ratio (a:b), or akey:valuepair was misread as a directive and every renderer showed an "unknown component" fallback box in the middle of an ordinary sentence. A text directive is now recognized only when its name starts with an ASCII letter and the colon sits at a word start (the start of the document, or preceded by anything other than an ASCII letter or digit). A colon that fails this rule is left as plain text, byte-identical to the source. Leaf (::) and container (:::) directives are unaffected, since they only ever occur at the start of a line.
Install
The renderer pulls the rest in as dependencies:
npm install @markii/react@0.9.0
All eight packages, if you want them explicitly:
npm install @markii/core@0.9.0
npm install @markii/stdlib@0.9.0
npm install @markii/bundle@0.9.0
npm install @markii/pack@0.9.0
npm install @markii/runtime@0.9.0
npm install @markii/lua@0.9.0
npm install @markii/react@0.9.0
npm install @markii/html@0.9.0
Downloads
The attached tarballs are the packed npm packages. The names are stable across releases, so the latest of each is always at a fixed URL:
https://github.com/markii-org/markii/releases/latest/download/markii-core.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-stdlib.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-bundle.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-pack.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-runtime.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-lua.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-react.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-html.tgz
v0.8.1
Release Notes - Version 0.8.1
Release Date: August 30, 2026
Summary
This release resolves a component rendering issue in @markii/react where declared component kinds were ignored during compilation, ensuring that inline pack components now correctly map to their designated format.
Fixed
- **
@markii/react:loadPacknow derives a registry entry'sinlineflag from the manifest's declaredkind**: akind: "inline"pack component previously rendered as the spec §4 rule 8 form-mismatch fallback when written in its own declared form, because the compiled registration script stamped every entryinline: false. The manifest'skindnow overrides the module's flag (inlineforkind: "inline", block forleafandcontainer); an entry whose manifest declares nokindkeeps the module's own flag and renders unchanged, exactly as before. The generated registration script (@markii/host, private) also bakes the truthful per-component flag into the artifact.
Install
The renderer pulls the rest in as dependencies:
npm install @markii/react@0.8.1
All eight packages, if you want them explicitly:
npm install @markii/core@0.8.1
npm install @markii/stdlib@0.8.1
npm install @markii/bundle@0.8.1
npm install @markii/pack@0.8.1
npm install @markii/runtime@0.8.1
npm install @markii/lua@0.8.1
npm install @markii/react@0.8.1
npm install @markii/html@0.8.1
Downloads
The attached tarballs are the packed npm packages. The names are stable across releases, so the latest of each is always at a fixed URL:
https://github.com/markii-org/markii/releases/latest/download/markii-core.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-stdlib.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-bundle.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-pack.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-runtime.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-lua.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-react.tgz
https://github.com/markii-org/markii/releases/latest/download/markii-html.tgz