set project title

This commit is contained in:
2026-05-27 16:36:44 +10:00
parent a08ca39dbb
commit 741d5d6c06
5 changed files with 26 additions and 5 deletions

View File

@@ -1,10 +1,11 @@
import { join } from 'path' import { join, basename } from 'path'
import { homedir } from 'os' import { homedir } from 'os'
import { readFileSync, writeFileSync, mkdirSync } from 'fs' import { readFileSync, writeFileSync, mkdirSync } from 'fs'
export interface GlobalConfig { export interface GlobalConfig {
apiKey?: string apiKey?: string
projectPath?: string projectPath?: string
projectTitle?: string
fontSize?: number fontSize?: number
theme?: 'dark' | 'light' theme?: 'dark' | 'light'
} }
@@ -28,6 +29,9 @@ export const getDraftRoot = (): string =>
export const getApiKey = (): string | undefined => export const getApiKey = (): string | undefined =>
_config.apiKey ?? process.env.ANTHROPIC_API_KEY _config.apiKey ?? process.env.ANTHROPIC_API_KEY
export const getProjectTitle = (): string =>
_config.projectTitle?.trim() || basename(getDraftRoot())
export function readGlobalConfig(): GlobalConfig { export function readGlobalConfig(): GlobalConfig {
return { ..._config } return { ..._config }
} }

View File

@@ -6,7 +6,7 @@ import { listDraftFiles, readMarkdownFile, writeMarkdownFile, getProjectWordCoun
import type { SearchOptions } from './fileSystem' import type { SearchOptions } from './fileSystem'
import { streamMessage, resetClient } from './aiService' import { streamMessage, resetClient } from './aiService'
import type { AIPayload, Attachment } from '../renderer/types/editor' import type { AIPayload, Attachment } from '../renderer/types/editor'
import { readGlobalConfig, writeGlobalConfig, getDraftRoot } from './globalConfig' import { readGlobalConfig, writeGlobalConfig, getProjectTitle } from './globalConfig'
import type { GlobalConfig } from './globalConfig' import type { GlobalConfig } from './globalConfig'
export function registerIpcHandlers(): void { export function registerIpcHandlers(): void {
@@ -278,7 +278,7 @@ export function registerIpcHandlers(): void {
ipcMain.handle('export:projectPdf', async (event): Promise<void> => { ipcMain.handle('export:projectPdf', async (event): Promise<void> => {
const win = BrowserWindow.fromWebContents(event.sender) const win = BrowserWindow.fromWebContents(event.sender)
const projectName = basename(getDraftRoot()) const projectName = getProjectTitle()
const result = await dialog.showSaveDialog(win!, { const result = await dialog.showSaveDialog(win!, {
defaultPath: `${projectName}.pdf`, defaultPath: `${projectName}.pdf`,

View File

@@ -10,6 +10,7 @@ interface Props {
export function SettingsDialog({ onClose, onProjectChanged, isSetup }: Props): JSX.Element { export function SettingsDialog({ onClose, onProjectChanged, isSetup }: Props): JSX.Element {
const [apiKey, setApiKey] = useState('') const [apiKey, setApiKey] = useState('')
const [projectPath, setProjectPath] = useState('') const [projectPath, setProjectPath] = useState('')
const [projectTitle, setProjectTitle] = useState('')
const [originalPath, setOriginalPath] = useState('') const [originalPath, setOriginalPath] = useState('')
const [saved, setSaved] = useState(false) const [saved, setSaved] = useState(false)
const overlayRef = useRef<HTMLDivElement>(null) const overlayRef = useRef<HTMLDivElement>(null)
@@ -18,6 +19,7 @@ export function SettingsDialog({ onClose, onProjectChanged, isSetup }: Props): J
window.api.readConfig().then((cfg) => { window.api.readConfig().then((cfg) => {
setApiKey(cfg.apiKey ?? '') setApiKey(cfg.apiKey ?? '')
setProjectPath(cfg.projectPath ?? '') setProjectPath(cfg.projectPath ?? '')
setProjectTitle(cfg.projectTitle ?? '')
setOriginalPath(cfg.projectPath ?? '') setOriginalPath(cfg.projectPath ?? '')
}) })
}, []) }, [])
@@ -41,7 +43,7 @@ export function SettingsDialog({ onClose, onProjectChanged, isSetup }: Props): J
} }
const handleSave = async (): Promise<void> => { const handleSave = async (): Promise<void> => {
await window.api.writeConfig({ apiKey: apiKey || undefined, projectPath: projectPath || undefined }) await window.api.writeConfig({ apiKey: apiKey || undefined, projectPath: projectPath || undefined, projectTitle: projectTitle.trim() || undefined })
if (projectPath !== originalPath) onProjectChanged?.() if (projectPath !== originalPath) onProjectChanged?.()
setOriginalPath(projectPath) setOriginalPath(projectPath)
setSaved(true) setSaved(true)
@@ -79,6 +81,20 @@ export function SettingsDialog({ onClose, onProjectChanged, isSetup }: Props): J
</div> </div>
</div> </div>
<div className="settings-field">
<label className="settings-label" htmlFor="settings-project-title">Project Title</label>
<input
id="settings-project-title"
className="settings-input"
type="text"
value={projectTitle}
onChange={(e) => setProjectTitle(e.target.value)}
placeholder="My Novel"
spellCheck={false}
/>
<p className="settings-hint">Used as the title in PDF exports. Defaults to the project folder name if left blank.</p>
</div>
<div className="settings-field"> <div className="settings-field">
<label className="settings-label" htmlFor="settings-api-key">Anthropic API Key</label> <label className="settings-label" htmlFor="settings-api-key">Anthropic API Key</label>
<input <input

View File

@@ -1,6 +1,7 @@
export interface GlobalConfig { export interface GlobalConfig {
apiKey?: string apiKey?: string
projectPath?: string projectPath?: string
projectTitle?: string
} }
export interface FileNode { export interface FileNode {

File diff suppressed because one or more lines are too long