feat(view,tests): query blocks stay code in text the agent did not write

This commit is contained in:
hh committed 2026-10-13 01:23:49 +02:00
1 parent 43d1e1a4a3
commit 91699a382e
5 files changed
+66 -4

No files matched your search

+17 -1
View File
@@ -223,7 +223,12 @@ function stamped(messages: HistoryMessage[]): HistoryMessage[] {
const HISTORY: Record<string, HistoryMessage[]> = { const HISTORY: Record<string, HistoryMessage[]> = {
[MASTER]: [ [MASTER]: [
{ content: "с утра: дневник приехал? что в хендауте", role: "user" }, {
content: "с утра: дневник приехал? что в хендауте",
role: "user",
service:
"[envelope - background signal, not a message; react only if it bears on the question]\ntime: 2026-08-29 06:04 (Warsaw)\nvault, changed since 05:09: 📅 дни/2026-08-29.md (+4)\n--- 📅 дни/2026-08-29.md, added lines only ---\n+ # планы\n+ просрочены:\n+ ```tasks\n+ not done\n+ 06:04 - пожалуй пойду спать",
},
{ {
content: content:
"Приехал. В [[2026-08-28|хендауте]] два хвоста: **бластер** ждёт выбора модели, и **ufw на dell** - порт 62990 снаружи молчит. Ветка по ufw открыта, см. [[ufw на dell]].", "Приехал. В [[2026-08-28|хендауте]] два хвоста: **бластер** ждёт выбора модели, и **ufw на dell** - порт 62990 снаружи молчит. Ветка по ufw открыта, см. [[ufw на dell]].",
@@ -232,6 +237,11 @@ const HISTORY: Record<string, HistoryMessage[]> = {
{ content: "ок, гони ufw", role: "user" }, { content: "ок, гони ufw", role: "user" },
{ {
content: [ content: [
{
thinking:
"The firewall rule and the proxy config can disagree in two ways: the port is closed, or nothing listens behind it. Reading the Caddy site first tells me which port the outside is supposed to reach at all.",
type: "thinking",
},
{ {
text: "Смотрю правила через `ssh dell` и сверяю с `site.caddy`.", text: "Смотрю правила через `ssh dell` и сверяю с `site.caddy`.",
type: "text", type: "text",
@@ -242,6 +252,12 @@ const HISTORY: Record<string, HistoryMessage[]> = {
name: "Bash", name: "Bash",
type: "tool_use", type: "tool_use",
}, },
{
id: "tu_say",
input: { text: "Нашёл: снаружи должен отвечать 443, не 62990." },
name: "mcp__gateway__say",
type: "tool_use",
},
], ],
role: "assistant", role: "assistant",
}, },
+15 -3
View File
@@ -1,5 +1,6 @@
import type { App, Component } from "obsidian"; import type { App, Component } from "obsidian";
import { MarkdownRenderer, Platform } from "obsidian"; import { MarkdownPreviewRenderer, MarkdownRenderer, Platform } from "obsidian";
import { inertFences } from "$lib/panel/fences";
import type { PanelHost } from "$lib/panel/host"; import type { PanelHost } from "$lib/panel/host";
export interface HostOptions { export interface HostOptions {
@@ -12,17 +13,28 @@ export interface HostOptions {
sourcePath: () => string; sourcePath: () => string;
} }
const QUERY_LANGUAGES = new Set(["tasks", "dataview", "dataviewjs", "query"]);
function runs(language: string): boolean {
const registered = (
MarkdownPreviewRenderer as unknown as {
codeBlockPostProcessors?: Record<string, unknown>;
}
).codeBlockPostProcessors;
return QUERY_LANGUAGES.has(language) || language in (registered ?? {});
}
// The Obsidian side of the panel: markdown through the app's own renderer, // The Obsidian side of the panel: markdown through the app's own renderer,
// [[links]] opened in place, a conversation's note opened by its binding. // [[links]] opened in place, a conversation's note opened by its binding.
export function obsidianHost(options: HostOptions): PanelHost { export function obsidianHost(options: HostOptions): PanelHost {
const { app, component } = options; const { app, component } = options;
return { return {
markdown(node, text) { markdown(node, text, { inert = false } = {}) {
let alive = true; let alive = true;
const staging = document.createElement("div"); const staging = document.createElement("div");
MarkdownRenderer.render( MarkdownRenderer.render(
app, app,
text, inert ? inertFences(text, runs) : text,
staging, staging,
options.sourcePath(), options.sourcePath(),
component component
+27
View File
@@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest";
import { inertFences } from "$lib/panel/fences";
const runs = (language: string) => ["tasks", "dataview"].includes(language);
describe("inertFences", () => {
it("turns a query block into code", () => {
expect(inertFences("```tasks\nnot done\n```", runs)).toBe(
"```text\nnot done\n```"
);
expect(inertFences("~~~Dataview\nlist\n~~~", runs)).toBe(
"~~~text\nlist\n~~~"
);
});
it("reaches a block opened inside a list item or a quote", () => {
expect(inertFences("+ ```tasks\n+ not done", runs)).toBe(
"+ ```text\n+ not done"
);
expect(inertFences("> 1. ````tasks {x}", runs)).toBe("> 1. ````text {x}");
});
it("leaves ordinary code and closing fences alone", () => {
const text = "```python\nprint(1)\n```\ninline `tasks` stays";
expect(inertFences(text, runs)).toBe(text);
});
});
+6
View File
@@ -34,6 +34,12 @@ export const moment = () => undefined;
export const requestUrl = () => export const requestUrl = () =>
Promise.reject(new Error("no network in the stub")); Promise.reject(new Error("no network in the stub"));
export const setIcon = () => undefined; export const setIcon = () => undefined;
export const MarkdownPreviewRenderer = {
codeBlockPostProcessors: { tasks: () => undefined } as Record<
string,
unknown
>,
};
function escapeHtml(value: string): string { function escapeHtml(value: string): string {
return value return value
+1
View File
@@ -3,6 +3,7 @@ import { defineConfig } from "vitest/config";
export default defineConfig({ export default defineConfig({
resolve: { resolve: {
alias: { alias: {
$lib: new URL("../beaver-gateway/ui/src/lib", import.meta.url).pathname,
obsidian: new URL("./tests/obsidian-stub.ts", import.meta.url).pathname, obsidian: new URL("./tests/obsidian-stub.ts", import.meta.url).pathname,
}, },
}, },