@mai/projax-ui (0.1.0)
Installation
@mai:registry=https://mgit.flexsiebels.de/api/packages/mAi/npm/npm install @mai/projax-ui@0.1.0"@mai/projax-ui": "0.1.0"About this package
projax-ui
Svelte 5 screens for projax — the tree and board views that mount on @mai/mkit's DataView, DataBoard and Tree (m/mkit#242, item 6 of m/projax#40's ordered work: docs/plans/projax-as-a-module.md). The tree screen (m/mkit#243, item 7) is the first one to land.
A standalone package rather than a @mai/mkit subpath, so a projax-only change never bumps mBrian, paliad, mWiki or the HLC hub, none of which mount projax (m/mkit#242, d-8f030a3a, d-a91a1412). The -ui in the name keeps it clear of the Go data package m/projax ships under d-8f030a3a.
Install
Published as @mai/projax-ui on this Gitea instance's own npm registry, under the mAi account's namespace, the account that cuts every release.
bun add @mai/projax-ui @mai/mkit
Point the @mai scope at the registry in a .npmrc (project root or user level):
@mai:registry=https://mgit.msbls.de/api/packages/mAi/npm/
The package is readable without a credential. @mai/mkit is a peer dependency, not a regular one: install it alongside, at the version this package's peerDependencies names. A consumer that instead lets a nested copy resolve — mismatched kit versions inside one app — ends up with two kit stores, seen in mBrian at @mai/mkit 0.2.7 beside @mai/meditor 0.9.0.
Versioning
d-880df9d8, m's rule for this workspace, also in CHANGELOG.md's header: 0.x for now, a patch for every fix and every addition, a minor only for a change a consumer must react to, and 1.0 when m calls the package stable. A branch never names the number; it writes a CHANGELOG.d/ fragment with bump: patch or bump: minor (root README § Publishing).
Use
import { ProjaxTree, ProjaxBoard, ProjaxFields, buildForest, projaxQuery, loadAllRows } from '@mai/projax-ui';
ProjaxTree is the tree screen (m/mkit#243, item 7 of m/projax#40's ordered work), over @mai/mkit/shell's Tree/TreeGroup/TreeRow. It never holds or sends PROJAX_MCP_TOKEN: it takes rows, already fetched — the host's own server load calls projax's GET /api/items with its Bearer token (projaxQuery/loadAllRows build the request and page past the 100-row ceiling) and passes the result in as a prop.
// +page.server.ts, or the equivalent in a host that is not SvelteKit
import { loadAllRows, projaxQuery, ProjaxFields } from '@mai/projax-ui';
import { decodeState } from '@mai/mkit/dataview';
export async function load({ url, fetch }) {
const state = decodeState(url.search, ProjaxFields);
const rows = await loadAllRows(
(query) => fetch(`https://projax.msbls.de/api/items?${query}`, { headers: { Authorization: `Bearer ${PROJAX_MCP_TOKEN}` } }).then((r) => r.json()),
projaxQuery(state, ProjaxFields)
);
return { rows };
}
ProjaxItemCard is the per-item render slot — status chip, tags, management chips, pinned star, archived flag, the multi-parent badge — exported on its own, shared by the tree and the board so neither rebuilds it.
The task and issue rollup
GET /api/items?rollup=true adds each row's own aggregate over CalDAV and Gitea — open tasks, overdue, open issues, next signal, last activity (m/projax#49) — at a measured cold cost of 7.8–8.2 s on the live item set (m/projax#52), against about 0.11 s for a plain page. ProjaxTree and ProjaxBoard therefore take the rollup as a second, separate load, never folded into the fast first one that paints the screen:
// +page.server.ts, or the equivalent in a host that is not SvelteKit
import { loadAllRows, loadRollups, projaxQuery, ProjaxFields } from '@mai/projax-ui';
import { decodeState } from '@mai/mkit/dataview';
export async function load({ url, fetch }) {
const state = decodeState(url.search, ProjaxFields);
const loader = (query) => fetch(`https://projax.msbls.de/api/items?${query}`, { headers: { Authorization: `Bearer ${PROJAX_MCP_TOKEN}` } }).then((r) => r.json());
const rows = await loadAllRows(loader, projaxQuery(state, ProjaxFields));
// Not awaited: the page renders `rows` at once, and `rollupSet` streams in once the slow call resolves.
const rollupSet = loadRollups(loader, projaxQuery(state, ProjaxFields, undefined, true));
return { rows, rollupSet };
}
<!-- +page.svelte -->
<ProjaxTree rows={data.rows} rollupSet={data.rollupSet} />
rollupSet takes either a resolved RollupSet ({rollups, builtAt}, keyed by item id) or a Promise<RollupSet> — a host that already awaited it hands in the plain value, a host that wants the tree to paint before the rollup arrives hands in the promise unawaited, as above. ProjaxTree/ProjaxBoard look each row's rollup up by id and pass it to that row's ProjaxItemCard, which renders nothing extra until its own rollup is present — a plain GET /api/items and the tree's first render are never blocked by it.
ProjaxBoard is the board screen (m/mkit#244, item 8), over @mai/mkit/dataview's DataBoard. It groups the items feed by status, area, tag or management; a drop moves the card at once and hands a {feed, group_by, card_id, from, to} intent to a host-supplied mover — the package holds no PROJAX_MCP_TOKEN and no move semantics, the mover's the loader's own counterpart. A refusal reverts the card and shows the mover's error text as is.
// +page.svelte, or the equivalent in a host that is not SvelteKit
import { ProjaxBoard } from '@mai/projax-ui';
async function mover(intent) {
const res = await fetch('https://projax.msbls.de/api/board/move', {
method: 'POST',
headers: { Authorization: `Bearer ${PROJAX_MCP_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify(intent)
});
if (res.status === 204) return { ok: true };
return { ok: false, error: (await res.json()).error };
}
<ProjaxBoard rows={data.rows} groupBy="status" {mover} />
The tasks feed (group_by=due) is not built: ProjaxFields has no due field and this package has no tasks rows source, so grouping items by a due date has nothing to group on. A tasks board waits on both landing first.
Develop
bun install
bun run test:unit # src/*.test.ts
bun run build # svelte-package into dist/
bun run check # svelte-check
Dependencies
Development Dependencies
| ID | Version |
|---|---|
| @mai/mkit | ^0.2.13 |
| @sveltejs/package | ^2.3.0 |
| @sveltejs/vite-plugin-svelte | ^6.2.4 |
| @types/bun | ^1.3.9 |
| publint | ^0.3.0 |
| svelte | ^5.57.0 |
| svelte-check | ^4.0.0 |
| typescript | ^5.0.0 |
| vite | ^7.3.6 |
Peer Dependencies
| ID | Version |
|---|---|
| @mai/mkit | ^0.2.12 |
| svelte | ^5.0.0 |