letagents 0.12.33 → 0.12.35
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 +0 -2
- package/dist/mcp/codex-session/app-server.js +16 -2
- package/dist/mcp/codex-session/session-start.js +1 -1
- package/dist/mcp/server/runtime/messages.js +18 -1
- package/dist/mcp/server/tools/tasks/verdict-tools.js +1 -1
- package/dist/mcp/server/tools/wake-rules.js +2 -1
- package/dist/shared/presence-change-channel.js +2 -0
- package/package.json +2 -2
- package/shared/agent-branch.d.mts +5 -0
- package/shared/agent-branch.mjs +28 -0
- package/shared/code-block.css +193 -0
- package/shared/code-highlighting.d.mts +109 -0
- package/shared/code-highlighting.mjs +345 -0
- package/shared/codex-owner-isolation.d.mts +34 -0
- package/shared/codex-owner-isolation.mjs +210 -0
- package/shared/message-link-preview-store.d.mts +10 -0
- package/shared/message-link-preview-store.mjs +77 -0
- package/shared/message-link-previews.d.mts +16 -0
- package/shared/message-link-previews.mjs +70 -0
- package/shared/message-pin-store.d.mts +20 -0
- package/shared/message-pin-store.mjs +102 -0
- package/shared/message-pins.d.mts +25 -0
- package/shared/message-pins.mjs +16 -0
- package/shared/message-reaction-store.d.mts +51 -0
- package/shared/message-reaction-store.mjs +290 -0
- package/shared/message-reactions.d.mts +73 -0
- package/shared/message-reactions.mjs +130 -0
- package/shared/message-search-controller.d.mts +38 -0
- package/shared/message-search-controller.mjs +99 -0
- package/shared/message-search.d.mts +46 -0
- package/shared/message-search.mjs +114 -0
- package/shared/paced-queue.d.mts +28 -0
- package/shared/paced-queue.mjs +159 -0
- package/shared/permission-review.d.mts +19 -0
- package/shared/permission-review.mjs +238 -15
- package/shared/room-message-urls.d.mts +25 -0
- package/shared/room-message-urls.mjs +119 -0
- package/shared/room-notification-client.d.mts +18 -0
- package/shared/room-notification-client.mjs +140 -0
- package/shared/room-notification-preferences.d.mts +17 -0
- package/shared/room-notification-preferences.mjs +34 -0
- package/shared/room-resource-invalidation.d.mts +5 -0
- package/shared/room-resource-invalidation.mjs +6 -0
- package/shared/room-turn-no-reply.d.mts +1 -0
- package/shared/room-turn-no-reply.mjs +1 -0
- package/shared/room-typing.d.mts +16 -0
- package/shared/room-typing.mjs +112 -0
- package/shared/room-unread-client.ts +128 -0
- package/shared/room-unread.d.mts +25 -0
- package/shared/room-unread.mjs +57 -0
- package/shared/room-unread.test.mjs +91 -0
- package/shared/rooms/RoomsDirectory.vue +1 -1
- package/shared/rooms/directory.ts +1 -0
- package/shared/slash-commands.d.mts +31 -0
- package/shared/slash-commands.mjs +48 -0
- package/shared/slash-commands.test.mjs +26 -0
- package/shared/ui/MessageReactionBar.vue +65 -0
- package/shared/ui/MessageReactionPicker.vue +291 -0
- package/shared/ui/MessageSearchResults.vue +106 -0
- package/shared/ui/PinnedMessages.vue +124 -0
- package/shared/ui/TypingIndicator.vue +22 -0
- package/shared/ui/WakeRulesPanel.vue +1 -0
- package/shared/ui/message-reactions.css +254 -0
- package/shared/ui/message-search.css +186 -0
- package/shared/ui/useComposerSlashCommands.ts +92 -0
- package/shared/wake-rules.d.mts +8 -3
- package/shared/wake-rules.mjs +20 -4
package/README.md
CHANGED
|
@@ -212,8 +212,6 @@ The API runs at `http://localhost:3001`. Point `LETAGENTS_API_URL` at your serve
|
|
|
212
212
|
|
|
213
213
|
Optional — **long room long-polls** (multi-hour `wait_for_messages` / `GET …/messages/poll`): set the **same** `LETAGENTS_POLL_MAX_MS` on **both** the API process and any MCP client you run from source (milliseconds; default `180000`).
|
|
214
214
|
|
|
215
|
-
Optional — **offline-with-work notice grace**: set `LETAGENTS_LIVENESS_NOTICE_AFTER_MS` on the API process (milliseconds; default `1800000`, or 30 minutes). The room is told about an unreachable worker only while it holds an active work lease, and only after this grace; ordinary connects and disconnects are never posted (the roster shows presence). Internal transport staleness remains 2 minutes for routing and diagnostics.
|
|
216
|
-
|
|
217
215
|
The API now uses PostgreSQL with Drizzle ORM. `DB_URL` must be set before starting the server or running migrations.
|
|
218
216
|
|
|
219
217
|
Useful database commands:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
2
|
import { createServer } from "net";
|
|
3
|
+
import { codexOwnerIsolationOverrides } from "../../../shared/codex-owner-isolation.mjs";
|
|
3
4
|
const DEFAULT_SERVER_HOST = "127.0.0.1";
|
|
4
5
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
5
6
|
function readyUrlFromServerUrl(serverUrl) {
|
|
@@ -62,8 +63,21 @@ export async function resolveCodexServerUrl(explicitServerUrl) {
|
|
|
62
63
|
}
|
|
63
64
|
return allocateLoopbackServerUrl();
|
|
64
65
|
}
|
|
65
|
-
export function
|
|
66
|
-
|
|
66
|
+
export function codexAppServerArgs(serverUrl, configOverrides) {
|
|
67
|
+
return ["app-server", ...configOverrides.flatMap((override) => ["-c", override]), "--listen", serverUrl];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The session works for the room, so its app-server gets none of the owner's
|
|
71
|
+
* Codex plugins, connectors, hooks, personal skills or MCP servers. It keeps
|
|
72
|
+
* the owner's LetAgents server, which it joins the room with. A launch that
|
|
73
|
+
* cannot list the owner's servers, or whose project changes the LetAgents
|
|
74
|
+
* server, fails rather than starting.
|
|
75
|
+
*/
|
|
76
|
+
export async function launchAppServer(serverUrl, codexBin, options = {}) {
|
|
77
|
+
const overrides = await codexOwnerIsolationOverrides(codexBin, { cwd: options.cwd, env: process.env });
|
|
78
|
+
const child = spawn(codexBin, codexAppServerArgs(serverUrl, overrides), {
|
|
79
|
+
// Start where the servers were listed, so the project config matches.
|
|
80
|
+
...(options.cwd ? { cwd: options.cwd } : {}),
|
|
67
81
|
detached: true,
|
|
68
82
|
stdio: "ignore",
|
|
69
83
|
});
|
|
@@ -90,7 +90,7 @@ export async function startLocalCodexSession(input) {
|
|
|
90
90
|
let startupSucceeded = false;
|
|
91
91
|
try {
|
|
92
92
|
if (launchedServer) {
|
|
93
|
-
serverPid = launchAppServer(serverUrl, codexBin);
|
|
93
|
+
serverPid = await launchAppServer(serverUrl, codexBin, { cwd });
|
|
94
94
|
if (serverPid) {
|
|
95
95
|
registerLaunchedAppServer(serverPid);
|
|
96
96
|
}
|
|
@@ -122,11 +122,28 @@ function withThreadMetadata(record, recordsById, summaries) {
|
|
|
122
122
|
},
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Who reacted is worth an agent's attention; an empty list on every message,
|
|
127
|
+
* or an avatar address per reactor, is not.
|
|
128
|
+
*/
|
|
129
|
+
function withAgentReadableReactions(record) {
|
|
130
|
+
const { reactions, ...rest } = record;
|
|
131
|
+
if (!Array.isArray(reactions))
|
|
132
|
+
return record;
|
|
133
|
+
const readable = reactions.filter(isRecord).map((reaction) => ({
|
|
134
|
+
emoji: reaction.emoji,
|
|
135
|
+
count: reaction.count,
|
|
136
|
+
by: (Array.isArray(reaction.reactors) ? reaction.reactors : [])
|
|
137
|
+
.filter(isRecord)
|
|
138
|
+
.map((reactor) => reactor.name ?? reactor.login),
|
|
139
|
+
}));
|
|
140
|
+
return readable.length ? { ...rest, reactions: readable } : rest;
|
|
141
|
+
}
|
|
125
142
|
function toAgentReadableMessage(message, recordsById, summaries) {
|
|
126
143
|
if (!message || typeof message !== "object") {
|
|
127
144
|
return message;
|
|
128
145
|
}
|
|
129
|
-
const record = withThreadMetadata(message, recordsById, summaries);
|
|
146
|
+
const record = withThreadMetadata(withAgentReadableReactions(message), recordsById, summaries);
|
|
130
147
|
const kind = normalizeAgentPromptKind(record.agent_prompt_kind);
|
|
131
148
|
const text = typeof record.text === "string" ? record.text : null;
|
|
132
149
|
if (!kind || text === null) {
|
|
@@ -6,7 +6,7 @@ import { jsonToolResponse, taskToolError } from "./response.js";
|
|
|
6
6
|
import { taskReviewIdentitySchema } from "./schemas.js";
|
|
7
7
|
export function registerTaskVerdictTools(server) {
|
|
8
8
|
server.tool("submit_review_verdict", "Submit a GitHub review verdict through the durable effect journal. Requires this exact worker session to hold an active review lease. Success means GitHub confirmed publication; pending, failed, or uncertain effects keep their original idempotency key. Ambiguous outcomes are reconciled by correlation lookup and are never blindly retried.", {
|
|
9
|
-
task_id: z.string().describe("Task in review, e.g. 'task_1'."),
|
|
9
|
+
task_id: z.string().describe("Task in review, or blocked by requested changes for a re-review, e.g. 'task_1'."),
|
|
10
10
|
verdict: z.enum(["approve", "request_changes", "comment"]).describe("GitHub review verdict."),
|
|
11
11
|
body: z.string().max(65_536).optional().describe("Review explanation. Empty or junk blocking verdicts are quarantined."),
|
|
12
12
|
expected_head_sha: z.string().regex(/^[0-9a-fA-F]{40}$/).describe("Exact 40-hex pull request head SHA that was reviewed."),
|
|
@@ -30,6 +30,7 @@ export function registerWakeRuleTools(server) {
|
|
|
30
30
|
+ "github.review_submitted {pr | mine: true, states?: [approved, changes_requested, commented]}; "
|
|
31
31
|
+ "github.pr_closed {pr | mine: true, merged_only?}. "
|
|
32
32
|
+ "`mine` means the branches and pull requests of tasks you are working on. Rules expire after 24 hours unless you set expires_at (at most 7 days). "
|
|
33
|
+
+ "A rule on one pull request ends when it merges (or stays closed for 10 minutes), and a task rule ends when the task is done or cancelled; you are woken once to say your wait ended. "
|
|
33
34
|
+ "Adding the same rule twice returns the existing one. People see what you are waiting for and can cancel it.", {
|
|
34
35
|
event: z.enum(WAKE_RULE_EVENTS),
|
|
35
36
|
arguments: z.record(z.string(), z.unknown()).optional().describe("What exactly to wait for; see the event list above."),
|
|
@@ -58,7 +59,7 @@ export function registerWakeRuleTools(server) {
|
|
|
58
59
|
return toolError(error);
|
|
59
60
|
}
|
|
60
61
|
});
|
|
61
|
-
server.tool("list_wake_rules", "List what agents in this room are waiting for, and wake rules that recently fired, expired or
|
|
62
|
+
server.tool("list_wake_rules", "List what agents in this room are waiting for, and wake rules that recently fired, expired, were cancelled or ended (ended_reason says why). Check this on resume to see what you were waiting for.", { ...scope, agent_key: z.string().optional().describe("Only this agent’s rules.") }, async ({ room_id, agent_key }) => {
|
|
62
63
|
try {
|
|
63
64
|
const { target, local } = await roomTarget(room_id);
|
|
64
65
|
if (local)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "letagents",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.35",
|
|
4
4
|
"description": "Let Agents Chat \u2014 MCP server for AI agent communication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/mcp/server.js",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"esbuild": "^0.25.12"
|
|
71
71
|
},
|
|
72
72
|
"fast-uri": "3.1.8",
|
|
73
|
-
"hono": "4.13.
|
|
73
|
+
"hono": "4.13.8",
|
|
74
74
|
"ip-address": "10.7.2"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** The part of a leased branch's name that names the agent. */
|
|
2
|
+
export function agentBranchSegment(agentKey: string): string;
|
|
3
|
+
export function leasedBranchRef(taskId: string, agentKey: string): string;
|
|
4
|
+
/** True when `branch` is a branch LetAgents would lease to this agent, for any task. */
|
|
5
|
+
export function isAgentBranch(branch: unknown, agentKey: unknown): boolean;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The branches LetAgents leases to an agent: `letagents/<task>/<agent>`. The
|
|
3
|
+
* server names a task's branch with this module, and the desktop uses it to
|
|
4
|
+
* tell an agent's own branches from every other branch.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** The part of a leased branch's name that names the agent. */
|
|
8
|
+
export function agentBranchSegment(agentKey) {
|
|
9
|
+
const slug = agentKey
|
|
10
|
+
.trim()
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
13
|
+
.replace(/^-+|-+$/g, "")
|
|
14
|
+
.slice(0, 64);
|
|
15
|
+
return slug || "agent";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function leasedBranchRef(taskId, agentKey) {
|
|
19
|
+
return `letagents/${taskId}/${agentBranchSegment(agentKey)}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** True when `branch` is a branch LetAgents would lease to this agent, for any task. */
|
|
23
|
+
export function isAgentBranch(branch, agentKey) {
|
|
24
|
+
if (typeof branch !== "string" || typeof agentKey !== "string" || !agentKey.trim()) return false;
|
|
25
|
+
const [prefix, task, agent, ...rest] = branch.split("/");
|
|
26
|
+
return prefix === "letagents" && rest.length === 0 && typeof task === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(task)
|
|
27
|
+
&& agent === agentBranchSegment(agentKey);
|
|
28
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/* ─── Shared Fenced Code Block & Syntax Highlighting Styles ─── */
|
|
2
|
+
|
|
3
|
+
.fenced-code-block {
|
|
4
|
+
position: relative;
|
|
5
|
+
max-width: 100%;
|
|
6
|
+
margin: 0.75em 0;
|
|
7
|
+
padding: 0;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
border: 1px solid var(--border);
|
|
10
|
+
border-radius: var(--radius-md, 8px);
|
|
11
|
+
background: var(--code-bg, color-mix(in srgb, var(--bg) 88%, transparent));
|
|
12
|
+
color: var(--code-text, var(--text));
|
|
13
|
+
font-family: var(--font-mono);
|
|
14
|
+
font-size: 0.82rem;
|
|
15
|
+
line-height: 1.55;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.fenced-code-block-header {
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: space-between;
|
|
22
|
+
padding: 6px 12px;
|
|
23
|
+
background: var(--code-header-bg, color-mix(in srgb, var(--text) 4%, transparent));
|
|
24
|
+
border-bottom: 1px solid var(--border);
|
|
25
|
+
user-select: none;
|
|
26
|
+
font-family: var(--font-sans);
|
|
27
|
+
font-size: 0.75rem;
|
|
28
|
+
line-height: 1.2;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.fenced-code-block-lang {
|
|
32
|
+
color: var(--code-lang, var(--text-secondary));
|
|
33
|
+
font-weight: 500;
|
|
34
|
+
text-transform: lowercase;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.fenced-code-block-lang[data-lang]::before {
|
|
38
|
+
content: attr(data-lang);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.fenced-code-block-copy {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
margin-left: auto;
|
|
46
|
+
padding: 3px 8px;
|
|
47
|
+
border: 1px solid var(--border);
|
|
48
|
+
border-radius: var(--radius-sm, 4px);
|
|
49
|
+
background: var(--code-copy-bg, transparent);
|
|
50
|
+
color: var(--code-copy-text, var(--text-secondary));
|
|
51
|
+
font-family: var(--font-sans);
|
|
52
|
+
font-size: 0.72rem;
|
|
53
|
+
font-weight: 500;
|
|
54
|
+
line-height: 1.2;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
transition: color var(--duration-fast, 150ms) var(--ease-out, ease),
|
|
57
|
+
background var(--duration-fast, 150ms) var(--ease-out, ease),
|
|
58
|
+
border-color var(--duration-fast, 150ms) var(--ease-out, ease);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@media (hover: hover) and (pointer: fine) {
|
|
62
|
+
.fenced-code-block-copy:hover {
|
|
63
|
+
background: var(--accent-hover, rgba(255, 255, 255, 0.08));
|
|
64
|
+
color: var(--text);
|
|
65
|
+
border-color: var(--border-strong);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.fenced-code-block-copy:focus-visible {
|
|
70
|
+
outline: 2px solid var(--blue, #3b82f6);
|
|
71
|
+
outline-offset: 1px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.fenced-code-block-copy::before {
|
|
75
|
+
content: "Copy";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.fenced-code-block-copy[data-copied="true"] {
|
|
79
|
+
color: var(--green-text, #4ade80);
|
|
80
|
+
border-color: color-mix(in srgb, var(--green-text, #4ade80) 40%, transparent);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.fenced-code-block-copy[data-copied="true"]::before {
|
|
84
|
+
content: "Copied";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.fenced-code-block-copy[data-copied="error"] {
|
|
88
|
+
color: var(--red-text, #f87171);
|
|
89
|
+
border-color: color-mix(in srgb, var(--red-text, #f87171) 40%, transparent);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.fenced-code-block-copy[data-copied="error"]::before {
|
|
93
|
+
content: "Failed";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* Eleven different surfaces across desktop and web (e.g. chat messages, long message reader,
|
|
98
|
+
* board task inspector, thread panel, events view, markdown editor preview, agent inspector)
|
|
99
|
+
* define conflicting styles for `pre` with varying selector specificity.
|
|
100
|
+
* The `!important` declarations below ensure the fenced code block pre maintains a clean,
|
|
101
|
+
* borderless, transparent container with standard monospace sizing across all surfaces.
|
|
102
|
+
*/
|
|
103
|
+
.fenced-code-block pre {
|
|
104
|
+
margin: 0 !important;
|
|
105
|
+
padding: 10px 14px !important;
|
|
106
|
+
overflow-x: auto !important;
|
|
107
|
+
border: none !important;
|
|
108
|
+
border-radius: 0 !important;
|
|
109
|
+
background: transparent !important;
|
|
110
|
+
line-height: 1.55;
|
|
111
|
+
font-family: var(--font-mono);
|
|
112
|
+
font-size: 0.82rem;
|
|
113
|
+
white-space: pre;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.fenced-code-block code.fenced-code-block-content {
|
|
117
|
+
display: block;
|
|
118
|
+
padding: 0;
|
|
119
|
+
overflow-x: visible;
|
|
120
|
+
background: transparent;
|
|
121
|
+
color: inherit;
|
|
122
|
+
font-family: inherit;
|
|
123
|
+
font-size: inherit;
|
|
124
|
+
line-height: inherit;
|
|
125
|
+
white-space: pre;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ─── Syntax Highlighting Token Mappings ─── */
|
|
129
|
+
.hljs-comment,
|
|
130
|
+
.hljs-quote {
|
|
131
|
+
color: var(--code-comment, var(--text-secondary));
|
|
132
|
+
font-style: italic;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.hljs-keyword,
|
|
136
|
+
.hljs-selector-tag,
|
|
137
|
+
.hljs-subst {
|
|
138
|
+
color: var(--code-keyword, var(--task-assigned));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.hljs-string,
|
|
142
|
+
.hljs-doctag,
|
|
143
|
+
.hljs-regexp {
|
|
144
|
+
color: var(--code-string, var(--green-text));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.hljs-number,
|
|
148
|
+
.hljs-literal {
|
|
149
|
+
color: var(--code-number, var(--amber-text));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.hljs-title,
|
|
153
|
+
.hljs-title.function_,
|
|
154
|
+
.hljs-title.class_,
|
|
155
|
+
.hljs-section {
|
|
156
|
+
color: var(--code-function, var(--blue-text));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.hljs-variable,
|
|
160
|
+
.hljs-template-variable,
|
|
161
|
+
.hljs-attr,
|
|
162
|
+
.hljs-attribute {
|
|
163
|
+
color: var(--code-variable, var(--task-in-review));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.hljs-type,
|
|
167
|
+
.hljs-built_in,
|
|
168
|
+
.hljs-name {
|
|
169
|
+
color: var(--code-type, var(--red-text));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.hljs-meta,
|
|
173
|
+
.hljs-tag {
|
|
174
|
+
color: var(--code-comment, var(--text-secondary));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.hljs-addition {
|
|
178
|
+
color: var(--code-string, var(--green-text));
|
|
179
|
+
background-color: var(--green-dim, rgba(34, 197, 94, 0.12));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.hljs-deletion {
|
|
183
|
+
color: var(--code-type, var(--red-text));
|
|
184
|
+
background-color: var(--red-dim, rgba(239, 68, 68, 0.12));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.hljs-emphasis {
|
|
188
|
+
font-style: italic;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.hljs-strong {
|
|
192
|
+
font-weight: bold;
|
|
193
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export type CanonicalLanguage =
|
|
2
|
+
| 'javascript'
|
|
3
|
+
| 'typescript'
|
|
4
|
+
| 'python'
|
|
5
|
+
| 'bash'
|
|
6
|
+
| 'json'
|
|
7
|
+
| 'yaml'
|
|
8
|
+
| 'xml'
|
|
9
|
+
| 'css'
|
|
10
|
+
| 'sql'
|
|
11
|
+
| 'diff'
|
|
12
|
+
| 'rust'
|
|
13
|
+
| 'go'
|
|
14
|
+
| 'c'
|
|
15
|
+
| 'cpp'
|
|
16
|
+
| 'java'
|
|
17
|
+
| 'csharp'
|
|
18
|
+
| 'dockerfile'
|
|
19
|
+
| 'ruby'
|
|
20
|
+
| 'php'
|
|
21
|
+
| 'swift'
|
|
22
|
+
| 'kotlin'
|
|
23
|
+
| 'ini';
|
|
24
|
+
|
|
25
|
+
export type LanguageAlias =
|
|
26
|
+
| 'js'
|
|
27
|
+
| 'jsx'
|
|
28
|
+
| 'mjs'
|
|
29
|
+
| 'cjs'
|
|
30
|
+
| 'ts'
|
|
31
|
+
| 'tsx'
|
|
32
|
+
| 'mts'
|
|
33
|
+
| 'cts'
|
|
34
|
+
| 'py'
|
|
35
|
+
| 'sh'
|
|
36
|
+
| 'zsh'
|
|
37
|
+
| 'shell'
|
|
38
|
+
| 'yml'
|
|
39
|
+
| 'html'
|
|
40
|
+
| 'xhtml'
|
|
41
|
+
| 'svg'
|
|
42
|
+
| 'rs'
|
|
43
|
+
| 'golang'
|
|
44
|
+
| 'c++'
|
|
45
|
+
| 'cc'
|
|
46
|
+
| 'hpp'
|
|
47
|
+
| 'h'
|
|
48
|
+
| 'cs'
|
|
49
|
+
| 'docker'
|
|
50
|
+
| 'rb'
|
|
51
|
+
| 'kt'
|
|
52
|
+
| 'kts'
|
|
53
|
+
| 'toml';
|
|
54
|
+
|
|
55
|
+
export type CodeHighlightLanguage = CanonicalLanguage;
|
|
56
|
+
|
|
57
|
+
export interface ResolvedLanguage {
|
|
58
|
+
canonical: CanonicalLanguage;
|
|
59
|
+
label: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface CodeHighlighterOptions {
|
|
63
|
+
highlight: (code: string, canonicalLanguage: CanonicalLanguage) => string;
|
|
64
|
+
now?: () => number;
|
|
65
|
+
maxBudgetMs?: number;
|
|
66
|
+
windowMs?: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CodeHighlighter {
|
|
70
|
+
renderCodeBlock(code: string, rawTag?: string): string;
|
|
71
|
+
cache: CodeHighlightCache;
|
|
72
|
+
resolveLanguage(rawTag?: string): ResolvedLanguage | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const CANONICAL_LANGUAGES: readonly CanonicalLanguage[];
|
|
76
|
+
export const LANGUAGE_ALIASES: ReadonlyMap<string, CanonicalLanguage>;
|
|
77
|
+
|
|
78
|
+
export const MAX_HIGHLIGHT_CHARS: number;
|
|
79
|
+
export const MAX_HIGHLIGHT_LINES: number;
|
|
80
|
+
export const MAX_HIGHLIGHT_LINE_LENGTH: number;
|
|
81
|
+
export const MAX_CACHE_ENTRIES: number;
|
|
82
|
+
export const MAX_CACHE_CHARS: number;
|
|
83
|
+
|
|
84
|
+
export const DEFAULT_TIME_BUDGET_MS: number;
|
|
85
|
+
export const DEFAULT_TIME_WINDOW_MS: number;
|
|
86
|
+
|
|
87
|
+
export function escapeHtml(value: unknown): string;
|
|
88
|
+
export function escapeAttribute(value: unknown): string;
|
|
89
|
+
export function resolveLanguage(rawTag?: string): ResolvedLanguage | null;
|
|
90
|
+
|
|
91
|
+
export class CodeHighlightCache {
|
|
92
|
+
constructor(maxEntries?: number, maxChars?: number);
|
|
93
|
+
get(key: string): string | undefined;
|
|
94
|
+
set(key: string, value: string): void;
|
|
95
|
+
clear(): void;
|
|
96
|
+
readonly size: number;
|
|
97
|
+
readonly totalChars: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function createCodeHighlighter(options: CodeHighlighterOptions): CodeHighlighter;
|
|
101
|
+
export function executeCodeBlockCopy(
|
|
102
|
+
button: HTMLButtonElement,
|
|
103
|
+
textToCopy: string | null,
|
|
104
|
+
copyFn: (text: string) => Promise<boolean> | boolean,
|
|
105
|
+
copyTimers?: WeakMap<HTMLButtonElement, number>
|
|
106
|
+
): Promise<{ success: boolean; timerId: number | null }>;
|
|
107
|
+
export function setupCodeBlockCopyListener(
|
|
108
|
+
copyFn: (text: string) => Promise<boolean> | boolean
|
|
109
|
+
): () => void;
|