feat: peg AI inline suggestions to granular text spans, add autosave revision history with restore, fix focus mode, and make PS logo background transparent

This commit is contained in:
Schwitzz
2026-10-30 12:49:43 -05:00
parent f40f23dcb9
commit 951aa81055
11 changed files with 1093 additions and 424 deletions
+7 -7
View File
@@ -186,7 +186,7 @@ export const DEFAULT_AI_PROVIDERS: AIProviderSetting[] = [
];
export const LINE_SUGGESTION_PROMPT_RULES =
"IMPORTANT LINE-NUMBER PROTOCOL: The active chapter is provided with 1-based line numbers ([Line 1], [Line 2], …) matching the writing editor gutter. Always cite exact line numbers (e.g. [Line 2]) in your response. Whenever you suggest edits, rewrites, or improvements to specific lines, also emit one or more structured suggestion blocks at the end of your reply in this exact format so the author can highlight the line on screen and click Accept, Deny, or Copy to Note:\n<<<SUGGESTION\nLINE: <line_number>\nTITLE: <short label>\nORIGINAL: <exact original text of that line>\nREPLACEMENT: <your revised replacement text for that line>\nSUMMARY: <1-sentence rationale>\nSUGGESTION>>>";
"IMPORTANT GRANULAR TEXT-ANCHORED PROTOCOL: Anchor every suggested edit to the EXACT verbatim phrase, clause, or single sentence from the manuscript—never rely on line numbers and never replace an entire multi-sentence paragraph at once. Quote the exact target phrase in double quotes (e.g. \"left the mainland before dawn\") in your discussion so the author can click it to highlight that exact phrase in the editor. At the end of your reply, emit 3 to 6 granular suggestion blocks targeting specific phrases, clauses, or sentences in this exact format so the author can step through and click Accept, Deny, or Copy to Note without affecting surrounding sentences:\n<<<SUGGESTION\nTITLE: <short granular edit label, e.g. Sharpen verb phrase / Cut filter word>\nORIGINAL: <exact verbatim phrase, clause, or single sentence copied character-for-character from the chapter>\nREPLACEMENT: <revised text that replaces ONLY the ORIGINAL span>\nSUMMARY: <1-sentence editorial rationale>\nSUGGESTION>>>";
export const DEFAULT_AI_AGENTS: AIAgentConfig[] = [
{
@@ -198,7 +198,7 @@ export const DEFAULT_AI_AGENTS: AIAgentConfig[] = [
maxTokens: 2048,
thinkingEnabled: false,
thinkingEffort: "medium",
systemPrompt: `You are the PubScript Line Editor. Tighten prose, eliminate filter words, sharpen dialogue tags, improve pacing, flag weak openings or closings, and verify consistency against character and world sheets while preserving the author's distinct voice.\n\n${LINE_SUGGESTION_PROMPT_RULES}`,
systemPrompt: `You are the PubScript Line Editor. Tighten prose at the phrase and clause level, eliminate filter words, sharpen verbs and dialogue tags, improve sentence rhythm, and verify consistency against character and world sheets while preserving the author's voice.\n\n${LINE_SUGGESTION_PROMPT_RULES}`,
},
{
id: "reader",
@@ -209,7 +209,7 @@ export const DEFAULT_AI_AGENTS: AIAgentConfig[] = [
maxTokens: 2048,
thinkingEnabled: false,
thinkingEffort: "medium",
systemPrompt: `You are an honest, attentive first-time reader. Report what felt vivid, where your attention drifted, what was confusing, what was telegraphed too early, and what emotional beats paid off by citing exact [Line N] numbers.\n\n${LINE_SUGGESTION_PROMPT_RULES}`,
systemPrompt: `You are an honest, attentive first-time reader. Report what felt vivid, where your attention drifted, what was confusing, what was telegraphed too early, and what emotional beats paid off by quoting exact phrases from the chapter.\n\n${LINE_SUGGESTION_PROMPT_RULES}`,
},
{
id: "publisher",
@@ -220,7 +220,7 @@ export const DEFAULT_AI_AGENTS: AIAgentConfig[] = [
maxTokens: 2048,
thinkingEnabled: false,
thinkingEffort: "medium",
systemPrompt: `You are a literary Publisher and developmental strategist. Evaluate commercial hook, genre pacing, comparable titles (comps), blurb/pitch angles, audience expectations, and structural stakes across acts, referencing specific [Line N] passages.\n\n${LINE_SUGGESTION_PROMPT_RULES}`,
systemPrompt: `You are a literary Publisher and developmental strategist. Evaluate commercial hook, genre pacing, comparable titles (comps), blurb/pitch angles, audience expectations, and structural stakes across acts, quoting exact phrases and sentences.\n\n${LINE_SUGGESTION_PROMPT_RULES}`,
},
{
id: "co-author",
@@ -231,7 +231,7 @@ export const DEFAULT_AI_AGENTS: AIAgentConfig[] = [
maxTokens: 4096,
thinkingEnabled: true,
thinkingEffort: "medium",
systemPrompt: `You are a collaborative Co-Author with full knowledge of the manuscript's characters, world-building, and plot beats. Brainstorm plot turns, draft or revise specific [Line N] passages in the author's established voice, and expand world lore as adoptable line suggestions.\n\n${LINE_SUGGESTION_PROMPT_RULES}`,
systemPrompt: `You are a collaborative Co-Author with full knowledge of the manuscript's characters, world-building, and plot beats. Brainstorm plot turns, propose surgical phrase and sentence revisions in the author's established voice, and expand world lore as granular adoptable suggestions.\n\n${LINE_SUGGESTION_PROMPT_RULES}`,
},
];
@@ -305,10 +305,10 @@ function loadSettings(): AppSettings {
Array.isArray(parsed.aiAgents) && parsed.aiAgents.length > 0
? parsed.aiAgents
: DEFAULT_AI_AGENTS;
// Upgrade any stored built-in agent whose prompt predates the line-number protocol.
// Upgrade any stored built-in agent whose prompt predates the granular text-anchored protocol.
const aiAgents = rawAgents.map((agent) => {
const def = DEFAULT_AI_AGENTS.find((d) => d.id === agent.id);
if (def && !agent.systemPrompt.includes("LINE-NUMBER PROTOCOL") && !agent.systemPrompt.includes("<<<SUGGESTION")) {
if (def && !agent.systemPrompt.includes("GRANULAR TEXT-ANCHORED PROTOCOL")) {
return { ...agent, systemPrompt: def.systemPrompt };
}
return agent;