mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 11:07:04 +00:00
refactor(ui): call a book a project
A book is the use case Margin was built for, not the limit of it. Blog posts published straight to the web are coming, and "New book" on the shelf is the wrong invitation for that. Project covers both without promising anything the app does not do. This is user-visible strings only. Internal identifiers keep saying book, because renaming bookId and setBooks is churn nobody reads. Claude-Session: https://claude.ai/code/session_018HwEWvoE1NkkUjJCMLSnup
This commit is contained in:
1 parent
42996804bb
commit
7eeb54f653
11 files changed
+21
-21
No files matched your search
@@ -18,7 +18,7 @@ use tauri::{Emitter, Runtime};
|
|||||||
fn build_menu<R: Runtime>(handle: &tauri::AppHandle<R>) -> tauri::Result<Menu<R>> {
|
fn build_menu<R: Runtime>(handle: &tauri::AppHandle<R>) -> tauri::Result<Menu<R>> {
|
||||||
let menu = Menu::default(handle)?;
|
let menu = Menu::default(handle)?;
|
||||||
|
|
||||||
let new_book = MenuItemBuilder::with_id("new-book", "New Book")
|
let new_book = MenuItemBuilder::with_id("new-book", "New Project")
|
||||||
.accelerator("CmdOrCtrl+N")
|
.accelerator("CmdOrCtrl+N")
|
||||||
.build(handle)?;
|
.build(handle)?;
|
||||||
let save = MenuItemBuilder::with_id("save", "Save")
|
let save = MenuItemBuilder::with_id("save", "Save")
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub struct BookSummary {
|
|||||||
fn corrupt_summary(stem: &str) -> BookSummary {
|
fn corrupt_summary(stem: &str) -> BookSummary {
|
||||||
BookSummary {
|
BookSummary {
|
||||||
id: stem.to_string(),
|
id: stem.to_string(),
|
||||||
title: "Unreadable book".to_string(),
|
title: "Unreadable project".to_string(),
|
||||||
author: String::new(),
|
author: String::new(),
|
||||||
corrupt: true,
|
corrupt: true,
|
||||||
updated_at: 0,
|
updated_at: 0,
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export function BackupSettings() {
|
|||||||
disabled={working || remoteCount === 0}
|
disabled={working || remoteCount === 0}
|
||||||
onClick={() => setConfirmRestore(true)}
|
onClick={() => setConfirmRestore(true)}
|
||||||
>
|
>
|
||||||
{remoteCount === null ? "Restore from Drive" : `Restore ${remoteCount} book${remoteCount === 1 ? "" : "s"}`}
|
{remoteCount === null ? "Restore from Drive" : `Restore ${remoteCount} project${remoteCount === 1 ? "" : "s"}`}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export function CoverView() {
|
|||||||
<Icon d="M4 5h16v14H4zM4 16l4.5-4.5 3 3L16 10l4 4" /> Upload image
|
<Icon d="M4 5h16v14H4zM4 16l4.5-4.5 3 3L16 10l4 4" /> Upload image
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="cover-hint">Title, subtitle & author are set in Book setup.</p>
|
<p className="cover-hint">Title, subtitle & author are set in Project setup.</p>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ export function EditorView() {
|
|||||||
<Icon d={icons.SIDEBAR} />
|
<Icon d={icons.SIDEBAR} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button className="doc-title" onClick={() => setSettingsOpen(true)} title="Book setup">
|
<button className="doc-title" onClick={() => setSettingsOpen(true)} title="Project setup">
|
||||||
{book.metadata.title || "Untitled"}
|
{book.metadata.title || "Untitled"}
|
||||||
{dirty && <span className="dirty-dot" />}
|
{dirty && <span className="dirty-dot" />}
|
||||||
</button>
|
</button>
|
||||||
@@ -491,7 +491,7 @@ export function EditorView() {
|
|||||||
|
|
||||||
{escArmed && (
|
{escArmed && (
|
||||||
<div className="esc-hint">
|
<div className="esc-hint">
|
||||||
Press <kbd>Esc</kbd> again for All books
|
Press <kbd>Esc</kbd> again for All projects
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ function ExportPreviewModal() {
|
|||||||
<div className="preview-stage">
|
<div className="preview-stage">
|
||||||
<div className="preview-loading">
|
<div className="preview-loading">
|
||||||
<div className="spinner spinner-dark" />
|
<div className="spinner spinner-dark" />
|
||||||
<p>Typesetting your book…</p>
|
<p>Typesetting your project…</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ export function FindBar({ editor, open, initialExpanded, onClose }: FindBarProps
|
|||||||
title="Replace All scope"
|
title="Replace All scope"
|
||||||
onClick={() => setScope((s) => (s === "chapter" ? "book" : "chapter"))}
|
onClick={() => setScope((s) => (s === "chapter" ? "book" : "chapter"))}
|
||||||
>
|
>
|
||||||
{scope === "chapter" ? "This chapter" : "Whole book"}
|
{scope === "chapter" ? "This chapter" : "Whole project"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
|||||||
const openById = (id: string) =>
|
const openById = (id: string) =>
|
||||||
loadBook(id)
|
loadBook(id)
|
||||||
.then(onOpen)
|
.then(onOpen)
|
||||||
.catch((e) => setNotice(`Could not open book: ${e}`));
|
.catch((e) => setNotice(`Could not open project: ${e}`));
|
||||||
|
|
||||||
const removeBook = async () => {
|
const removeBook = async () => {
|
||||||
if (!pendingDelete) return;
|
if (!pendingDelete) return;
|
||||||
@@ -109,7 +109,7 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
|||||||
<div className="shelf">
|
<div className="shelf">
|
||||||
<button className="card card-action" onClick={() => createAndOpenBook(onOpen, setNotice)}>
|
<button className="card card-action" onClick={() => createAndOpenBook(onOpen, setNotice)}>
|
||||||
<Icon d="M12 5v14M5 12h14" size={20} />
|
<Icon d="M12 5v14M5 12h14" size={20} />
|
||||||
<span>New book</span>
|
<span>New project</span>
|
||||||
</button>
|
</button>
|
||||||
<button className="card card-action" onClick={handleImport} disabled={busy}>
|
<button className="card card-action" onClick={handleImport} disabled={busy}>
|
||||||
<Icon d="M12 3v10m0 0l-4-4m4 4l4-4M5 19h14" size={20} />
|
<Icon d="M12 3v10m0 0l-4-4m4 4l4-4M5 19h14" size={20} />
|
||||||
@@ -126,7 +126,7 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
|||||||
<div key={b.id} className="card card-book card-corrupt">
|
<div key={b.id} className="card card-book card-corrupt">
|
||||||
<span className="card-title">{b.title}</span>
|
<span className="card-title">{b.title}</span>
|
||||||
<span className="card-author">Couldn't be read; a .bak backup may sit beside it.</span>
|
<span className="card-author">Couldn't be read; a .bak backup may sit beside it.</span>
|
||||||
<RowMenu label="Book options" className="card-menu" onDelete={() => setPendingDelete(b)} />
|
<RowMenu label="Project options" className="card-menu" onDelete={() => setPendingDelete(b)} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
@@ -145,7 +145,7 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
|||||||
<span className="card-title">{b.title || "Untitled"}</span>
|
<span className="card-title">{b.title || "Untitled"}</span>
|
||||||
{b.author && <span className="card-author">{b.author}</span>}
|
{b.author && <span className="card-author">{b.author}</span>}
|
||||||
{b.updatedAt > 0 && <span className="card-meta">Edited {relativeTime(b.updatedAt, now)}</span>}
|
{b.updatedAt > 0 && <span className="card-meta">Edited {relativeTime(b.updatedAt, now)}</span>}
|
||||||
<RowMenu label="Book options" className="card-menu" onDelete={() => setPendingDelete(b)} />
|
<RowMenu label="Project options" className="card-menu" onDelete={() => setPendingDelete(b)} />
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
@@ -165,10 +165,10 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
|||||||
)}
|
)}
|
||||||
{pendingDelete && (
|
{pendingDelete && (
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
title="Delete book"
|
title="Delete project"
|
||||||
message={
|
message={
|
||||||
<>
|
<>
|
||||||
Delete <strong>{pendingDelete.title || "Untitled"}</strong>? This permanently removes the book and all its
|
Delete <strong>{pendingDelete.title || "Untitled"}</strong>? This permanently removes the project and all its
|
||||||
chapters.
|
chapters.
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,19 +50,19 @@ export function MoveChapterDialog({ chapter, label, onClose }: MoveChapterDialog
|
|||||||
<div className="overlay" onClick={moving ? undefined : onClose}>
|
<div className="overlay" onClick={moving ? undefined : onClose}>
|
||||||
<div ref={panelRef} className="panel panel-move" onClick={(e) => e.stopPropagation()}>
|
<div ref={panelRef} className="panel panel-move" onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="panel-head">
|
<div className="panel-head">
|
||||||
<h2>Move to book</h2>
|
<h2>Move to project</h2>
|
||||||
<button className="icon-btn" onClick={onClose} title="Close">
|
<button className="icon-btn" onClick={onClose} title="Close">
|
||||||
<Icon d="M6 6l12 12M18 6L6 18" />
|
<Icon d="M6 6l12 12M18 6L6 18" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-body">
|
<div className="panel-body">
|
||||||
<p className="confirm-text">
|
<p className="confirm-text">
|
||||||
Move <strong>{label}</strong> out of this book and into:
|
Move <strong>{label}</strong> out of this project and into:
|
||||||
</p>
|
</p>
|
||||||
{books === null ? (
|
{books === null ? (
|
||||||
<p className="move-empty">Loading books…</p>
|
<p className="move-empty">Loading projects…</p>
|
||||||
) : books.length === 0 ? (
|
) : books.length === 0 ? (
|
||||||
<p className="move-empty">There is no other book to move this into yet.</p>
|
<p className="move-empty">There is no other project to move this into yet.</p>
|
||||||
) : (
|
) : (
|
||||||
<ul className="move-list">
|
<ul className="move-list">
|
||||||
{books.map((b) => (
|
{books.map((b) => (
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export function Settings({ onClose, onSave }: { onClose: () => void; onSave: ()
|
|||||||
<div className="overlay" onClick={onClose}>
|
<div className="overlay" onClick={onClose}>
|
||||||
<div ref={panelRef} className="panel" onClick={(e) => e.stopPropagation()}>
|
<div ref={panelRef} className="panel" onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="panel-head">
|
<div className="panel-head">
|
||||||
<h2>Book setup</h2>
|
<h2>Project setup</h2>
|
||||||
<button className="icon-btn" onClick={onClose} title="Close">
|
<button className="icon-btn" onClick={onClose} title="Close">
|
||||||
<Icon d={icons.CLOSE} />
|
<Icon d={icons.CLOSE} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -169,9 +169,9 @@ export function Sidebar({ onNavigate }: { onNavigate?: () => void }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="sidebar">
|
<aside className="sidebar">
|
||||||
<button className="brand" onClick={closeBook} title="All books">
|
<button className="brand" onClick={closeBook} title="All projects">
|
||||||
<Icon d="M14 7l-5 5 5 5" size={15} />
|
<Icon d="M14 7l-5 5 5 5" size={15} />
|
||||||
<span className="back-label">All books</span>
|
<span className="back-label">All projects</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="cover-item"
|
className="cover-item"
|
||||||
|
|||||||
Reference in new issue
Block a user