Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 6 additions & 3 deletions console/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
//! `pnpm install --frozen-lockfile && pnpm build` inside `web/`
//! before the rest of the crate compiles.
//! 3. Ensures the console's *own* injected UI assets exist: `src/ui.rs`
//! embeds `ui/dist/config-form.js` and `ui/dist/styles.css` via
//! `include_str!` (the state worker precedent). Set `SKIP_UI_BUILD=1`
//! to use the existing `ui/dist/` outputs as-is.
//! embeds `ui/dist/config-form.js`, `ui/dist/catalog-page.js`, and
//! `ui/dist/styles.css` via `include_str!` (the state worker precedent).
//! Set `SKIP_UI_BUILD=1` to use the existing `ui/dist/` outputs as-is.

use std::path::{Path, PathBuf};
use std::process::Command;
Expand Down Expand Up @@ -99,6 +99,7 @@ fn ensure_web_bundle() {
/// CI escape hatch.
fn ensure_ui_assets() {
println!("cargo:rerun-if-changed=ui/config-form.tsx");
println!("cargo:rerun-if-changed=ui/catalog-page.tsx");
println!("cargo:rerun-if-changed=ui/styles.css");
println!("cargo:rerun-if-changed=ui/src");
println!("cargo:rerun-if-changed=ui/build.mjs");
Expand All @@ -112,6 +113,7 @@ fn ensure_ui_assets() {
let ui_dir = manifest_dir.join("ui");
let dist_assets = [
ui_dir.join("dist").join("config-form.js"),
ui_dir.join("dist").join("catalog-page.js"),
ui_dir.join("dist").join("styles.css"),
];

Expand Down Expand Up @@ -174,6 +176,7 @@ fn ui_dist_is_fresh(dist_asset: &Path, ui_dir: &Path) -> bool {

let watched_files = [
ui_dir.join("config-form.tsx"),
ui_dir.join("catalog-page.tsx"),
ui_dir.join("styles.css"),
ui_dir.join("build.mjs"),
ui_dir.join("package.json"),
Expand Down
59 changes: 54 additions & 5 deletions console/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
//! `console:script` / `console:style` trigger types it hosts (the engine
//! routes the registration straight back to this worker).
//!
//! One contribution: a custom configuration form for the `console` entry
//! (`host.configForms`), replacing the schema-generated JSON editor with the
//! injectable-UI toggle board — one bordered card per worker (title +
//! description + switch) flipping `injectableUi.disabledWorkers`, which
//! [`crate::configuration::start_injectable_ui_sync`] applies live.
//! Two contributions:
//!
//! - a custom configuration form for the `console` entry
//! (`host.configForms`), replacing the schema-generated JSON editor with
//! the injectable-UI toggle board — one bordered card per worker (title +
//! description + switch) flipping `injectableUi.disabledWorkers`, which
//! [`crate::configuration::start_injectable_ui_sync`] applies live;
//! - the engine-catalogue pages (`host.pages`): functions and triggers,
//! reading `engine::functions::*` / `engine::triggers::*` /
//! `engine::registered-triggers::list`. They are engine-level views no
//! single worker owns, and they ship injected so the console SPA carries
//! no per-view code.
//!
//! Registration machinery comes from the shared `iii-console-ui` crate
//! (workers/crates/console-ui); this module only names the assets and embeds
Expand All @@ -23,18 +30,24 @@ use iii_console_ui::ConsoleUi;
use iii_sdk::IIIClient;

pub const CONFIG_FORM_PATH: &str = "console/config-form.js";
pub const CATALOG_PAGE_PATH: &str = "console/catalog-page.js";
pub const STYLES_PATH: &str = "console/styles.css";

/// Built by `build.rs` (esbuild over `ui/`).
const CONFIG_FORM_JS: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/ui/dist/config-form.js"
));
const CATALOG_PAGE_JS: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/ui/dist/catalog-page.js"
));
const STYLES_CSS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/ui/dist/styles.css"));

fn console_ui() -> ConsoleUi {
ConsoleUi::new("console")
.script(CONFIG_FORM_PATH, CONFIG_FORM_JS)
.script(CATALOG_PAGE_PATH, CATALOG_PAGE_JS)
.style(STYLES_PATH, STYLES_CSS)
}

Expand Down Expand Up @@ -62,6 +75,42 @@ mod tests {
);
}

