aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-06-10 19:06:31 -0400
committerbobzel <zzzman@gmail.com>2025-06-10 19:06:31 -0400
commit403dcfb5e8b659f62ed51212ede3f5807caa58c6 (patch)
tree8d72f2d4c3a22c13c8f53f8e82355a887333f4ac /src
parent272534c8a7517d08c70928c96d487d84b14772f6 (diff)
cleanup and streamlining of scrapbook code.
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx112
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx44
-rw-r--r--src/client/views/nodes/scrapbook/ScrapbookBox.scss87
-rw-r--r--src/client/views/nodes/scrapbook/ScrapbookBox.tsx645
-rw-r--r--src/client/views/nodes/scrapbook/ScrapbookPreset.tsx169
-rw-r--r--src/client/views/nodes/scrapbook/ScrapbookPresetRegistry.ts20
7 files changed, 353 insertions, 727 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4ad9c9bd8..aea636040 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -331,6 +331,9 @@ export class DocumentOptions {
toolType?: string; // type of pen tool
expertMode?: BOOLt = new BoolInfo('something available only in expert (not novice) mode');
+ placeholder_docType?: STRt = new StrInfo('type of document that this document accepts as a child', false, false, Array.from(Object.keys(DocumentType)));
+ placeholder_acceptTags?: List<string>;
+
contextMenuFilters?: List<ScriptField>;
contextMenuScripts?: List<ScriptField>;
contextMenuLabels?: List<string>;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b2b904509..12515a72c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -81,14 +81,11 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
@observable _labelsVisibile: boolean = false;
@observable _lassoPts: [number, number][] = [];
@observable _lassoFreehand: boolean = false;
- // ─── New Observables for “Pick 1 of N AI Scrapbook” ───
- @observable aiChoices: Doc[] = []; // temporary hidden Scrapbook docs
- @observable pickerX = 0; // popup x coordinate
- @observable pickerY = 0; // popup y coordinate
- @observable pickerVisible = false; // show/hide ScrapbookPicker
-
-
-
+ // ─── New Observables for “Pick 1 of N AI Scrapbook” ───
+ @observable aiChoices: Doc[] = []; // temporary hidden Scrapbook docs
+ @observable pickerX = 0; // popup x coordinate
+ @observable pickerY = 0; // popup y coordinate
+ @observable pickerVisible = false; // show/hide ScrapbookPicker
@computed get Transform() {
return this._props.getTransform();
@@ -532,42 +529,39 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
MarqueeOptionsMenu.Instance.fadeOut(true);
});
-
getAiPresetsDescriptors(): DocumentDescriptor[] {
- const selected = this.marqueeSelect(false);
- return selected.map((doc) => ({
- type: typeof doc.$type === 'string' ? doc.$type : 'UNKNOWN',
- tags: (() => {
- const s = new Set<string>();
- StrListCast(doc.$tags_chat ?? new List<string>()).forEach((t) => s.add(t));
- return Array.from(s);
- })(),
- }));
- }
-
+ const selected = this.marqueeSelect(false);
+ return selected.map(doc => ({
+ type: typeof doc.$type === 'string' ? doc.$type : 'UNKNOWN',
+ tags: (() => {
+ const s = new Set<string>();
+ StrListCast(doc.$tags_chat ?? new List<string>()).forEach(t => s.add(t));
+ return Array.from(s);
+ })(),
+ }));
+ }
generateScrapbook = action(async () => {
-
const selectedDocs = this.marqueeSelect(false);
if (!selectedDocs.length) return;
- const descriptors = this.getAiPresetsDescriptors();
- if (descriptors.length === 0) {
- alert('No documents selected to generate a scrapbook from!');
- return;
- }
+ const descriptors = this.getAiPresetsDescriptors();
+ if (descriptors.length === 0) {
+ alert('No documents selected to generate a scrapbook from!');
+ return;
+ }
const aiPreset = await requestAiGeneratedPreset(descriptors);
if (!aiPreset.length) {
- alert("Failed to generate preset");
+ alert('Failed to generate preset');
return;
}
const scrapbookPlaceholders: Doc[] = buildPlaceholdersFromConfigs(aiPreset);
/*
const scrapbookPlaceholders: Doc[] = aiPreset.map(cfg => {
const placeholderDoc = Docs.Create.TextDocument(cfg.tag);
- placeholderDoc.accepts_docType = cfg.type as DocumentType;
- placeholderDoc.accepts_tagType = new List<string>(cfg.acceptTags ?? [cfg.tag]);
+ placeholderDoc.placeholder_docType = cfg.type as DocumentType;
+ placeholderDoc.placeholder_acceptTags = new List<string>(cfg.acceptTags ?? [cfg.tag]);
const placeholder = new Doc();
placeholder.proto = placeholderDoc;
@@ -586,25 +580,17 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
y: this.Bounds.top,
_width: 500,
_height: 500,
- title: 'AI-generated Scrapbook'
+ title: 'AI-generated Scrapbook',
});
-
-
// 3) Now grab that new scrapbook’s flat placeholders
const flatPl = DocListCast(scrapbook[Doc.LayoutDataKey(scrapbook)]) as Doc[];
- const unwrap = (items: Doc[]): Doc[] =>
- items.flatMap(d =>
- d.$type === DocumentType.COL
- ? unwrap(DocListCast(d[Doc.LayoutDataKey(d)]))
- : [d]
- );
+ const unwrap = (items: Doc[]): Doc[] => items.flatMap(d => (d.$type === DocumentType.COL ? unwrap(DocListCast(d[Doc.LayoutDataKey(d)])) : [d]));
const allPlaceholders = unwrap(flatPl);
// 4) Slot each selectedDocs[i] into the first matching placeholder
selectedDocs.forEach(realDoc => {
- slotRealDocIntoPlaceholders(realDoc, allPlaceholders
- );
+ slotRealDocIntoPlaceholders(realDoc, allPlaceholders);
});
const selected = this.marqueeSelect(false).map(d => {
@@ -625,9 +611,6 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
this.hideMarquee();
});
-
-
-
@action
marqueeCommand = (e: KeyboardEvent) => {
const ee = e as unknown as KeyboardEvent & { propagationIsStopped?: boolean };
@@ -649,7 +632,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
if (e.key === 'g') this.collection(e, true);
if (e.key === 'c' || e.key === 't') this.collection(e);
if (e.key === 's' || e.key === 'S') this.summary();
- if (e.key === 'g' || e.key === 'G') this.generateScrapbook(); // ← scrapbook shortcut
+ if (e.key === 'g' || e.key === 'G') this.generateScrapbook(); // ← scrapbook shortcut
if (e.key === 'p') this.pileup();
this.cleanupInteractions(false);
}
@@ -795,27 +778,26 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
render() {
return (
<>
- <div
- className="marqueeView"
- ref={r => {
- r?.addEventListener('dashDragMovePause', this.onDragMovePause as EventListenerOrEventListenerObject);
- this.MarqueeRef = r;
- }}
- style={{
- overflow: StrCast(this._props.Document._overflow),
- cursor: Doc.ActiveTool === InkTool.Ink || this._visible ? 'crosshair' : 'pointer',
- }}
- onDragOver={e => e.preventDefault()}
- onScroll={e => {
- e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0;
- }}
- onClick={this.onClick}
- onPointerDown={this.onPointerDown}>
- {this._visible ? this.marqueeDiv : null}
- {this.props.children}
- </div>
- </>
-
+ <div
+ className="marqueeView"
+ ref={r => {
+ r?.addEventListener('dashDragMovePause', this.onDragMovePause as EventListenerOrEventListenerObject);
+ this.MarqueeRef = r;
+ }}
+ style={{
+ overflow: StrCast(this._props.Document._overflow),
+ cursor: Doc.ActiveTool === InkTool.Ink || this._visible ? 'crosshair' : 'pointer',
+ }}
+ onDragOver={e => e.preventDefault()}
+ onScroll={e => {
+ e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0;
+ }}
+ onClick={this.onClick}
+ onPointerDown={this.onPointerDown}>
+ {this._visible ? this.marqueeDiv : null}
+ {this.props.children}
+ </div>
+ </>
);
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0c3179173..1768eb08d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -8,7 +8,6 @@ import { baseKeymap, selectAll, splitBlock } from 'prosemirror-commands';
import { history } from 'prosemirror-history';
import { inputRules } from 'prosemirror-inputrules';
import { keymap } from 'prosemirror-keymap';
-import { runInAction } from 'mobx';
import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transaction } from 'prosemirror-state';
import { EditorView, NodeViewConstructor } from 'prosemirror-view';
@@ -65,7 +64,6 @@ import { removeMarkWithAttrs } from './prosemirrorPatches';
import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from './RichTextRules';
import { schema } from './schema_rts';
-import { tickStep } from 'd3';
// import * as applyDevTools from 'prosemirror-dev-tools';
export interface FormattedTextBoxProps extends FieldViewProps {
@@ -310,30 +308,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
};
- autoTag = async () => {
+ autoTag = () => {
+ const rawText = RTFCast(this.Document[this.fieldKey])?.Text ?? StrCast(this.Document[this.fieldKey]);
+ const callType = rawText.includes('[placeholder]') ? GPTCallType.CLASSIFYTEXTMINIMAL : GPTCallType.CLASSIFYTEXTFULL;
- const layoutKey = Doc.LayoutDataKey(this.Document);
- const rawText = RTFCast(this.Document[layoutKey])?.Text ?? StrCast(this.Document[layoutKey]);
-
- const callType = rawText.includes("[placeholder]")
- ? GPTCallType.CLASSIFYTEXTMINIMAL
- : GPTCallType.CLASSIFYTEXTFULL;
-
- gptAPICall(rawText, callType).then(desc => {
- runInAction(() => {
- // Clear existing tags
- this.Document.$tags_chat = new List<string>();
-
- // Split GPT response into tokens and push individually
- const tokens = desc.trim().split(/\s+/);
- tokens.forEach(tok => {
- (this.Document.$tags_chat as List<string>).push(tok);
- });
-
- this.Document._layout_showTags = true;
- });
- });
-};
+ gptAPICall(rawText, callType).then(
+ action(desc => {
+ // Split GPT response into tokens and push individually & clear existing tags
+ this.Document.$tags_chat = new List<string>(desc.trim().split(/\s+/));
+ this.Document._layout_showTags = true;
+ })
+ );
+ };
leafText = (node: Node) => {
if (node.type === this.EditorView?.state.schema.nodes.dashField) {
@@ -1298,13 +1284,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
{ fireImmediately: true }
);
- this._disposers.tagger = reaction(
- () => ({ title: this.Document.title, sel: this._props.isSelected() }),
- action(() => {
- this.autoTag();
- }),
- { fireImmediately: true }
- );
+ this._disposers.tagger = reaction(() => ({ title: this.Document.title, sel: this._props.isSelected() }), this.autoTag, { fireImmediately: true });
if (!this._props.dontRegisterView) {
this._disposers.record = reaction(
diff --git a/src/client/views/nodes/scrapbook/ScrapbookBox.scss b/src/client/views/nodes/scrapbook/ScrapbookBox.scss
index 8dc93df60..6ac2220f9 100644
--- a/src/client/views/nodes/scrapbook/ScrapbookBox.scss
+++ b/src/client/views/nodes/scrapbook/ScrapbookBox.scss
@@ -1,63 +1,66 @@
-
.scrapbook-box {
- /* Make sure the container fills its parent, and set a base background */
- position: relative; /* so that absolute children (loading overlay, etc.) are positioned relative to this */
- width: 100%;
- height: 100%;
- background: beige;
- overflow: hidden; /* prevent scrollbars if children overflow */
+ /* Make sure the container fills its parent, and set a base background */
+ position: relative; /* so that absolute children (loading overlay, etc.) are positioned relative to this */
+ width: 100%;
+ height: 100%;
+ background: beige;
+ overflow: hidden; /* prevent scrollbars if children overflow */
}
/* Loading overlay that covers the entire scrapbook while AI-generation is in progress */
.scrapbook-box-loading-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- background: rgba(255, 255, 255, 0.8);
- z-index: 10; /* sits above the ImageBox and other content */
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: rgba(255, 255, 255, 0.8);
+ z-index: 10; /* sits above the ImageBox and other content */
}
/* The <select> dropdown for choosing presets */
.scrapbook-box-preset-select {
- position: absolute;
- top: 8px;
- left: 8px;
- z-index: 20;
- padding: 4px 8px;
- font-size: 14px;
- border: 1px solid #ccc;
- border-radius: 4px;
- background: white;
+ position: relative;
+ top: 8px;
+ left: 8px;
+ z-index: 20;
+ padding: 4px 8px;
+ font-size: 14px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ background: white;
}
/* Container for the “Regenerate Background” button */
.scrapbook-box-ui {
- position: absolute;
- top: 8px;
- right: 8px;
- z-index: 20;
+ position: relative;
+ top: 8px;
+ right: 8px;
+ z-index: 20;
+ background: white;
+ width: 40px;
+ display: flex;
+ justify-content: center;
}
/* The button itself */
.scrapbook-box-ui-button {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 8px;
- font-size: 14px;
- color: black;
- background: white;
- border: 1px solid #ccc;
- border-radius: 4px;
- cursor: pointer;
- white-space: nowrap;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 8px;
+ font-size: 14px;
+ color: black;
+ background: white;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ cursor: pointer;
+ white-space: nowrap;
}
.scrapbook-box-ui-button:hover {
- background: #f5f5f5;
+ background: #f5f5f5;
}
diff --git a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
index 52e3c26dc..fcb82a6ba 100644
--- a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
+++ b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx
@@ -1,510 +1,259 @@
-import { action, makeObservable, observable, reaction, computed } from 'mobx';
+import { IconButton, Size } from '@dash/components';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
+import ReactLoading from 'react-loading';
+import { Doc, DocListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
+import { DateCast, DocCast, NumCast, toList } from '../../../../fields/Types';
import { emptyFunction } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DocumentType } from '../../../documents/DocumentTypes';
+import { DragManager } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { undoable } from '../../../util/UndoManager';
import { CollectionView } from '../../collections/CollectionView';
import { ViewBoxAnnotatableComponent } from '../../DocComponent';
-import { AspectRatioLimits } from '../../smartdraw/FireflyConstants';
+import { AspectRatioLimits, FireflyImageDimensions } from '../../smartdraw/FireflyConstants';
+import { SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
-import { DragManager } from '../../../util/DragManager';
-import { toList } from '../../../../fields/Types';
-import { undoable } from '../../../util/UndoManager';
-import ReactLoading from 'react-loading';
-import { NumCast } from '../../../../fields/Types';
-import { ScrapbookItemConfig } from './ScrapbookPreset';
import { ImageBox } from '../ImageBox';
-import { FireflyImageDimensions } from '../../smartdraw/FireflyConstants';
-import { SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
-import { ImageCast } from '../../../../fields/Types';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { IReactionDisposer } from 'mobx';
-import { observer } from 'mobx-react';
-import { runInAction } from 'mobx';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faRedoAlt } from '@fortawesome/free-solid-svg-icons';
-import { getPresetNames, createPreset } from './ScrapbookPresetRegistry';
import './ScrapbookBox.scss';
-import { isDestArraysEqual } from 'pdfjs-dist/types/web/pdf_history';
-
-
-export function buildPlaceholdersFromConfigs(configs: ScrapbookItemConfig[]): Doc[] {
- const placeholders: Doc[] = [];
-
- for (const cfg of configs) {
- if (cfg.children && cfg.children.length) {
- const childDocs = cfg.children.map(child => {
- const doc = Docs.Create.TextDocument("[placeholder] " + child.tag);
- doc.accepts_docType = child.type;
- doc.accepts_tagType = new List<string>(child.acceptTags ?? [child.tag]);
-
- const ph = new Doc();
- ph.proto = doc;
- ph.original = doc;
- ph.x = child.x;
- ph.y = child.y;
- if (child.width != null) ph._width = child.width;
- if (child.height != null) ph._height = child.height;
- return ph;
- });
-
- const protoW = cfg.containerWidth ?? cfg.width;
- const protoH = cfg.containerHeight ?? cfg.height;
- // Create a stacking document with the child placeholders
- const containerProto = Docs.Create.StackingDocument(childDocs, {
- ...(protoW != null ? { _width: protoW } : {}),
- ...(protoH != null ? { _height: protoH } : {}),
- title: cfg.tag,
- });
-
- const ph = new Doc();
- ph.proto = containerProto;
- ph.original = containerProto;
- ph.x = cfg.x;
- ph.y = cfg.y;
- if (cfg.width != null) ph._width = cfg.width;
- if (cfg.height != null) ph._height = cfg.height;
- placeholders.push(ph);
- }
-
- else {
- const doc = Docs.Create.TextDocument("[placeholder] " + cfg.tag);
- doc.accepts_docType = cfg.type;
- doc.accepts_tagType = new List<string>(cfg.acceptTags ?? [cfg.tag]);
-
- const ph = new Doc();
- ph.proto = doc;
- ph.original = doc;
- ph.x = cfg.x;
- ph.y = cfg.y;
- if (cfg.width != null) ph._width = cfg.width;
- if (cfg.height != null) ph._height = cfg.height;
- placeholders.push(ph);
- }
- }
+import { ScrapbookItemConfig } from './ScrapbookPreset';
+import { createPreset, getPresetNames } from './ScrapbookPresetRegistry';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
- return placeholders;
+function createPlaceholder(cfg: ScrapbookItemConfig, doc: Doc) {
+ const placeholder = new Doc();
+ placeholder.proto = doc;
+ placeholder.original = doc;
+ placeholder.x = cfg.x;
+ placeholder.y = cfg.y;
+ if (cfg.width !== null) placeholder._width = cfg.width;
+ if (cfg.height !== null) placeholder._height = cfg.height;
+ return placeholder;
}
-export function slotRealDocIntoPlaceholders(
- realDoc: Doc,
- placeholders: Doc[]
-): boolean {
- const realTags = new Set<string>(
- StrListCast(realDoc.$tags_chat ?? new List<string>())
- .map(t => t.toLowerCase())
-);
- // Find placeholder with most matching tags
- let bestMatch: Doc | null = null;
- let maxMatches = 0;
-/*
- (d.accepts_docType === docs[0].$type || // match fields based on type, or by analyzing content .. simple example of matching text in placeholder to dropped doc's type
- RTFCast(d[Doc.LayoutDataKey(d)])?.Text.includes(StrCast(docs[0].$type)))*/
-
- placeholders.forEach(ph => {
- if (ph.accepts_docType !== realDoc.$type) {
- // Skip this placeholder entirely if types do not match.
- return;
- };
- const phTagTypes = StrListCast(ph.accepts_tagType ?? new List<string>())
- .map(t => t.toLowerCase());
- console.log({ realTags, phTagTypes });
- const matches = phTagTypes.filter(tag => realTags.has(tag));
+function createMessagePlaceholder(cfg: ScrapbookItemConfig) {
+ return createPlaceholder(cfg,
+ Docs.Create.TextDocument(cfg.message ?? ('[placeholder] ' + cfg.acceptTags?.[0]), { placeholder: "", placeholder_docType: cfg.type, placeholder_acceptTags: new List<string>(cfg.acceptTags) })
+ ); // prettier-ignore
+}
+export function buildPlaceholdersFromConfigs(configs: ScrapbookItemConfig[]) {
+ return configs.map(cfg => {
+ if (cfg.children?.length) {
+ const childDocs = cfg.children.map(createMessagePlaceholder);
+ const protoW = cfg.containerWidth ?? cfg.width;
+ const protoH = cfg.containerHeight ?? cfg.height;
+ // Create a stacking document with the child placeholders
+ const containerProto = Docs.Create.StackingDocument(childDocs, {
+ ...(protoW !== null ? { _width: protoW } : {}),
+ ...(protoH !== null ? { _height: protoH } : {}),
+ title: cfg.message,
+ });
+ return createPlaceholder(cfg, containerProto);
+ }
+ return createMessagePlaceholder(cfg);
+ });
+}
+export function slotRealDocIntoPlaceholders(realDoc: Doc, placeholders: Doc[]): boolean {
+ const realTags = new Set<string>(StrListCast(realDoc.$tags_chat).map(t => t.toLowerCase?.() ?? ''));
+
+ // Find placeholder with most matching tags
+ let bestMatch: Doc | null = null;
+ let maxMatches = 0;
+
+ // match fields based on type, or by analyzing content .. simple example of matching text in placeholder to dropped doc's type
+ placeholders
+ .filter(ph => ph.placeholder_docType === realDoc.$type) // Skip this placeholder entirely if types do not match.
+ .forEach(ph => {
+ const matches = StrListCast(ph.placeholder_acceptTags)
+ .map(t => t.toLowerCase?.())
+ .filter(tag => realTags.has(tag));
+
+ if (matches.length > maxMatches) {
+ maxMatches = matches.length;
+ bestMatch = ph;
+ }
+ });
- if (matches.length > maxMatches) {
- maxMatches = matches.length;
- bestMatch = ph;
+ if (bestMatch && maxMatches > 0) {
+ setTimeout(undoable(() => (bestMatch!.proto = realDoc), 'Scrapbook add'));
+ return true;
}
- });
-
- if (bestMatch && maxMatches > 0) {
- setTimeout(
- undoable(() => {
- bestMatch!.proto = realDoc;
- }, 'Scrapbook add'),
- 0
- );
- return true;
- }
-
- return false;
+ return false;
}
// Scrapbook view: a container that lays out its child items in a template
@observer
export class ScrapbookBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
- @observable selectedPreset = getPresetNames()[0];
-
- @observable createdDate: string;
- @observable loading = false;
- @observable src = '';
- @observable imgDoc: Doc | undefined;
+ public static LayoutString(fieldStr: string) {
+ return FieldView.LayoutString(ScrapbookBox, fieldStr);
+ }
private _disposers: { [name: string]: IReactionDisposer } = {};
- private imageBoxRef = React.createRef<ImageBox>();
-
+ private _imageBoxRef = React.createRef<ImageBox>();
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
- const existingItems = DocListCast(this.dataDoc[this.fieldKey] as List<Doc>);
- if (!existingItems || existingItems.length === 0) {
- // Only wire up reaction/setTitle if it's truly a brand-new, empty Scrapbook
- reaction(
- () => this.selectedPreset,
- presetName => this.initScrapbook(presetName),
- { fireImmediately: true }
- );
-
- this.createdDate = this.getFormattedDate();
- this.setTitle();
- } else {
- // If items are already present, just preserve whatever was injected.
- // We still want `createdDate` set so that the UI title bar can show it if needed.
- this.createdDate = this.getFormattedDate();
}
-
- // ensure we always have a List<Doc> in dataDoc['items']
- if (!this.dataDoc[this.fieldKey]) {
- this.dataDoc[this.fieldKey] = new List<Doc>();
- }
-
- }
-
- public static LayoutString(fieldStr: string) {
- return FieldView.LayoutString(ScrapbookBox, fieldStr);
- }
+ @observable _selectedPreset = getPresetNames()[0];
+ @observable _loading = false;
- getFormattedDate(): string {
- return new Date().toLocaleDateString(undefined, {
+ @computed get createdDate() {
+ return DateCast(this.dataDoc.$author_date)?.date.toLocaleDateString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric',
});
}
-
-
- @action
- initScrapbook(name: string) {
- const configs = createPreset(name);
- // 1) ensure title is set
- const title = `Scrapbook - ${this.createdDate}`;
- if (this.dataDoc.title !== title) {
- this.dataDoc.title = title;
- }
-
- // 2) build placeholders from the preset
- const placeholders = buildPlaceholdersFromConfigs(configs);
-
- // 3) commit them into the field
- this.dataDoc[this.fieldKey] = new List<Doc>(placeholders);
- }
-
-
+ @computed get ScrapbookLayoutDocs() { return DocListCast(this.dataDoc[this.fieldKey]); } // prettier-ignore
+ @computed get BackgroundDoc() { return DocCast(this.dataDoc[this.fieldKey + '_background']); } // prettier-ignore
+ set ScrapbookLayoutDocs(doc: Doc[]) { this.dataDoc[this.fieldKey] = new List(doc); } // prettier-ignore
+ set BackgroundDoc(doc: Opt<Doc>) { this.dataDoc[this.fieldKey + '_background'] = doc; } // prettier-ignore
@action
- setTitle() {
- const title = `Scrapbook - ${this.createdDate}`;
- if (this.dataDoc.title !== title) {
- this.dataDoc.title = title;
- if (!this.dataDoc[this.fieldKey]){
- const image = Docs.Create.TextDocument('[placeholder] person image');
- image.accepts_docType = DocumentType.IMG;
- image.accepts_tagType = 'PERSON'
- const placeholder = new Doc();
- placeholder.proto = image;
- placeholder.original = image;
- placeholder._width = 250;
- placeholder._height = 200;
- placeholder.x = 0;
- placeholder.y = -100;
-
-
- const summary = Docs.Create.TextDocument('[placeholder] long summary');
- summary.accepts_docType = DocumentType.RTF;
- summary.accepts_tagType = 'lengthy description';
- const placeholder2 = new Doc();
- placeholder2.proto = summary;
- placeholder2.original = summary;
- placeholder2.x = 0;
- placeholder2.y = 200;
- placeholder2._width = 250;
-
-
- const sidebar = Docs.Create.TextDocument('[placeholder] brief sidebar');
- sidebar.accepts_docType = DocumentType.RTF;
- sidebar.accepts_tagType = 'title';
- const placeholder3 = new Doc();
- placeholder3.proto = sidebar;
- placeholder3.original = sidebar;
- placeholder3.x = 280;
- placeholder3.y = -50;
- placeholder3._width = 50;
- placeholder3._height = 200;
-
-
-
- const internalImg = Docs.Create.TextDocument('[placeholder] landscape internal');
- internalImg.accepts_docType = DocumentType.IMG;
- internalImg.accepts_tagType = 'LANDSCAPE'
- const placeholder5 = new Doc();
- placeholder5.proto = internalImg;
- placeholder5.original = internalImg;
- placeholder5._width = 50;
- placeholder5._height = 100;
- placeholder5.x = 0;
- placeholder5.y = -100;
-
- const collection = Docs.Create.StackingDocument([placeholder5], { _width: 300, _height: 300, title: "internal coll" });
- //collection.accepts_docType = DocumentType.COL; don't mark this field
- const placeholder4 = new Doc();
- placeholder4.proto = collection;
- placeholder4.original = collection;
- placeholder4.x = -200;
- placeholder4.y = -100;
- placeholder4._width = 100;
- placeholder4._height = 200;
-
- const starter = Docs.Create.TextDocument('To create a scrapbook from existing documents, marquee select. For existing scrapbook arrangements, select a preset from the dropdown.');
- starter.accepts_docType = DocumentType.RTF;
- starter.accepts_tagType = 'n/a'
- const starterplaceholder = new Doc();
- starterplaceholder.proto = summary;
- starterplaceholder.original = summary;
- starterplaceholder.x = 0;
- starterplaceholder.y = 0;
- starterplaceholder._width = 250;
-
-
-
-
-
- /*note-to-self
- would doing:
-
- const collection = Docs.Create.ScrapbookDocument([placeholder, placeholder2, placeholder3]);
- create issues with references to the same object?*/
-
- /*note-to-self
- Should we consider that there are more collections than just COL type collections?
- when spreading*/
-
- /*note-to-self
- difference between passing a new List<Doc> versus just the raw array?
- */
- this.dataDoc[this.fieldKey] = new List<Doc>([starterplaceholder]);
- }
+ setDefaultPlaceholder = () => {
+ this.ScrapbookLayoutDocs = [
+ createMessagePlaceholder({
+ message: 'To create a scrapbook from existing documents, marquee select. For existing scrapbook arrangements, select a preset from the dropdown.',
+ type: DocumentType.RTF,
+ width: 250,
+ height: 200,
+ x: 0,
+ y: 0,
+ }),
+ ];
+
+ const placeholder1 = createMessagePlaceholder({ acceptTags: ['PERSON'], type: DocumentType.IMG, width: 250, height: 200, x: 0, y: -100 });
+ const placeholder2 = createMessagePlaceholder({ acceptTags: ['lengthy description'], type: DocumentType.RTF, width: 250, height: undefined, x: 0, y: 200 });
+ const placeholder3 = createMessagePlaceholder({ acceptTags: ['title'], type: DocumentType.RTF, width: 50, height: 200, x: 280, y: -50 });
+ const placeholder4 = createPlaceholder( { width: 100, height: 200, x: -200, y: -100 }, Docs.Create.StackingDocument([
+ createMessagePlaceholder({ acceptTags: ['LANDSCAPE'], type: DocumentType.IMG, width: 50, height: 100, x: 0, y: -100 })
+ ], { _width: 300, _height: 300, title: 'internal coll' })); // prettier-ignore
+ console.log('UNUSED', placeholder4, placeholder3, placeholder2, placeholder1);
+ /* note-to-self
+ would doing:
+ const collection = Docs.Create.ScrapbookDocument([placeholder, placeholder2, placeholder3]);
+ create issues with references to the same object? */
+
+ /*note-to-self
+ Should we consider that there are more collections than just COL type collections?
+ when spreading */
+
+ /*note-to-self
+ difference between passing a new List<Doc> versus just the raw array? */
+ };
-
- }
- }
+ selectPreset = action((presetName: string) => (this.ScrapbookLayoutDocs = buildPlaceholdersFromConfigs(createPreset(presetName))));
componentDidMount() {
- this.setTitle();
- this.generateAiImage();
+ const title = `Scrapbook - ${this.createdDate}`;
+ if (!this.ScrapbookLayoutDocs.length) this.setDefaultPlaceholder();
+ if (!this.BackgroundDoc) this.generateAiImage(this.regenPrompt);
+ if (this.dataDoc.title !== title) this.dataDoc.title = title; // ensure title is set
this._disposers.propagateResize = reaction(
() => ({ w: this.layoutDoc._width, h: this.layoutDoc._height }),
(dims, prev) => {
- // prev is undefined on the first run, so bail early
- if (!prev || !SnappingManager.ShiftKey || !this.imgDoc) return;
-
- // either guard the ref…
- const imageBox = this.imageBoxRef.current;
- if (!imageBox) return;
-
- // …or just hard-code the fieldKey if you know it’s always `"data"`
- const key = imageBox.props.fieldKey;
-
- runInAction(() => {
- if(!this.imgDoc){
- return
+ const imageBox = this._imageBoxRef.current;
+ // prev is undefined on the first run
+ if (prev && SnappingManager.ShiftKey && this.BackgroundDoc && imageBox) {
+ this.BackgroundDoc[imageBox.fieldKey + '_outpaintOriginalWidth'] = prev.w;
+ this.BackgroundDoc[imageBox.fieldKey + '_outpaintOriginalHeight'] = prev.h;
+ imageBox.layoutDoc._width = dims.w;
+ imageBox.layoutDoc._height = dims.h;
}
- // use prev.w/h (the *old* size) as your orig dims
- this.imgDoc[key + '_outpaintOriginalWidth'] = prev.w;
- this.imgDoc[key + '_outpaintOriginalHeight'] = prev.h;
- ;(this.imageBoxRef.current as any).layoutDoc._width = dims.w
- ;(this.imageBoxRef.current as any).layoutDoc._height = dims.h
-
- });
}
);
}
-
@action
- async generateAiImage(prompt?: string) {
- this.loading = true;
- try {
- // 1) Default to regenPrompt if none provided
- if (!prompt) prompt = this.regenPrompt;
-
- // 2) Measure the scrapbook’s current size
- const w = NumCast(this.layoutDoc._width, 1);
- const h = NumCast(this.layoutDoc._height, 1);
- const ratio = w / h;
-
- // 3) Pick the Firefly preset that best matches the aspect ratio
- let preset = FireflyImageDimensions.Square;
- if (ratio > AspectRatioLimits[FireflyImageDimensions.Widescreen]) {
- preset = FireflyImageDimensions.Widescreen;
- } else if (ratio > AspectRatioLimits[FireflyImageDimensions.Landscape]) {
- preset = FireflyImageDimensions.Landscape;
- } else if (ratio < AspectRatioLimits[FireflyImageDimensions.Portrait]) {
- preset = FireflyImageDimensions.Portrait;
- }
-
- // 4) Call exactly the same CreateWithFirefly that ImageBox uses
- const doc = await SmartDrawHandler.CreateWithFirefly(prompt, preset);
-
- if (doc instanceof Doc) {
- // 5) Hook it into your state
- this.imgDoc = doc;
- const imgField = ImageCast(doc.data);
- this.src = imgField?.url.href ?? '';
- } else {
- alert('Failed to generate document.');
- this.src = '';
- }
- } catch (e) {
- alert(`Generation error: ${e}`);
- } finally {
- runInAction(() => {
- this.loading = false;
- });
- }
- }
-
- childRejectDrop = (de: DragManager.DropEvent, subView?: DocumentView) => {
- return true; // disable dropping documents onto any child of the scrapbook.
- };
- rejectDrop = (de: DragManager.DropEvent, subView?: DocumentView) => {
- // Test to see if the dropped doc is dropped on an acceptable location (anywerhe? on a specific box).
- // const draggedDocs = de.complete.docDragData?.draggedDocuments;
- return false; // allow all Docs to be dropped onto scrapbook -- let filterAddDocument make the final decision.
+ generateAiImage = (prompt: string) => {
+ this._loading = true;
+
+ const ratio = NumCast(this.layoutDoc._width, 1) / NumCast(this.layoutDoc._height, 1); // Measure the scrapbook’s current aspect
+ const choosePresetForDimensions = (() => { // Pick the Firefly preset that best matches the aspect ratio
+ if (ratio > AspectRatioLimits[FireflyImageDimensions.Widescreen]) return FireflyImageDimensions.Widescreen;
+ if (ratio > AspectRatioLimits[FireflyImageDimensions.Landscape]) return FireflyImageDimensions.Landscape;
+ if (ratio < AspectRatioLimits[FireflyImageDimensions.Portrait]) return FireflyImageDimensions.Portrait;
+ return FireflyImageDimensions.Square;
+ })(); // prettier-ignore
+
+ SmartDrawHandler.CreateWithFirefly(prompt, choosePresetForDimensions) // Call exactly the same CreateWithFirefly that ImageBox uses
+ .then(action(doc => {
+ if (doc instanceof Doc) {
+ this.BackgroundDoc = doc; // set the background image directly on the scrapbook
+ } else {
+ alert('Failed to generate document.');
+ }
+ }))
+ .catch(e => alert(`Generation error: ${e}`))
+ .finally(action(() => (this._loading = false))); // prettier-ignore
};
- filterAddDocument = (docIn: Doc | Doc[]) => {
- const docs = toList(docIn); //The docs being added to the scrapbook
-
- // 1) Grab all template slots:
- const slots = DocListCast(this.dataDoc[this.fieldKey]);
-
- // 2) recursive unwrap:
- const unwrap = (items: Doc[]): Doc[] =>
- items.flatMap(d =>
- d.$type === DocumentType.COL
- ? unwrap(DocListCast(d[Doc.LayoutDataKey(d)]))
- : [d]
- );
-
- // 3) produce a flat list of every doc, unwrapping any number of nested COLs
- const allDocs: Doc[] = unwrap(slots);
- if (docs?.length === 1) {
- return slotRealDocIntoPlaceholders(
- docs[0],
- allDocs,
- )
- ? false
- : false;
- }
-
- return false;
-};
-
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ childRejectDrop = (de: DragManager.DropEvent, subView?: DocumentView) => true; // disable dropping documents onto any child of the scrapbook.
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ rejectDrop = (de: DragManager.DropEvent, subView?: DocumentView) => false; // allow all Docs to be dropped onto scrapbook -- let filterAddDocument make the final decision.
+
+ /**
+ * iterate through all items and their children and return a flat list of leaf placeholder content Docs
+ * @param items
+ * @returns list of placeholder content Docs
+ */
+ unwrapPlaceholders = (items: Doc[]): Doc[] => items.flatMap(d => (d.$type === DocumentType.COL ? this.unwrapPlaceholders(DocListCast(d[Doc.LayoutDataKey(d)])) : [d]));
+
+ /**
+ * Filter function to determine if a document can be added to the scrapbook.
+ * This checks if the document matches any of the placeholder slots in the scrapbook.
+ * @param docs - The document(s) being added to the scrapbook.
+ * @returns true if the document can be added, false otherwise.
+ */
+ filterAddDocument = (docs: Doc | Doc[]) => {
+ toList(docs).forEach(doc => slotRealDocIntoPlaceholders(doc, this.unwrapPlaceholders(this.ScrapbookLayoutDocs)));
+ return false;
+ };
@computed get regenPrompt() {
- const slots = DocListCast(this.dataDoc[this.fieldKey]);
-
- const unwrap = (items: Doc[]): Doc[] =>
- items.flatMap(d =>
- d.$type === DocumentType.COL
- ? unwrap(DocListCast(d[Doc.LayoutDataKey(d)]))
- : [d]
- );
-
- const allDocs: Doc[] = unwrap(slots);
- const internalTagsSet = new Set<string>();
-
- allDocs.forEach(doc => {
- const tags = StrListCast(doc.$tags_chat ?? new List<string>());
- tags.forEach(tag =>
- {if (!tag.startsWith("ASPECT_")) {
- internalTagsSet.add(tag);
- }
- });
- });
-
+ const allDocs = this.unwrapPlaceholders(this.ScrapbookLayoutDocs); // find all non-collections in scrapbook (e.g., placeholder content docs)
+ const internalTagsSet = new Set<string>(allDocs.flatMap(doc => StrListCast(doc.$tags_chat).filter(tag => !tag.startsWith?.('ASPECT_'))));
const internalTags = Array.from(internalTagsSet).join(', ');
-
-
- return internalTags
- ? `Create a new scrapbook background featuring: ${internalTags}`
- : 'A serene mountain landscape at sunrise, ultra-wide, pastel sky, abstract, scrapbook background';
- }
-
- render() {
- return (
- <div className="scrapbook-box">
- {this.loading && (
- <div className="scrapbook-box-loading-overlay">
- <ReactLoading type="spin" width={50} height={50} />
- </div>
- )}
-
- {this.src && this.imgDoc && (
- <ImageBox
- ref={this.imageBoxRef}
- {...this._props}
- Document={this.imgDoc}
- fieldKey="data"
- />
- )}
-
- <select
- className="scrapbook-box-preset-select"
- value={this.selectedPreset}
- onChange={e => (this.selectedPreset = e.currentTarget.value)}
- >
- {getPresetNames().map(name => (
- <option key={name} value={name}>
- {name}
- </option>
- ))}
- </select>
- {this._props.isContentActive() && (
- <div className="scrapbook-box-ui">
- <button
- type="button"
- title="Regenerate Background"
- onClick={() => this.generateAiImage(this.regenPrompt)}
- className="scrapbook-box-ui-button"
- >
- <FontAwesomeIcon icon={faRedoAlt} />
- <span>Regenerate Background</span>
- </button>
- </div>
- )}
+ return internalTags ? `Create a new scrapbook background featuring: ${internalTags}` : 'A serene mountain landscape at sunrise, ultra-wide, pastel sky, abstract, scrapbook background';
+ }
- <CollectionView
- {...this._props}
- setContentViewBox={emptyFunction}
- rejectDrop={this.rejectDrop}
- childRejectDrop={this.childRejectDrop}
- filterAddDocument={this.filterAddDocument}
- />
- </div>
- );
- }
+ render() {
+ return (
+ <div className="scrapbook-box">
+ <div style={{ display: this._loading ? undefined : 'none' }} className="scrapbook-box-loading-overlay">
+ <ReactLoading type="spin" width={50} height={50} />
+ </div>
+
+ {this.BackgroundDoc && <ImageBox ref={this._imageBoxRef} {...this._props} Document={this.BackgroundDoc} fieldKey="data" />}
+ <div style={{ display: this._props.isContentActive() ? 'flex' : 'none', alignItems: 'center', justifyContent: 'space-between', padding: '0 10px' }}>
+ <select className="scrapbook-box-preset-select" value={this._selectedPreset} onChange={action(e => this.selectPreset((this._selectedPreset = e.currentTarget.value)))}>
+ {getPresetNames().map(name => (
+ <option key={name} value={name}>
+ {name}
+ </option>
+ ))}
+ </select>
+ <div className="scrapbook-box-ui" style={{ opacity: this._loading ? 0.5 : 1 }}>
+ <IconButton size={Size.SMALL} tooltip="regenerate a new background" label="back-ground" icon={<FontAwesomeIcon icon="redo-alt" size="sm" />} onClick={() => !this._loading && this.generateAiImage(this.regenPrompt)} />
+ </div>
+ </div>
+
+ <CollectionView {...this._props} setContentViewBox={emptyFunction} rejectDrop={this.rejectDrop} childRejectDrop={this.childRejectDrop} filterAddDocument={this.filterAddDocument} />
+ </div>
+ );
+ }
}
-
-
Docs.Prototypes.TemplateMap.set(DocumentType.SCRAPBOOK, {
layout: { view: ScrapbookBox, dataField: 'items' },
options: {
diff --git a/src/client/views/nodes/scrapbook/ScrapbookPreset.tsx b/src/client/views/nodes/scrapbook/ScrapbookPreset.tsx
index 706b9dafd..fe33741af 100644
--- a/src/client/views/nodes/scrapbook/ScrapbookPreset.tsx
+++ b/src/client/views/nodes/scrapbook/ScrapbookPreset.tsx
@@ -1,23 +1,22 @@
import { DocumentType } from '../../../documents/DocumentTypes';
export enum ScrapbookPresetType {
- Default = 'Default',
- Classic = 'Classic',
+ Default = 'Default',
+ Classic = 'Classic',
None = 'Select Template',
- Collage = 'Collage',
+ Collage = 'Collage',
Spotlight = 'Spotlight',
- Gallery = 'Gallery'
+ Gallery = 'Gallery',
}
export interface ScrapbookItemConfig {
- type: DocumentType;
- /** text shown in the placeholder bubble */
- tag: string;
- /** what this slot actually accepts (defaults to `tag`) */
- acceptTags?: string[];
-
x: number;
y: number;
+
+ message?: string; // optional text to display instead of [placeholder] + acceptTags[0]
+ type?: DocumentType;
+ /** what this slot actually accepts (defaults to `tag`) */
+ acceptTags?: string[];
/** the frame this placeholder occupies */
width?: number;
height?: number;
@@ -30,147 +29,65 @@ export interface ScrapbookItemConfig {
export class ScrapbookPreset {
static createPreset(presetType: ScrapbookPresetType): ScrapbookItemConfig[] {
switch (presetType) {
- case ScrapbookPresetType.None:
- return ScrapbookPreset.createNonePreset();
- case ScrapbookPresetType.Classic:
- return ScrapbookPreset.createClassicPreset();
- case ScrapbookPresetType.Collage:
- return ScrapbookPreset.createCollagePreset();
- case ScrapbookPresetType.Spotlight:
- return ScrapbookPreset.createSpotlightPreset();
- case ScrapbookPresetType.Default:
- return ScrapbookPreset.createDefaultPreset();
- case ScrapbookPresetType.Gallery:
- return ScrapbookPreset.createGalleryPreset();
+ case ScrapbookPresetType.None: return ScrapbookPreset.createNonePreset();
+ case ScrapbookPresetType.Classic: return ScrapbookPreset.createClassicPreset();
+ case ScrapbookPresetType.Collage: return ScrapbookPreset.createCollagePreset();
+ case ScrapbookPresetType.Spotlight: return ScrapbookPreset.createSpotlightPreset();
+ case ScrapbookPresetType.Default: return ScrapbookPreset.createDefaultPreset();
+ case ScrapbookPresetType.Gallery: return ScrapbookPreset.createGalleryPreset();
default:
throw new Error(`Unknown preset type: ${presetType}`);
- }
+ } // prettier-ignore
}
- private static createNonePreset(): ScrapbookItemConfig[] {
- return [
-
- { type: DocumentType.RTF,
- tag: 'To create a scrapbook from existing documents, marquee select. For existing scrapbook arrangements, select a preset from the dropdown.',
- acceptTags: ['n/a'],
- x: 0, y: 0, width: 250, height: 200
- },
-
- ];
+ private static createNonePreset(): ScrapbookItemConfig[] {
+ return [{ message: 'To create a scrapbook from existing documents, marquee select. For existing scrapbook arrangements, select a preset from the dropdown.', type: DocumentType.RTF, acceptTags: [], x: 0, y: 0, width: 250, height: 200 }];
}
private static createClassicPreset(): ScrapbookItemConfig[] {
return [
- { type: DocumentType.IMG,
- tag: '[placeholder] LANDSCAPE',
- acceptTags: ['LANDSCAPE'],
- x: 0, y: -100, width: 250, height: 200
- },
- { type: DocumentType.RTF,
- tag: '[placeholder] caption',
- acceptTags: ['sentence'],
- x: 0, y: 200, width: 250, height: 50
- },
- { type: DocumentType.RTF,
- tag: '[placeholder] lengthy description',
- acceptTags: ['paragraphs'],
- x: 280, y: -50, width: 50, height: 200
- },
- { type: DocumentType.IMG,
- tag: '[placeholder] PERSON',
- acceptTags: ['PERSON'],
- x: -200, y: -100, width: 100, height: 200
- },
+ { type: DocumentType.IMG, message: '[placeholder] LANDSCAPE', acceptTags: ['LANDSCAPE'], x: 0, y: -100, width: 250, height: 200 },
+ { type: DocumentType.RTF, message: '[placeholder] caption', acceptTags: ['sentence'], x: 0, y: 200, width: 250, height: 50 },
+ { type: DocumentType.RTF, message: '[placeholder] lengthy description', acceptTags: ['paragraphs'], x: 280, y: -50, width: 50, height: 200 },
+ { type: DocumentType.IMG, message: '[placeholder] PERSON', acceptTags: ['PERSON'], x: -200, y: -100, width: 100, height: 200 },
];
}
private static createGalleryPreset(): ScrapbookItemConfig[] {
return [
- { type: DocumentType.IMG, tag: 'Gallery 1', acceptTags: ['LANDSCAPE'], x: -150, y: -150, width: 150, height: 150 },
- { type: DocumentType.IMG, tag: 'Gallery 2', acceptTags: ['LANDSCAPE'], x: 0, y: -150, width: 150, height: 150 },
- { type: DocumentType.IMG, tag: 'Gallery 3', acceptTags: ['LANDSCAPE'], x: 150, y: -150, width: 150, height: 150 },
- { type: DocumentType.IMG, tag: 'Gallery 4', acceptTags: ['LANDSCAPE'], x: -150, y: 0, width: 150, height: 150 },
- { type: DocumentType.IMG, tag: 'Gallery 5', acceptTags: ['LANDSCAPE'], x: 0, y: 0, width: 150, height: 150 },
- { type: DocumentType.IMG, tag: 'Gallery 6', acceptTags: ['LANDSCAPE'], x: 150, y: 0, width: 150, height: 150 },
+ { type: DocumentType.IMG, message: 'Gallery 1', acceptTags: ['LANDSCAPE'], x: -150, y: -150, width: 150, height: 150 },
+ { type: DocumentType.IMG, message: 'Gallery 2', acceptTags: ['LANDSCAPE'], x: 0, y: -150, width: 150, height: 150 },
+ { type: DocumentType.IMG, message: 'Gallery 3', acceptTags: ['LANDSCAPE'], x: 150, y: -150, width: 150, height: 150 },
+ { type: DocumentType.IMG, message: 'Gallery 4', acceptTags: ['LANDSCAPE'], x: -150, y: 0, width: 150, height: 150 },
+ { type: DocumentType.IMG, message: 'Gallery 5', acceptTags: ['LANDSCAPE'], x: 0, y: 0, width: 150, height: 150 },
+ { type: DocumentType.IMG, message: 'Gallery 6', acceptTags: ['LANDSCAPE'], x: 150, y: 0, width: 150, height: 150 },
];
- }
-
+ }
private static createDefaultPreset(): ScrapbookItemConfig[] {
return [
- { type: DocumentType.IMG,
- tag: 'image',
- acceptTags: ['LANDSCAPE'],
- x: 0, y: -100, width: 250, height: 200
- },
- { type: DocumentType.RTF,
- tag: 'summary',
- acceptTags: ['sentence'],
- x: 0, y: 200, width: 250
- },
- { type: DocumentType.RTF,
- tag: 'sidebar',
- acceptTags: ['paragraphs'],
- x: 280, y: -50, width: 50, height: 200
- },
- {
- type: DocumentType.COL,
- tag: 'internal coll',
- x: -200, y: -100, width: 100, height: 200,
- containerWidth: 300, containerHeight: 300,
- children: [
- { type: DocumentType.IMG,
- tag: 'image internal',
- acceptTags: ['PERSON'],
- x: 0, y: 0, width: 50, height: 100
- }
- ]
- }
- ];
+ { type: DocumentType.IMG, message: 'image', acceptTags: ['LANDSCAPE'], x: 0, y: -100, width: 250, height: 200 },
+ { type: DocumentType.RTF, message: 'summary', acceptTags: ['sentence'], x: 0, y: 200, width: 250 },
+ { type: DocumentType.RTF, message: 'sidebar', acceptTags: ['paragraphs'], x: 280, y: -50, width: 50, height: 200 },
+ { containerWidth: 300, containerHeight: 300, x: -200, y: -100, width: 100, height: 200,
+ children: [{ type: DocumentType.IMG, message: 'image internal', acceptTags: ['PERSON'], x: 0, y: 0, width: 50, height: 100 }], },
+ ]; // prettier-ignore
}
private static createCollagePreset(): ScrapbookItemConfig[] {
return [
- { type: DocumentType.IMG,
- tag: 'LANDSCAPE',
- acceptTags: ['LANDSCAPE'],
- x: -150, y: -150, width: 150, height: 150
- },
- { type: DocumentType.IMG,
- tag: 'PERSON',
- acceptTags: ['PERSON'],
- x: 0, y: -150, width: 150, height: 150
- },
- { type: DocumentType.RTF,
- tag: 'caption',
- acceptTags: ['sentence'],
- x: -150, y: 0, width: 300, height: 100
- },
- { type: DocumentType.RTF,
- tag: 'lengthy description',
- acceptTags: ['paragraphs'],
- x: 0, y: 100, width: 300, height: 100
- }
- ];
+ { type: DocumentType.IMG, message: 'LANDSCAPE', acceptTags: ['LANDSCAPE'], x: -150, y: -150, width: 150, height: 150 },
+ { type: DocumentType.IMG, message: 'PERSON', acceptTags: ['PERSON'], x: 0, y: -150, width: 150, height: 150 },
+ { type: DocumentType.RTF, message: 'caption', acceptTags: ['sentence'], x: -150, y: 0, width: 300, height: 100 },
+ { type: DocumentType.RTF, message: 'lengthy description', acceptTags: ['paragraphs'], x: 0, y: 100, width: 300, height: 100 },
+ ]; // prettier-ignore
}
private static createSpotlightPreset(): ScrapbookItemConfig[] {
return [
- { type: DocumentType.RTF,
- tag: 'title',
- acceptTags: ['word'],
- x: 0, y: -180, width: 300, height: 40
- },
- { type: DocumentType.IMG,
- tag: 'LANDSCAPE',
- acceptTags: ['LANDSCAPE'],
- x: 0, y: 0, width: 300, height: 200
- },
- { type: DocumentType.RTF,
- tag: 'caption',
- acceptTags: ['sentence'],
- x: 0, y: 230, width: 300, height: 50
- }
+ { type: DocumentType.RTF, message: 'title', acceptTags: ['word'], x: 0, y: -180, width: 300, height: 40 },
+ { type: DocumentType.IMG, message: 'LANDSCAPE', acceptTags: ['LANDSCAPE'], x: 0, y: 0, width: 300, height: 200 },
+ { type: DocumentType.RTF, message: 'caption', acceptTags: ['sentence'], x: 0, y: 230, width: 300, height: 50 },
];
}
}
diff --git a/src/client/views/nodes/scrapbook/ScrapbookPresetRegistry.ts b/src/client/views/nodes/scrapbook/ScrapbookPresetRegistry.ts
index c6d67ab73..3a2189d00 100644
--- a/src/client/views/nodes/scrapbook/ScrapbookPresetRegistry.ts
+++ b/src/client/views/nodes/scrapbook/ScrapbookPresetRegistry.ts
@@ -6,39 +6,31 @@ type PresetGenerator = () => ScrapbookItemConfig[];
// Internal map of preset name to generator
const presetRegistry = new Map<string, PresetGenerator>();
-
-
-
/**
* Register a new scrapbook preset under the given name.
*/
export function registerPreset(name: string, gen: PresetGenerator) {
- presetRegistry.set(name, gen);
+ presetRegistry.set(name, gen);
}
/**
* List all registered preset names.
*/
export function getPresetNames(): string[] {
- return Array.from(presetRegistry.keys());
+ return Array.from(presetRegistry.keys());
}
/**
* Create the config array for the named preset.
*/
export function createPreset(name: string): ScrapbookItemConfig[] {
- const gen = presetRegistry.get(name);
- if (!gen) throw new Error(`Unknown scrapbook preset: ${name}`);
- return gen();
+ const gen = presetRegistry.get(name);
+ if (!gen) throw new Error(`Unknown scrapbook preset: ${name}`);
+ return gen();
}
// ------------------------
// Register built-in presets
import { ScrapbookPreset } from './ScrapbookPreset';
-registerPreset(ScrapbookPresetType.None, () => ScrapbookPreset.createPreset(ScrapbookPresetType.None));
-registerPreset(ScrapbookPresetType.Classic, () => ScrapbookPreset.createPreset(ScrapbookPresetType.Classic));
-registerPreset(ScrapbookPresetType.Collage, () => ScrapbookPreset.createPreset(ScrapbookPresetType.Collage));
-registerPreset(ScrapbookPresetType.Spotlight, () => ScrapbookPreset.createPreset(ScrapbookPresetType.Spotlight));
-registerPreset(ScrapbookPresetType.Default, () => ScrapbookPreset.createPreset(ScrapbookPresetType.Default));
-registerPreset(ScrapbookPresetType.Gallery, () => ScrapbookPreset.createPreset(ScrapbookPresetType.Gallery));
+Object.keys(ScrapbookPresetType).forEach(key => registerPreset(key, () => ScrapbookPreset.createPreset(key as ScrapbookPresetType))); // pretter-ignore