✨ Publisher pack
This commit is contained in:
@@ -9,6 +9,7 @@ const sessionFile = (): string => join(hohoffDir(), 'session.json')
|
||||
const telemetryFile = (): string => join(hohoffDir(), 'telemetry.json')
|
||||
const revisionsDir = (): string => join(hohoffDir(), 'revisions')
|
||||
export const getStoryBiblePath = (): string => join(hohoffDir(), 'Story Bible.md')
|
||||
export const getPublisherPackPath = (): string => join(hohoffDir(), 'Publisher Pack.md')
|
||||
const submissionsFile = (): string => join(hohoffDir(), 'submissions.json')
|
||||
|
||||
const STORY_BIBLE_TEMPLATE = `# Story Bible
|
||||
@@ -257,6 +258,27 @@ export async function openStoryBibleFile(): Promise<{ path: string; content: str
|
||||
return { path: getStoryBiblePath(), content }
|
||||
}
|
||||
|
||||
const PUBLISHER_PACK_TEMPLATE = `# Synopsis
|
||||
|
||||
<!-- A concise summary of the novel's plot, themes, and arc -->
|
||||
|
||||
# Author Bio
|
||||
|
||||
<!-- A short professional biography in third person -->
|
||||
`
|
||||
|
||||
export async function openPublisherPackFile(): Promise<{ path: string; content: string }> {
|
||||
await mkdir(hohoffDir(), { recursive: true })
|
||||
let content: string
|
||||
try {
|
||||
content = await readFile(getPublisherPackPath(), 'utf-8')
|
||||
} catch {
|
||||
content = PUBLISHER_PACK_TEMPLATE
|
||||
await writeFile(getPublisherPackPath(), content, 'utf-8')
|
||||
}
|
||||
return { path: getPublisherPackPath(), content }
|
||||
}
|
||||
|
||||
// Parse a markdown document into a preamble (text before first ## heading) and
|
||||
// an ordered list of { header, body } sections delimited by ## headings.
|
||||
function parseSections(content: string): { preamble: string; sections: { header: string; body: string }[] } {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ipcMain, dialog, BrowserWindow } from 'electron'
|
||||
import { readFileSync, writeFileSync, unlinkSync } from 'fs'
|
||||
import { extname, basename, join } from 'path'
|
||||
import { tmpdir } from 'os'
|
||||
import { listDraftFiles, readMarkdownFile, writeMarkdownFile, getProjectWordCount, saveOrderFile, readSession, writeSession, saveRevision, listRevisions, loadRevision, deleteRevision, renameFileOrDir, deleteFileOrDir, createMarkdownFile, createSubdirectory, moveFileOrDir, readStoryBibleFile, openStoryBibleFile, writeStoryBibleFile, searchAcrossFiles, replaceInFiles, readAllDraftFiles, readProjectConfig, writeProjectConfig, PROJECT_CONFIG_FIELDS, readTelemetry, readSubmissions, writeSubmissions } from './fileSystem'
|
||||
import { listDraftFiles, readMarkdownFile, writeMarkdownFile, getProjectWordCount, saveOrderFile, readSession, writeSession, saveRevision, listRevisions, loadRevision, deleteRevision, renameFileOrDir, deleteFileOrDir, createMarkdownFile, createSubdirectory, moveFileOrDir, readStoryBibleFile, openStoryBibleFile, writeStoryBibleFile, openPublisherPackFile, searchAcrossFiles, replaceInFiles, readAllDraftFiles, readProjectConfig, writeProjectConfig, PROJECT_CONFIG_FIELDS, readTelemetry, readSubmissions, writeSubmissions } from './fileSystem'
|
||||
import type { SearchOptions, ProjectConfig } from './fileSystem'
|
||||
import { streamMessage, resetClient } from './aiService'
|
||||
import { onWordSnapshot, flushTelemetry } from './telemetry'
|
||||
@@ -91,6 +91,10 @@ export function registerIpcHandlers(): void {
|
||||
return await writeStoryBibleFile(content)
|
||||
})
|
||||
|
||||
ipcMain.handle('fs:openPublisherPack', async () => {
|
||||
return await openPublisherPackFile()
|
||||
})
|
||||
|
||||
ipcMain.handle('fs:pickAttachments', async (event): Promise<Attachment[]> => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
const result = await dialog.showOpenDialog(win!, {
|
||||
|
||||
@@ -95,6 +95,9 @@ contextBridge.exposeInMainWorld('api', {
|
||||
openStoryBible: (): Promise<{ path: string; content: string }> =>
|
||||
ipcRenderer.invoke('fs:openStoryBible'),
|
||||
|
||||
openPublisherPack: (): Promise<{ path: string; content: string }> =>
|
||||
ipcRenderer.invoke('fs:openPublisherPack'),
|
||||
|
||||
writeStoryBible: (content: string): Promise<string> =>
|
||||
ipcRenderer.invoke('fs:writeStoryBible', content),
|
||||
|
||||
|
||||
@@ -38,6 +38,15 @@ export default function App(): JSX.Element {
|
||||
console.error('[App] openStoryBible failed:', err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpenPublisherPack = async (): Promise<void> => {
|
||||
try {
|
||||
const { path, content } = await window.api.openPublisherPack()
|
||||
setActiveFile(path, content)
|
||||
} catch (err) {
|
||||
console.error('[App] openPublisherPack failed:', err)
|
||||
}
|
||||
}
|
||||
const [sidebarOpen, setSidebarOpen] = useState(
|
||||
() => localStorage.getItem('sidebarOpen') !== 'false'
|
||||
)
|
||||
@@ -212,16 +221,23 @@ export default function App(): JSX.Element {
|
||||
<button
|
||||
className="sidebar-nav-btn"
|
||||
onClick={handleOpenStoryBible}
|
||||
title="Open Story Bible"
|
||||
title="Story Bible"
|
||||
>
|
||||
<span className="sidebar-nav-icon">📖</span>
|
||||
</button>
|
||||
<button
|
||||
className="sidebar-nav-btn"
|
||||
onClick={handleOpenPublisherPack}
|
||||
title="Publisher Pack"
|
||||
>
|
||||
<span className="sidebar-nav-label">Pack</span>
|
||||
</button>
|
||||
<button
|
||||
className={`sidebar-nav-btn${showSubmissions ? ' active' : ''}`}
|
||||
onClick={goSubmissions}
|
||||
title="Submissions"
|
||||
>
|
||||
<span className="sidebar-nav-icon">✉</span>
|
||||
<span className="sidebar-nav-label">Sub</span>
|
||||
</button>
|
||||
</div>
|
||||
<FileTree />
|
||||
|
||||
@@ -207,6 +207,15 @@
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sidebar-nav-label {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* ─── Editor area ──────────────────────────────────────────────── */
|
||||
.editor-area {
|
||||
grid-row: 2;
|
||||
|
||||
1
src/renderer/types/global.d.ts
vendored
1
src/renderer/types/global.d.ts
vendored
@@ -32,6 +32,7 @@ declare global {
|
||||
moveFile: (sourcePath: string, targetDirPath: string) => Promise<string>
|
||||
openStoryBible: () => Promise<{ path: string; content: string }>
|
||||
writeStoryBible: (content: string) => Promise<string>
|
||||
openPublisherPack: () => Promise<{ path: string; content: string }>
|
||||
onMenuAction: (handler: (action: string) => void) => () => void
|
||||
searchFiles: (query: string, options: SearchOptions) => Promise<SearchFileResult[]>
|
||||
replaceInFiles: (query: string, replacement: string, options: SearchOptions, filePaths: string[]) => Promise<string[]>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user