Update the journal-set doc entry, the CLI diagnostic table, and the authoring skill, along with the in-app empty-state hint and the var message comment.
142 lines
4.4 KiB
TypeScript
142 lines
4.4 KiB
TypeScript
/**
|
|
* VariableView — flat list of all variable key-value pairs.
|
|
*
|
|
* Hovering a value shows a popup with:
|
|
* - Declaration expression (if any), styled distinctly
|
|
* - Active tag modifiers: #tag +N (from $source)
|
|
*/
|
|
|
|
import { Component, For, createMemo, Show, createSignal } from "solid-js";
|
|
import { useJournalStream } from "../stores/journalStream";
|
|
import { useJournalCompletions } from "./completions";
|
|
import { getMods, getDeclExpr } from "./var-reactivity";
|
|
|
|
export const VariableView: Component = () => {
|
|
const stream = useJournalStream();
|
|
const comp = useJournalCompletions();
|
|
|
|
const values = createMemo(() => stream.variables);
|
|
|
|
/** Runtime engine diagnostics (bad declarations / modifiers). */
|
|
const diagnostics = createMemo(() => comp.diagnostics());
|
|
|
|
/** Flat list of all variables */
|
|
const items = createMemo(() => {
|
|
const vars = values();
|
|
return Object.entries(vars).map(([key, value]) => ({
|
|
key,
|
|
value,
|
|
isTag: value.startsWith("#"),
|
|
}));
|
|
});
|
|
|
|
const hasAnyData = () =>
|
|
comp.data.declarations.length > 0 || Object.keys(values()).length > 0;
|
|
|
|
return (
|
|
<div class="h-full overflow-y-auto bg-gray-50">
|
|
<Show when={diagnostics().length > 0}>
|
|
<div class="border-b border-amber-200 bg-amber-50 px-3 py-2">
|
|
<div class="text-xs font-semibold text-amber-800 mb-1">
|
|
变量求值有 {diagnostics().length} 个问题
|
|
</div>
|
|
<ul class="space-y-1">
|
|
<For each={diagnostics()}>
|
|
{(d) => (
|
|
<li class="text-xs text-amber-700 font-mono break-all">{d.message}</li>
|
|
)}
|
|
</For>
|
|
</ul>
|
|
</div>
|
|
</Show>
|
|
<Show
|
|
when={hasAnyData()}
|
|
fallback={
|
|
<p class="text-center text-gray-400 text-xs py-8">
|
|
暂无变量定义。在文档中使用 ```yaml role=declare 代码块定义变量。
|
|
</p>
|
|
}
|
|
>
|
|
<div class="divide-y divide-gray-100">
|
|
<For each={items()}>
|
|
{(item) => (
|
|
<VariableRow
|
|
key={item.key}
|
|
value={item.value}
|
|
isTag={item.isTag}
|
|
/>
|
|
)}
|
|
</For>
|
|
</div>
|
|
</Show>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// VariableRow — single row with hover popup
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const VariableRow: Component<{
|
|
key: string;
|
|
value: string;
|
|
isTag: boolean;
|
|
}> = (props) => {
|
|
const [hovered, setHovered] = createSignal(false);
|
|
|
|
const declExpr = createMemo(() => getDeclExpr(props.key));
|
|
const mods = createMemo(() => getMods(props.key));
|
|
|
|
const hasPopup = () => !!declExpr() || mods().length > 0;
|
|
|
|
return (
|
|
<div
|
|
class="relative flex items-center px-3 py-1.5 hover:bg-white/50 transition-colors"
|
|
onMouseEnter={() => setHovered(true)}
|
|
onMouseLeave={() => setHovered(false)}
|
|
>
|
|
<span class="flex-1 text-sm font-mono text-gray-800">{props.key}</span>
|
|
<span
|
|
class={props.isTag
|
|
? "text-sm font-mono text-purple-700 font-semibold"
|
|
: "text-sm font-mono text-gray-700 font-semibold"
|
|
}
|
|
>
|
|
{props.value}
|
|
</span>
|
|
|
|
{/* Hover popup */}
|
|
<Show when={hasPopup() && hovered()}>
|
|
<div class="absolute right-0 top-full mt-1 z-50 bg-white border border-gray-200 rounded-md shadow-lg p-2 min-w-45 text-xs">
|
|
{/* Declaration expression */}
|
|
<Show when={declExpr()}>
|
|
<div class="mb-1">
|
|
<span class="text-gray-400">expr: </span>
|
|
<span class="font-mono text-gray-500 bg-gray-50 px-1 rounded">
|
|
{declExpr()}
|
|
</span>
|
|
</div>
|
|
</Show>
|
|
|
|
{/* Tag modifiers */}
|
|
<Show when={mods().length > 0}>
|
|
<div class={declExpr() ? "border-t border-gray-100 pt-1 mt-1" : ""}>
|
|
<For each={mods()}>
|
|
{(mod) => (
|
|
<div class="flex items-center gap-1 text-gray-500">
|
|
<span class="font-mono text-purple-600">{mod.tag}</span>
|
|
<span>+{mod.value}</span>
|
|
<span class="text-gray-400">(from {mod.source})</span>
|
|
</div>
|
|
)}
|
|
</For>
|
|
</div>
|
|
</Show>
|
|
</div>
|
|
</Show>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default VariableView;
|