feat: resolve all 22 items in Features and Bugs.txt and complete UI polish

This commit is contained in:
Schwitzz
2026-10-30 10:57:13 -05:00
parent 7214cee8cb
commit 4add429469
42 changed files with 6360 additions and 1805 deletions
+5
View File
@@ -72,6 +72,11 @@ export function usePubScriptEditor(options: PubScriptEditorHookOptions = {}) {
const editor = useEditor({
editable,
extensions: pubScriptExtensions,
editorProps: {
attributes: {
class: "ps-editor-content",
},
},
...(content !== undefined ? { content } : {}),
...useEditorOptions,
});
+113 -2
View File
@@ -17,7 +17,14 @@
import StarterKit from "@tiptap/starter-kit";
import { TextStyle } from "@tiptap/extension-text-style";
import { Editor as TipTapEditor, getSchema, type Content } from "@tiptap/core";
import {
Editor as TipTapEditor,
Extension,
Mark,
getSchema,
mergeAttributes,
type Content,
} from "@tiptap/core";
import SceneBreakNode from "./nodes/SceneBreak.js";
import FootnoteInline from "./nodes/FootnoteInline.js";
@@ -37,6 +44,90 @@ import {
colorAttribute,
} from "./constants.js";
/**
* Inline highlight mark (`<mark class="ps-mark-highlight">`).
*/
export const HighlightMark = Mark.create({
name: "highlight",
parseHTML() {
return [{ tag: "mark" }];
},
renderHTML({ HTMLAttributes }) {
return ["mark", mergeAttributes({ class: "ps-mark-highlight" }, HTMLAttributes), 0];
},
});
/**
* Inline superscript mark (`<sup>`).
*/
export const SuperscriptMark = Mark.create({
name: "superscript",
excludes: "subscript",
parseHTML() {
return [{ tag: "sup" }];
},
renderHTML({ HTMLAttributes }) {
return ["sup", HTMLAttributes, 0];
},
});
/**
* Inline subscript mark (`<sub>`).
*/
export const SubscriptMark = Mark.create({
name: "subscript",
excludes: "superscript",
parseHTML() {
return [{ tag: "sub" }];
},
renderHTML({ HTMLAttributes }) {
return ["sub", HTMLAttributes, 0];
},
});
/**
* Block-level text alignment extension (`left` | `center` | `right` | `justify`)
* applied to paragraphs, headings, and blockquotes.
*/
export const TextAlignExtension = Extension.create({
name: "textAlign",
addGlobalAttributes() {
return [
{
types: ["heading", "paragraph", "blockquote"],
attributes: {
textAlign: {
default: "left",
parseHTML: (element: HTMLElement) => element.style.textAlign || "left",
renderHTML: (attributes: { textAlign?: string }) => {
if (!attributes.textAlign || attributes.textAlign === "left") {
return {};
}
return { style: `text-align: ${attributes.textAlign}` };
},
},
lineHeight: {
default: null,
parseHTML: (element: HTMLElement) => element.style.lineHeight || null,
renderHTML: (attributes: { lineHeight?: string | null }) => {
if (!attributes.lineHeight) return {};
return { style: `line-height: ${attributes.lineHeight}` };
},
},
textIndent: {
default: null,
parseHTML: (element: HTMLElement) => element.style.textIndent || null,
renderHTML: (attributes: { textIndent?: string | null }) => {
if (!attributes.textIndent) return {};
return { style: `text-indent: ${attributes.textIndent}` };
},
},
},
},
];
},
});
/**
* The full extension list, registered once. Order matters: base kits first,
* then text styling, then PubScript nodes.
@@ -44,8 +135,28 @@ import {
export const pubScriptExtensions = [
StarterKit,
TextStyle.extend({
addAttributes: colorAttribute,
addAttributes() {
return {
...colorAttribute(),
fontFamily: {
default: null,
parseHTML: (element: HTMLElement) => element.style.fontFamily || null,
renderHTML: (attributes: { fontFamily?: string | null }) =>
attributes.fontFamily ? { style: `font-family: ${attributes.fontFamily}` } : {},
},
fontSize: {
default: null,
parseHTML: (element: HTMLElement) => element.style.fontSize || null,
renderHTML: (attributes: { fontSize?: string | null }) =>
attributes.fontSize ? { style: `font-size: ${attributes.fontSize}` } : {},
},
};
},
}),
HighlightMark,
SuperscriptMark,
SubscriptMark,
TextAlignExtension,
SceneBreakNode,
FootnoteInline,
FootnoteArea,