#[test]
fn embedded_catalog_page_is_nonempty_esm() {
assert!(
CATALOG_PAGE_JS.contains("export"),
"built catalog-page.js looks wrong"
);
}

/// The pages are useless if their ids drift from the routes the nav and
/// deep links use (`#/ext/functions`, `#/ext/triggers`).
#[test]
fn embedded_catalog_page_registers_every_page() {
for id in ["functions", "triggers"] {
assert!(
CATALOG_PAGE_JS.contains(id),
"built catalog-page.js is missing the `{id}` page"
);
}
}

/// The pages are live over engine signals, not timers. A build that lost
/// the subscriptions would look fine and silently go stale.
#[test]
fn embedded_catalog_page_subscribes_to_engine_signals() {
for signal in [
"engine::functions-available",
"engine::workers-available",
"trace",
] {
assert!(
CATALOG_PAGE_JS.contains(signal),
"built catalog-page.js no longer subscribes to `{signal}`"
);
}
}

#[test]
fn embedded_styles_are_scoped() {
// esbuild prints the attribute selector unquoted ([data-iii-ui=console]).
Expand Down
9 changes: 5 additions & 4 deletions console/ui/build.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/**
* Build the console worker's own two console assets:
* Build the console worker's own console assets:
*
* config-form.tsx → dist/config-form.js (injected over `console:script`)
* styles.css → dist/styles.css (injected over `console:style`)
* config-form.tsx → dist/config-form.js (injected over `console:script`)
* catalog-page.tsx → dist/catalog-page.js (injected over `console:script`)
* styles.css → dist/styles.css (injected over `console:style`)
*
* The five shared specifiers stay EXTERNAL — they resolve at runtime
* through the console's import map (a bundled React copy would surface as
Expand All @@ -14,7 +15,7 @@
import esbuild from 'esbuild'

const options = {
entryPoints: ['config-form.tsx', 'styles.css'],
entryPoints: ['config-form.tsx', 'catalog-page.tsx', 'styles.css'],
bundle: true,
format: 'esm',
jsx: 'automatic',
Expand Down
55 changes: 55 additions & 0 deletions console/ui/catalog-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Entry for the console worker's engine-catalogue pages — compiled by
* esbuild (react + @iii-dev/console-ui external) into dist/catalog-page.js
* and served over the `console:script` trigger (see src/ui.rs). The
* stylesheet is its own asset: styles.css ships over `console:style` as
* console/styles.css.
*
* Two contributions, both reading engine-level data no single worker owns:
*
* - src/catalog/FunctionsPage — every registered function, its schemas, an
* invoke panel, and its live call feed
* (#/ext/functions)
* - src/catalog/TriggersPage — trigger types and their live bindings, each
* with its family's real fire path
* (#/ext/triggers)
*
* Both run off engine signals (`engine::functions-available`,
* `engine::workers-available`, `trace`) rather than timers, so they are live
* without polling. Per-worker drill-down lives on the console's own Workers
* page, which expands a row into that worker's functions and triggers.
*
* They ship as injected UI rather than console pages so the console SPA
* keeps no per-view code: this bundle can be rebuilt, hot-reloaded, and
* toggled off without touching the host.
*/

import type { Host, PageRenderProps } from '@iii-dev/console-ui'
import { FunctionsPage } from './src/catalog/FunctionsPage'
import { TriggersPage } from './src/catalog/TriggersPage'

export default function setup(host: Host) {
host.pages.register({
id: 'functions',
title: 'functions',
render: ({ panelSide, onRequestClose }: PageRenderProps) => (
<FunctionsPage
host={host}
side={panelSide}
onRequestClose={onRequestClose}
/>
),
})

host.pages.register({
id: 'triggers',
title: 'triggers',
render: ({ panelSide, onRequestClose }: PageRenderProps) => (
<TriggersPage
host={host}
side={panelSide}
onRequestClose={onRequestClose}
/>
),
})
}
Loading
Loading