@zephytiju/prism-entity-summary
v0.1.0
Published
Platform Prism entity-summary micro-UI (component id "entity-summary"): a pure selection consumer — subscribes to the "search-results.selected-entity" channel, fetches the IEntitySummary projection for the selection through the host Lattice transport, and
Readme
PrismEntitySummaryMicroUI
Platform Prism entity-summary micro-UI. Component id: entity-summary.
Published to npm as @zephytiju/prism-entity-summary.
The entity-summary panel is a PURE selection consumer: it subscribes to the GLOBAL
search-results.selected-entity channel (the EntityReference the search-results micro-UI
publishes when an entity card is clicked) and, on every selection change, fetches the bounded
IEntitySummary projection through the Common bundle's generated client bound to the host
Lattice transport. It renders the panel header (title / subtitle — defaulting from the locale
bundle, overridable by configuration; Prism has no composed title node), the selected id pill, the
summary label, a type badge, and the attribute list as FLAT inline key/value rows — mono muted
label left, value right in its encoded semantic token color, no box/card around each attribute
(per the CONFIRMED design's "OPERATIONAL ATTRIBUTES" section; the attribute list is a sub-part
rendered inline — no separate repo) — with a loading skeleton while the fetch is in flight, a
guided empty state when nothing is selected, and an error state whose RETRY button re-runs the
last fetch locally. The component PUBLISHES NOTHING — no state channels, no events, no audit.
Composed applications (for example Guanlan) consume it as-is; the component is platform-owned.
Configuration keys
| Prop | Meaning |
| --- | --- |
| locale | UI locale for the component-fixed strings: "en" \| "zh-CN" (default "en") — see i18n |
| title | Panel header title override; when omitted the title defaults from the locale bundle (ENTITY SUMMARY / 实体摘要) |
| subtitle | Panel header mono subtitle override; when omitted it defaults from the locale bundle (SELECTION DETAIL / 所选实体详情) |
| attributeMaxRows | Cap on rendered attribute rows; further rows collapse into a + {count} more note (default: all rows) |
Channel contract
| Direction | Kind | Id | Payload |
| --- | --- | --- | --- |
| consumes | state | search-results.selected-entity | EntityReference \| null — { id, ontologyInterface: "IEntitySummary", label? } (published by the search-results micro-UI's entity card) |
The component is a pure consumer (D8): it binds ONLY usePrismStateValue on that one channel
id and never calls usePrismStateSetter / writeChannel / emitPrismEvent. The fetch
lifecycle (busy / error) is LOCAL state — it is never published, so nothing an instance does
can re-render other instances. The channel id is a string literal at the call-site so the
build-time channel-graph scanner derives the graph (scanSource on the component yields
consumesState: ["search-results.selected-entity"], no publications, no events).
Lattice binding
createEntitySummaryClient(useLatticeTransport()) from @zephytiju/lattice-common-interfaces —
every fetch goes through the per-method route interfaces/IEntitySummary/get with the typed
argument { refs: [selected] } and full server-side validation. No URLs, credentials, or
generic invokes. Selection changes re-run the fetch; the error state's RETRY re-runs the last
fetch locally (no event needed — the component keeps its own re-fetch trigger).
Audit rule
Reading an entity summary is NOT audit-worthy. This component emits NO audit event and NO Prism event of any kind.
Attribute value color encoding (<token>:<text>)
The semantic color of an attribute value is ENCODED IN THE BACKEND VALUE STRING: a
<token>:<text> prefix where the semantic token sits before the FIRST colon — e.g.
"threat:HOSTILE", "ok:OPERATIONAL", "accent:NORTH COMMAND", "muted:2026-09-17 04:12Z".
At render time the component parses the prefix (no RegExp: first-colon indexOf + exact match
against the known-token list), renders the remaining text in that token's semantic theme color
and strips the prefix. The known tokens are exactly the component's semantic color vocabulary:
ok, accent, threat, warn, signal, text, muted.
Values with NO colon, an empty token (":text") or an UNKNOWN token word render VERBATIM in the
default text color — so colon-bearing data (2026-09-17 04:12Z, RATIO 3:1) is safe unless it
genuinely starts with a known token word. Only the semantic token name travels on the wire; the
concrete palette still resolves through the host's MantineProvider. The convention is
documented on the bundle-facing types in src/types.ts (AttributeValueToken), and the parser
lives in src/AttributeList.tsx (parseEncodedAttributeValue).
Internationalization (i18n)
The component ships en and zh-CN locale bundles — src/locales/en.json /
src/locales/zh-CN.json — and every component-fixed UI string is resolved from them (the
no-selection guided state, the DEFAULT panel title/subtitle, the TYPE / ID section labels, the
"OPERATIONAL ATTRIBUTES" section label + {count} FIELDS count, the attribute-key → label map,
the + {count} more note, the error title and RETRY). The component renders no hardcoded copy.
{
"entity-summary": {
"title": "ENTITY SUMMARY",
"subtitle": "SELECTION DETAIL",
"emptyTitle": "NO ENTITY SELECTED",
"emptyHint": "select an entity in the search results to load its summary",
"typeLabel": "TYPE",
"idLabel": "ID",
"attributesLabel": "OPERATIONAL ATTRIBUTES",
"fieldsCount": "{count} FIELDS",
"attributeLabels": {
"CLASSIFICATION": "CLASSIFICATION",
"STATUS": "STATUS"
},
"moreRows": "+ {count} more",
"missingSummary": "no summary returned for the selected entity",
"errorTitle": "SUMMARY UNAVAILABLE",
"retry": "RETRY"
}
}locale?: "en" | "zh-CN"prop (default"en") selects the string table per instance — it is a per-instance prop, so two instances in one host may render differing languages.- The default
title/subtitlecome from the locale bundle (ENTITY SUMMARY/实体摘要,SELECTION DETAIL/所选实体详情); an explicittitle/subtitleprop is a composition-authored override, localized by the composer when supplied. - Attribute labels are localized through the
attributeLabelsmap (wire key → localized label). Keys missing from the map fall back to the RAW attribute key, and the map's key set may differ per locale. The label and value strings themselves (including the<token>:color encoding) come from the backend projection and are NOT localized by the component. - Locale bundles are namespaced under the component id (
"entity-summary") so a composer can deep-merge every component's bundle into ONE UI language bundle without collisions:
import { locales as entitySummaryLocales } from "@zephytiju/prism-entity-summary";
// entitySummaryLocales.en -> { "entity-summary": { … } }
// entitySummaryLocales["zh-CN"] -> { "entity-summary": { … } }
const uiBundle = deepMerge(hostStrings, entitySummaryLocales.en, searchResultsLocales.en);The parsed bundles are exported from the package entry (locales, en, zhCN,
stringsForLocale), and the raw JSONs are also served by the ./locales/* exports subpath
(e.g. @zephytiju/prism-entity-summary/locales/zh-CN.json); files ships both dist and
locales.
Theme
No palette is hardcoded. Every color resolves to SEMANTIC theme tokens (ok, accent,
threat, warn, signal, card, card-dark, input, border, line, text, muted,
deep, panel) consumed as CSS variables, plus --mantine-font-family-monospace for the mono
typography — the palette is supplied entirely by the host's MantineProvider. The local demo
ships TWO themes, both defined in src/demo.tsx: geovisionTheme (dark), mapping each
semantic token onto the exact :root variables of the CONFIRMED design (deep bg, panel black,
card, mint, red, blue, amber, purple, muted, border), and the contrasting latticeLightTheme
(light), mapping the SAME semantic token keys onto a different palette — the component is
skinned purely through the surrounding MantineProvider.
Source layout
src/ is strictly two parts:
- Component source (what the package compiles):
EntitySummary.tsx,types.ts(the local fetch-lifecycle type),index.ts(public entry),src/locales/(en.json,zh-CN.json,index.ts— the i18n string bundles and their resolver), andsrc/shims/node-crypto.ts(browser-build shim for the digest import — build infrastructure). - Demo: exactly ONE file,
src/demo.tsx— the two host themes (GeoVision v7 + Lattice Light), the mock host action executor (createDemoExecutor) and all demo test data (three sample entity refs with summaries, the one-shot failure arm), and the demo page rendering twoEntitySummaryinstances side by side behind a global EN | 中文 language switcher (plus per-instance switches) with a SELECT ENTITY control that publishes the selection channel and channel/executor monitors.
The npm package ships dist (compiled component + type declarations + locale JSONs) and the
top-level locales/ directory (the raw JSON bundles, served by the ./locales/* exports
subpath); no demo code is published. scripts/copy-locales.mjs copies the JSON bundles into
both locations during npm run build.
Local development
npm install
npm run typecheck
npm test
npm run dev
npm run shot-demonpm install pulls the platform peers (@zephytiju/prism-react,
@zephytiju/lattice-common-interfaces) from the npm registry, along with the host-side
peer dependencies (react, react-dom, @mantine/core). When consuming the published
package, install it directly (npm install @zephytiju/prism-entity-summary) and provide
those peer dependencies in the host application.
The demo (npm run dev, entry src/demo.tsx) renders TWO EntitySummary instances side by
side, each inside its own MantineProvider with a different theme (GeoVision v7 dark left,
Lattice Light right), behind an EN | 中文 segmented control — the GLOBAL switch sets the
locale prop of BOTH instances at once, and each instance carries its own per-instance
control so the two hosts can render DIFFERING locales simultaneously — and installs a mock
host action executor answering interfaces/IEntitySummary/get with sample summaries whose
attribute values carry the <token>:<text> color encoding across every semantic token (plus
one unencoded value demonstrating the default-text fallback). No title/subtitle is passed, so
the headers default from the per-locale bundle. The SELECT ENTITY control publishes
search-results.selected-entity the way the search-results entity card would, cycling through
three sample refs; because the channel is GLOBAL, BOTH instances fetch and render the same
selection in their own theme. ARM FAIL NEXT makes the next fetch fail once so the error state
and its local RETRY contract are demonstrable, and the monitors below show the consumed
selection plus the mock-host call log.
npm run shot-demo boots the vite dev server, drives both instances in headless Chrome
(selects MIL-0741, sets the LEFT instance to en and the RIGHT instance to zh-CN, waits
for the colored flat attribute rows) and captures the loaded summaries and monitors to
/tmp/guanlan-review/demo-entity-summary-v2.png.
Design record
https://qcnwge0wy4s0.feishu.cn/wiki/K40nwA5TZiUE7Nk5hK6cE0W8nTe — §7 (channel contracts) and §8 (Lattice transport and interface clients).
