@cyber-dash-tech/revela 0.7.0 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/README.zh-CN.md +20 -1
- package/lib/commands/edit.ts +7 -45
- package/lib/config.ts +1 -1
- package/lib/edit/deck-state.ts +1 -1
- package/lib/edit/open.ts +76 -0
- package/package.json +1 -1
- package/plugin.ts +2 -0
- package/tools/edit.ts +61 -0
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ Enable it for the current session, assign a presentation task, and the agent can
|
|
|
23
23
|
- uses workspace `DECKS.json` as machine-readable deck memory, slide spec, and prewrite readiness state
|
|
24
24
|
- blocks premature writes to `decks/*.html` until the active deck is marked structurally ready
|
|
25
25
|
- runs automatic layout QA whenever the agent writes `decks/*.html`
|
|
26
|
+
- opens a visual comment editor for existing decks so users can Ctrl/Cmd-click elements and send precise edit requests back to OpenCode
|
|
26
27
|
- exports finished decks to PDF and editable PPTX
|
|
27
28
|
- switches designs and domains locally with zero LLM cost
|
|
28
29
|
|
|
@@ -139,6 +140,7 @@ Disable presentation mode when done:
|
|
|
139
140
|
/revela init initialize or refresh workspace DECKS.json
|
|
140
141
|
/revela review [slug] review active deck readiness before writing HTML
|
|
141
142
|
/revela remember <text> save an explicit user/workflow preference
|
|
143
|
+
/revela edit <target> open visual comment editor for a deck slug or decks/*.html
|
|
142
144
|
|
|
143
145
|
/revela designs list installed designs
|
|
144
146
|
/revela designs <name> activate a design
|
|
@@ -157,7 +159,7 @@ Disable presentation mode when done:
|
|
|
157
159
|
/revela pptx <file> export an HTML deck to editable PPTX in the same directory
|
|
158
160
|
```
|
|
159
161
|
|
|
160
|
-
Most `/revela` commands run locally with zero LLM cost. `/revela init`, `/revela review`, `/revela remember`, `/revela designs-new`, and `/revela designs-edit` start AI-assisted workflows because they need to read or update project files.
|
|
162
|
+
Most `/revela` commands run locally with zero LLM cost. `/revela init`, `/revela review`, `/revela remember`, `/revela designs-new`, and `/revela designs-edit` start AI-assisted workflows because they need to read or update project files. `/revela edit` opens a local visual editor and then sends user comments back into the current OpenCode session when the user submits them.
|
|
161
163
|
|
|
162
164
|
---
|
|
163
165
|
|
|
@@ -592,6 +594,23 @@ A custom domain is a folder containing `INDUSTRY.md`.
|
|
|
592
594
|
|
|
593
595
|
---
|
|
594
596
|
|
|
597
|
+
## Visual Editing
|
|
598
|
+
|
|
599
|
+
Open the visual editor for an existing deck by slug or workspace-relative HTML path:
|
|
600
|
+
|
|
601
|
+
```text
|
|
602
|
+
/revela edit my-deck
|
|
603
|
+
/revela edit decks/my-deck.html
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
The editor opens in your browser. Use `Ctrl`/`Cmd` + click to reference deck elements, write a natural-language comment, then send it back to OpenCode. Revela sends a structured edit prompt that includes the deck file, slide context, selected element metadata, and your comment.
|
|
607
|
+
|
|
608
|
+
LLM tool equivalent: `revela-edit` with `{ "target": "decks/my-deck.html" }`. This lets the agent open the same editor when you say things like “I want to edit @decks/my-deck.html”.
|
|
609
|
+
|
|
610
|
+
`/revela edit` prepares minimal `DECKS.json` state for the existing HTML deck if needed, so the normal deck write gate can still protect `decks/*.html` while allowing targeted edits.
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
595
614
|
## Export
|
|
596
615
|
|
|
597
616
|
PDF export:
|
package/README.zh-CN.md
CHANGED
|
@@ -23,6 +23,7 @@ Revela 是一个 [OpenCode](https://opencode.ai) 插件,可以把你当前使
|
|
|
23
23
|
- 使用工作区 `DECKS.json` 保存机器可读的 deck 记忆、逐页规格和写入前 readiness 状态
|
|
24
24
|
- 在 active deck 结构化 ready 前,阻止过早写入 `decks/*.html`
|
|
25
25
|
- agent 每次写入 `decks/*.html` 时自动执行布局 QA
|
|
26
|
+
- 为已有 deck 打开可视化评论编辑器,用户可以 Ctrl/Cmd + 点击元素,并把精确修改意见发回 OpenCode
|
|
26
27
|
- 支持导出成 PDF 和可编辑 PPTX
|
|
27
28
|
- design 和 domain 的切换都在本地完成,不消耗 LLM token
|
|
28
29
|
|
|
@@ -138,6 +139,7 @@ Create a 6-slide HTML deck on humanoid robotics supply chains. Cite the main mar
|
|
|
138
139
|
/revela init 初始化或刷新工作区 DECKS.json
|
|
139
140
|
/revela review [slug] 写 HTML 前检查 active deck readiness
|
|
140
141
|
/revela remember <text> 保存明确的用户/工作流偏好
|
|
142
|
+
/revela edit <target> 为 deck slug 或 decks/*.html 打开可视化评论编辑器
|
|
141
143
|
|
|
142
144
|
/revela designs 列出已安装 design
|
|
143
145
|
/revela designs <name> 激活某个 design
|
|
@@ -156,7 +158,7 @@ Create a 6-slide HTML deck on humanoid robotics supply chains. Cite the main mar
|
|
|
156
158
|
/revela pptx <file> 将 HTML deck 导出为同目录可编辑 PPTX
|
|
157
159
|
```
|
|
158
160
|
|
|
159
|
-
大多数 `/revela` 命令都在本地执行,不消耗 LLM token。`/revela init`、`/revela review`、`/revela remember`、`/revela designs-new` 和 `/revela designs-edit` 会启动 AI
|
|
161
|
+
大多数 `/revela` 命令都在本地执行,不消耗 LLM token。`/revela init`、`/revela review`、`/revela remember`、`/revela designs-new` 和 `/revela designs-edit` 会启动 AI 辅助流程,因为它们需要读取或更新项目状态。`/revela edit` 会打开本地可视化编辑器,并在用户提交评论后把修改请求发回当前 OpenCode 会话。
|
|
160
162
|
|
|
161
163
|
---
|
|
162
164
|
|
|
@@ -557,6 +559,23 @@ Prompt 注入规则:
|
|
|
557
559
|
|
|
558
560
|
---
|
|
559
561
|
|
|
562
|
+
## 可视化编辑
|
|
563
|
+
|
|
564
|
+
可以通过 deck slug 或工作区相对 HTML 路径打开可视化编辑器:
|
|
565
|
+
|
|
566
|
+
```text
|
|
567
|
+
/revela edit my-deck
|
|
568
|
+
/revela edit decks/my-deck.html
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
编辑器会在浏览器中打开。使用 `Ctrl`/`Cmd` + 点击 deck 元素来引用它们,写一段自然语言评论,然后发送回 OpenCode。Revela 会把 deck 文件、slide 上下文、选中元素 metadata 和你的评论整理成结构化 edit prompt。
|
|
572
|
+
|
|
573
|
+
对应的 LLM tool:`revela-edit`,参数为 `{ "target": "decks/my-deck.html" }`。因此当你说“我要编辑 @decks/my-deck.html”时,agent 也可以主动打开同一个编辑器。
|
|
574
|
+
|
|
575
|
+
如果已有 HTML deck 缺少 `DECKS.json` 状态,`/revela edit` 会自动准备最小 deck state,让正常的 `decks/*.html` 写入门禁仍然生效,同时允许后续精准修改。
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
560
579
|
## 导出
|
|
561
580
|
|
|
562
581
|
PDF 导出:
|
package/lib/commands/edit.ts
CHANGED
|
@@ -1,27 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ctx } from "../ctx"
|
|
3
|
-
import { ACTIVE_PROMPT_FILE } from "../config"
|
|
4
|
-
import { buildPrompt } from "../prompt-builder"
|
|
5
|
-
import { resolveEditableDeck } from "../edit/resolve-deck"
|
|
6
|
-
import { ensureEditableDeckState } from "../edit/deck-state"
|
|
7
|
-
import { startEditServer } from "../edit/server"
|
|
8
|
-
|
|
9
|
-
function openUrl(url: string): void {
|
|
10
|
-
if (process.platform === "darwin") {
|
|
11
|
-
const proc = Bun.spawnSync(["open", url])
|
|
12
|
-
if (proc.exitCode !== 0) throw new Error(proc.stderr.toString() || "Failed to open edit page")
|
|
13
|
-
return
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (process.platform === "win32") {
|
|
17
|
-
const proc = Bun.spawnSync(["cmd", "/c", "start", "", url])
|
|
18
|
-
if (proc.exitCode !== 0) throw new Error(proc.stderr.toString() || "Failed to open edit page")
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const proc = Bun.spawnSync(["xdg-open", url])
|
|
23
|
-
if (proc.exitCode !== 0) throw new Error(proc.stderr.toString() || "Failed to open edit page")
|
|
24
|
-
}
|
|
1
|
+
import { openEditableDeck } from "../edit/open"
|
|
25
2
|
|
|
26
3
|
export async function handleEdit(
|
|
27
4
|
input: string,
|
|
@@ -35,32 +12,17 @@ export async function handleEdit(
|
|
|
35
12
|
}
|
|
36
13
|
|
|
37
14
|
try {
|
|
38
|
-
const
|
|
39
|
-
const preflight = ensureEditableDeckState(options.workspaceRoot, deck)
|
|
40
|
-
if (!preflight.readiness.ready) {
|
|
41
|
-
await send(`**Edit blocked:** ${preflight.readiness.blocker || "Deck is not ready for HTML edits."}`)
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
ctx.enabled = true
|
|
46
|
-
if (!existsSync(ACTIVE_PROMPT_FILE)) buildPrompt()
|
|
47
|
-
|
|
48
|
-
const editServer = startEditServer()
|
|
49
|
-
const token = editServer.createSession({
|
|
15
|
+
const result = openEditableDeck(target, {
|
|
50
16
|
client: options.client,
|
|
51
17
|
sessionID: options.sessionID,
|
|
52
|
-
|
|
18
|
+
workspaceRoot: options.workspaceRoot,
|
|
53
19
|
})
|
|
54
|
-
const url = `${editServer.baseUrl}/edit?token=${encodeURIComponent(token)}`
|
|
55
|
-
openUrl(url)
|
|
56
20
|
|
|
57
|
-
const source = deck.source === "decks-state" ? "DECKS.json" : deck.source === "file-path" ? "file path" : "fallback path"
|
|
58
|
-
const stateNote = preflight.changed ? "Deck state was prepared in DECKS.json before opening the editor.\n" : "Deck state is ready in DECKS.json.\n"
|
|
59
21
|
await send(
|
|
60
|
-
`Opened visual editor for deck \`${deck.slug}\`.\n` +
|
|
61
|
-
`File: \`${deck.file}\` (${source})\n` +
|
|
62
|
-
stateNote +
|
|
63
|
-
`URL: ${url}\n\n` +
|
|
22
|
+
`Opened visual editor for deck \`${result.deck.slug}\`.\n` +
|
|
23
|
+
`File: \`${result.deck.file}\` (${result.source})\n` +
|
|
24
|
+
`${result.stateNote}\n` +
|
|
25
|
+
`URL: ${result.url}\n\n` +
|
|
64
26
|
`Use Ctrl/Cmd + click in the browser to reference elements, write a comment, then send comments. Revela mode has been enabled for the edit prompt.`
|
|
65
27
|
)
|
|
66
28
|
} catch (e: any) {
|
package/lib/config.ts
CHANGED
|
@@ -28,7 +28,7 @@ export const CONFIG_FILE = join(CONFIG_DIR, "config.json")
|
|
|
28
28
|
export const ACTIVE_PROMPT_FILE = join(CONFIG_DIR, "_active-prompt.md")
|
|
29
29
|
|
|
30
30
|
/** Default design name. */
|
|
31
|
-
export const DEFAULT_DESIGN = "
|
|
31
|
+
export const DEFAULT_DESIGN = "summit"
|
|
32
32
|
|
|
33
33
|
/** Default domain name. */
|
|
34
34
|
export const DEFAULT_DOMAIN = "general"
|
package/lib/edit/deck-state.ts
CHANGED
package/lib/edit/open.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { existsSync } from "fs"
|
|
2
|
+
import { ctx } from "../ctx"
|
|
3
|
+
import { ACTIVE_PROMPT_FILE } from "../config"
|
|
4
|
+
import { seedBuiltinDesigns } from "../design/designs"
|
|
5
|
+
import { seedBuiltinDomains } from "../domain/domains"
|
|
6
|
+
import { buildPrompt } from "../prompt-builder"
|
|
7
|
+
import { ensureEditableDeckState } from "./deck-state"
|
|
8
|
+
import { resolveEditableDeck, type EditableDeck } from "./resolve-deck"
|
|
9
|
+
import { startEditServer } from "./server"
|
|
10
|
+
|
|
11
|
+
export interface OpenEditableDeckResult {
|
|
12
|
+
deck: EditableDeck
|
|
13
|
+
url: string
|
|
14
|
+
source: string
|
|
15
|
+
stateNote: string
|
|
16
|
+
preflightChanged: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface OpenEditableDeckOptions {
|
|
20
|
+
client: any
|
|
21
|
+
sessionID: string
|
|
22
|
+
workspaceRoot: string
|
|
23
|
+
openBrowser?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function openUrl(url: string): void {
|
|
27
|
+
if (process.platform === "darwin") {
|
|
28
|
+
const proc = Bun.spawnSync(["open", url])
|
|
29
|
+
if (proc.exitCode !== 0) throw new Error(proc.stderr.toString() || "Failed to open edit page")
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (process.platform === "win32") {
|
|
34
|
+
const proc = Bun.spawnSync(["cmd", "/c", "start", "", url])
|
|
35
|
+
if (proc.exitCode !== 0) throw new Error(proc.stderr.toString() || "Failed to open edit page")
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const proc = Bun.spawnSync(["xdg-open", url])
|
|
40
|
+
if (proc.exitCode !== 0) throw new Error(proc.stderr.toString() || "Failed to open edit page")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function openEditableDeck(target: string, options: OpenEditableDeckOptions): OpenEditableDeckResult {
|
|
44
|
+
const deck = resolveEditableDeck(options.workspaceRoot, target)
|
|
45
|
+
const preflight = ensureEditableDeckState(options.workspaceRoot, deck)
|
|
46
|
+
if (!preflight.readiness.ready) {
|
|
47
|
+
throw new Error(preflight.readiness.blocker || "Deck is not ready for HTML edits.")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
ctx.enabled = true
|
|
51
|
+
if (!existsSync(ACTIVE_PROMPT_FILE)) {
|
|
52
|
+
seedBuiltinDesigns()
|
|
53
|
+
seedBuiltinDomains()
|
|
54
|
+
buildPrompt()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const editServer = startEditServer()
|
|
58
|
+
const token = editServer.createSession({
|
|
59
|
+
client: options.client,
|
|
60
|
+
sessionID: options.sessionID,
|
|
61
|
+
deck,
|
|
62
|
+
})
|
|
63
|
+
const url = `${editServer.baseUrl}/edit?token=${encodeURIComponent(token)}`
|
|
64
|
+
if (options.openBrowser !== false) openUrl(url)
|
|
65
|
+
|
|
66
|
+
const source = deck.source === "decks-state" ? "DECKS.json" : deck.source === "file-path" ? "file path" : "fallback path"
|
|
67
|
+
const stateNote = preflight.changed ? "Deck state was prepared in DECKS.json before opening the editor." : "Deck state is ready in DECKS.json."
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
deck,
|
|
71
|
+
url,
|
|
72
|
+
source,
|
|
73
|
+
stateNote,
|
|
74
|
+
preflightChanged: preflight.changed,
|
|
75
|
+
}
|
|
76
|
+
}
|
package/package.json
CHANGED
package/plugin.ts
CHANGED
|
@@ -83,6 +83,7 @@ import extractDocumentMaterialsTool from "./tools/extract-document-materials"
|
|
|
83
83
|
import qaTool from "./tools/qa"
|
|
84
84
|
import pdfTool from "./tools/pdf"
|
|
85
85
|
import pptxTool from "./tools/pptx"
|
|
86
|
+
import createEditTool from "./tools/edit"
|
|
86
87
|
import { RESEARCH_PROMPT, RESEARCH_AGENT_SIGNATURE } from "./lib/agents/research-prompt"
|
|
87
88
|
import { runQA, formatReport } from "./lib/qa"
|
|
88
89
|
import { extractDesignClasses } from "./lib/design/designs"
|
|
@@ -338,6 +339,7 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
338
339
|
"revela-qa": qaTool,
|
|
339
340
|
"revela-pdf": pdfTool,
|
|
340
341
|
"revela-pptx": pptxTool,
|
|
342
|
+
"revela-edit": createEditTool({ client, workspaceRoot }),
|
|
341
343
|
},
|
|
342
344
|
|
|
343
345
|
// ── chat.message: intercept @-referenced / pasted binary files ────────
|
package/tools/edit.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tools/edit.ts
|
|
3
|
+
*
|
|
4
|
+
* revela-edit — Open Revela's visual comment editor for an existing deck.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { tool } from "@opencode-ai/plugin"
|
|
8
|
+
import { openEditableDeck } from "../lib/edit/open"
|
|
9
|
+
|
|
10
|
+
export function createEditTool(options: { client: any; workspaceRoot: string; openBrowser?: boolean }) {
|
|
11
|
+
return tool({
|
|
12
|
+
description:
|
|
13
|
+
"Open Revela's visual comment editor for an existing slide deck. " +
|
|
14
|
+
"Use this when the user asks to edit, revise, annotate, or visually comment on a deck, " +
|
|
15
|
+
"including when they reference a decks/*.html file with @. " +
|
|
16
|
+
"Accepts either a deck slug from DECKS.json or a workspace-relative decks/*.html path. " +
|
|
17
|
+
"This opens a local browser editor where the user can Ctrl/Cmd-click deck elements, write comments, " +
|
|
18
|
+
"and send precise edit requests back to the current OpenCode session.",
|
|
19
|
+
args: {
|
|
20
|
+
target: tool.schema
|
|
21
|
+
.string()
|
|
22
|
+
.describe("Deck slug or workspace-relative deck HTML path, e.g. investor-update or decks/investor-update.html."),
|
|
23
|
+
},
|
|
24
|
+
async execute({ target }, context: any) {
|
|
25
|
+
const sessionID = context?.sessionID ?? context?.session?.id ?? ""
|
|
26
|
+
if (!sessionID) {
|
|
27
|
+
return JSON.stringify({
|
|
28
|
+
ok: false,
|
|
29
|
+
error: "Cannot open visual editor because the current OpenCode session id is unavailable.",
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const result = openEditableDeck(target, {
|
|
35
|
+
client: options.client,
|
|
36
|
+
sessionID,
|
|
37
|
+
workspaceRoot: options.workspaceRoot,
|
|
38
|
+
openBrowser: options.openBrowser,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return JSON.stringify({
|
|
42
|
+
ok: true,
|
|
43
|
+
deck: result.deck.slug,
|
|
44
|
+
file: result.deck.file,
|
|
45
|
+
source: result.source,
|
|
46
|
+
url: result.url,
|
|
47
|
+
message:
|
|
48
|
+
`${result.stateNote} Opened visual editor. ` +
|
|
49
|
+
"Ask the user to use Ctrl/Cmd + click in the browser to reference elements, write a comment, then send comments.",
|
|
50
|
+
}, null, 2)
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return JSON.stringify({
|
|
53
|
+
ok: false,
|
|
54
|
+
error: error instanceof Error ? error.message : String(error),
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default createEditTool
|