aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/util/SharingManager.tsx6
-rw-r--r--src/client/util/bezierFit.ts214
-rw-r--r--src/client/views/AntimodeMenu.scss1
-rw-r--r--src/client/views/LightboxView.tsx6
-rw-r--r--src/client/views/MainView.scss2
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/PropertiesView.tsx13
-rw-r--r--src/client/views/TagsView.tsx3
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx30
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx2
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.scss13
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx135
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.scss1
-rw-r--r--src/client/views/nodes/DocumentView.tsx16
-rw-r--r--src/client/views/nodes/ImageBox.scss13
-rw-r--r--src/client/views/nodes/ImageBox.tsx123
-rw-r--r--src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts30
-rw-r--r--src/client/views/nodes/chatbot/tools/ImageCreationTool.ts3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx18
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx2
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx3
-rw-r--r--src/client/views/smartdraw/DrawingFillHandler.tsx4
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx8
26 files changed, 299 insertions, 360 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1ce25165c..21d3c978b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -384,6 +384,9 @@ export class DocumentOptions {
presentation_duration?: NUMt = new NumInfo('the duration of the slide in presentation view', false);
presentation_zoomText?: BOOLt = new BoolInfo('whether text anchors should shown in a larger box when following links to make them stand out', false);
+ data_annotations?: List<Doc>;
+ _data_usePath?: STRt = new StrInfo("description of field key to display in image box ('alternate','alternate:hover', 'data:hover'). defaults to primary", false);
+ data_alternates?: List<Doc>;
data?: FieldType;
data_useCors?: BOOLt = new BoolInfo('whether CORS protocol should be used for web page');
_face_showImages?: BOOLt = new BoolInfo('whether to show images in uniqe face Doc');
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index efc8e79a6..3a248400b 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -502,7 +502,6 @@ export class SharingManager extends React.Component<object> {
}
};
- // eslint-disable-next-line react/sort-comp
public close = action(() => {
this.isOpen = false;
this.selectedUsers = null; // resets the list of users and selected users (in the react-select component)
@@ -517,7 +516,6 @@ export class SharingManager extends React.Component<object> {
this.layoutDocAcls = false;
});
- // eslint-disable-next-line react/no-unused-class-component-methods
public open = (target?: DocumentView, targetDoc?: Doc) => {
this.populateUsers();
runInAction(() => {
@@ -534,7 +532,6 @@ export class SharingManager extends React.Component<object> {
* @param group
* @param emailId
*/
- // eslint-disable-next-line react/no-unused-class-component-methods
shareWithAddedMember = (group: Doc, emailId: string, retry: boolean = true) => {
const user = this.users.find(({ user: { email } }) => email === emailId)!;
if (group.docsShared) {
@@ -559,7 +556,6 @@ export class SharingManager extends React.Component<object> {
/**
* Called from the properties sidebar to change permissions of a user.
*/
- // eslint-disable-next-line react/no-unused-class-component-methods
shareFromPropertiesSidebar = undoable((shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => {
if (layout) this.layoutDocAcls = true;
if (shareWith !== 'Guest') {
@@ -583,7 +579,6 @@ export class SharingManager extends React.Component<object> {
* @param group
* @param emailId
*/
- // eslint-disable-next-line react/no-unused-class-component-methods
removeMember = (group: Doc, emailId: string) => {
const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
@@ -607,7 +602,6 @@ export class SharingManager extends React.Component<object> {
* Removes a group's permissions from documents that have been shared with it.
* @param group
*/
- // eslint-disable-next-line react/no-unused-class-component-methods
removeGroup = (group: Doc) => {
if (group.docsShared) {
DocListCast(group.docsShared).forEach(doc => {
diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts
index 65bd44bf9..0399fe1d5 100644
--- a/src/client/util/bezierFit.ts
+++ b/src/client/util/bezierFit.ts
@@ -1,6 +1,7 @@
/* eslint-disable no-use-before-define */
/* eslint-disable no-param-reassign */
import { Point } from '../../pen-gestures/ndollar';
+import { numberRange } from '../../Utils';
export enum SVGType {
Rect = 'rect',
@@ -623,24 +624,20 @@ export function GenerateControlPoints(coordinates: Point[], alpha = 0.1) {
return [...firstEnd, ...points, ...lastEnd];
}
-function convertToAbsolute(pathData: string): string {
+function convertRelativePathCmdsToAbsolute(pathData: string): string {
const commands = pathData.match(/[a-zA-Z][^a-zA-Z]*/g);
- if (!commands) return pathData;
-
let currentX = 0;
let currentY = 0;
let startX = 0;
let startY = 0;
-
- const absoluteCommands = commands.map(command => {
- const type = command[0];
+ const absoluteCommands = commands?.map(command => {
const values = command
.slice(1)
.trim()
.split(/[\s,]+/)
.map(v => +v);
- switch (type) {
+ switch (command[0]) {
case 'M':
currentX = values[0];
currentY = values[1];
@@ -654,13 +651,19 @@ function convertToAbsolute(pathData: string): string {
startY = currentY;
return `M${currentX},${currentY}`;
case 'L':
- currentX = values[0];
- currentY = values[1];
- return `L${currentX},${currentY}`;
- case 'l':
- currentX += values[0];
- currentY += values[1];
- return `L${currentX},${currentY}`;
+ currentX = values[values.length - 2];
+ currentY = values[values.length - 1];
+ return `L${values.join(',')}`;
+ case 'l': {
+ let str = '';
+ for (let i = 0; i < values.length; i += 2) {
+ str += (i === 0 ? 'L':',') + (values[i] + currentX) +
+ ',' + (values[i + 1] + currentY); // prettier-ignore
+ currentX += values[i];
+ currentY += values[i + 1];
+ }
+ return str;
+ }
case 'H':
currentX = values[0];
return `H${currentX}`;
@@ -674,8 +677,8 @@ function convertToAbsolute(pathData: string): string {
currentY += values[0];
return `V${currentY}`;
case 'C':
- currentX = values[4];
- currentY = values[5];
+ currentX = values[values.length - 2];
+ currentY = values[values.length - 1];
return `C${values.join(',')}`;
case 'c': {
let str = '';
@@ -698,8 +701,8 @@ function convertToAbsolute(pathData: string): string {
case 's':
return `S${values.map((v, i) => (i % 2 === 0 ? (currentX += v) : (currentY += v))).join(',')}`;
case 'Q':
- currentX = values[2];
- currentY = values[3];
+ currentX = values[values.length - 2];
+ currentY = values[values.length - 1];
return `Q${values.join(',')}`;
case 'q': {
let str = '';
@@ -737,7 +740,7 @@ function convertToAbsolute(pathData: string): string {
}
});
- return absoluteCommands.join(' ');
+ return absoluteCommands?.join(' ') ?? pathData;
}
export function SVGToBezier(name: SVGType, attributes: Record<string, string>, last: { X: number; Y: number }): Point[] {
@@ -805,138 +808,73 @@ export function SVGToBezier(name: SVGType, attributes: Record<string, string>, l
];
}
case 'path': {
+ const cmds = new Map<string, number>([
+ ['A', 7],
+ ['C', 6],
+ ['Q', 4],
+ ['L', 2],
+ ['V', 1],
+ ['H', 1],
+ ['Z', 0],
+ ['M', 2],
+ ]);
+ const cmdReg = (letter: string) => `${letter}?${numberRange(cmds.get(letter)??0).map(() => '[, ]?(-?\\d*\\.?\\d*)').join('')}`; // prettier-ignore
+ const pathdata = convertRelativePathCmdsToAbsolute(
+ attributes.d
+ .replace(/([0-9])-/g, '$1,-') // numbers are smooshed together - put a ',' between number-number => number,-number
+ .replace(/([.][0-9]+)(?=\.)/g, '$1,') // numbers are smooshed together - put a ',' between .number.number => .number,.number
+ .trim()
+ );
+ const move = pathdata.match(cmdReg('M'));
+ const start = move?.slice(1).map(v => +v) ?? [last.X, last.Y];
const coordList: Point[] = [];
- let fixedattrs = attributes.d.trim().replace(/([0-9])-/g, '$1,-');
- for (let i = 0; i < 100; i++) {
- const test = fixedattrs.replace(/([0-9]?\.[0-9]+)(\.[0-9]+)/g, '$1,$2');
- if (test === fixedattrs) break;
- fixedattrs = test;
- }
- const attrdata = convertToAbsolute(fixedattrs);
- const move = attrdata.match(/M(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)/);
- const [startX, startY] = move?.slice(1) ?? [last.X + '', last.Y + ''];
- const startPt = { X: +startX, Y: +startY };
- let first = true;
- let lastCmd = '';
- for (let attr = attrdata.slice(move?.[0].length ?? 0).trim(); attr; ) {
- lastCmd = 'AQCLVHZ'.includes(attr[0]) ? attr[0] : lastCmd;
- switch (lastCmd) {
- case 'Q': {
- const match = attr.match(/Q?[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)/);
- if (match) {
- const prev = first ? startPt : coordList.lastElement();
- const Q = [+match[1], +match[2], +match[3], +match[4]];
-
- coordList.push(prev);
- coordList.push({ X: prev.X + (2 / 3) * (Q[0] - prev.X), Y: prev.Y + (2 / 3) * (Q[1] - prev.Y) });
- coordList.push({ X: Q[2] + (2 / 3) * (Q[0] - Q[2]), Y: Q[3] + (2 / 3) * (Q[1] - Q[3]) });
- coordList.push({ X: Q[2], Y: Q[3] });
- attr = attr.slice(match[0].length).trim();
- } else {
- attr = attr.slice(1).trim();
- alert('error' + attr);
- }
- break;
- }
- case 'C': {
- const match = attr.match(/C?[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)/);
- if (match) {
- coordList.push(first ? startPt : coordList.lastElement());
- coordList.push({ X: +match[1], Y: +match[2] });
- coordList.push({ X: +match[3], Y: +match[4] });
- coordList.push({ X: +match[5], Y: +match[6] });
- attr = attr.slice(match[0].length).trim();
- } else {
- attr = attr.slice(1).trim();
- alert('error' + attr);
- }
- break;
- }
- case 'A': {
- const match = attr.match(/A?[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)/);
- if (match) {
- console.log('SKIPPING arc - not implemented');
- // coordList.push(first ? startPt : coordList.lastElement());
- // coordList.push({ X: +match[1], Y: +match[2] });
- // coordList.push({ X: +match[3], Y: +match[4] });
- // coordList.push({ X: +match[5], Y: +match[6] });
- attr = attr.slice(match[0].length).trim();
- } else {
- attr = attr.slice(1).trim();
- alert('error' + attr);
- }
- break;
- }
- case 'L': {
- const match = attr.match(/L?[, ]?(-?\d*\.?\d*)[, ]?(-?\d*\.?\d*)/);
- if (match) {
- coordList.push(first ? startPt : coordList.lastElement());
- coordList.push(coordList.lastElement());
- coordList.push({ X: +match[1], Y: +match[2] });
- coordList.push({ X: +match[1], Y: +match[2] });
- attr = attr.slice(match[0].length).trim();
- } else {
- attr = attr.slice(1).trim();
- alert('error' + attr);
- }
+ for (let prev = coordList.lastElement() ?? { X: start[0], Y: start[1] },
+ pathcmd = pathdata.slice(move?.[0].length ?? 0).trim(),
+ m = move,
+ lastCmd = '';
+ pathcmd;
+ pathcmd = pathcmd.slice(m?.[0].length ?? 1).trim(),
+ prev = coordList.lastElement()
+ ) {
+ lastCmd = Array.from(cmds.keys()).includes(pathcmd[0]) ? pathcmd[0] : lastCmd; // command character is first, otherwise we're continuing coordinates for the last command
+ m = pathcmd.match(new RegExp(cmdReg(lastCmd)))!; // matches command + number parameters specific to command
+ switch (m ? lastCmd : 'error') {
+ case 'Q': // convert quadratic to Bezier
+ ((Q) => coordList.push(
+ prev,
+ { X: prev.X + (2 / 3) * (Q[0] - prev.X), Y: prev.Y + (2 / 3) * (Q[1] - prev.Y) },
+ { X: Q[2] + (2 / 3) * (Q[0] - Q[2]), Y: Q[3] + (2 / 3) * (Q[1] - Q[3]) },
+ { X: Q[2], Y: Q[3] }
+ ))([+m[1], +m[2], +m[3], +m[4]]);
+ break; case 'C': // bezier curve
+ coordList.push(prev, { X: +m[1], Y: +m[2] }, { X: +m[3], Y: +m[4] }, { X: +m[5], Y: +m[6] });
+ break; case 'L': // convert line to bezier
+ coordList.push(prev, prev, { X: +m[1], Y: +m[2] }, { X: +m[1], Y: +m[2] });
+ break; case 'H': // convert horiz line to bezier
+ coordList.push(prev, prev, { X: +m[1], Y: prev.Y }, { X: +m[1], Y: prev.Y });
+ break; case 'V': // convert vert line to bezier
+ coordList.push(prev, prev, { X: prev.X, Y: +m[1] }, { X: prev.X, Y: +m[1] });
+ break; case 'A': // convert arc to bezier
+ console.log('SKIPPING arc - conversion to bezier not implemented');
+ break; case 'Z':
break;
- }
- case 'H': {
- const match = attr.match(/H?[, ]?(-?\d*\.?\d*)/);
- if (match) {
- coordList.push(first ? startPt : coordList.lastElement());
- coordList.push(coordList.lastElement());
- coordList.push({ X: +match[1], Y: coordList.lastElement().Y });
- coordList.push({ X: +match[1], Y: coordList.lastElement().Y });
- attr = attr.slice(match[0].length).trim();
- } else {
- attr = attr.slice(1).trim();
- alert('error' + attr);
- }
- break;
- }
- case 'V': {
- const match = attr.match(/V?[, ]?(-?\d*\.?\d*)/);
- if (match) {
- coordList.push(first ? startPt : coordList.lastElement());
- coordList.push(coordList.lastElement());
- coordList.push({ X: coordList.lastElement().X, Y: +match[1] });
- coordList.push({ X: coordList.lastElement().X, Y: +match[1] });
- attr = attr.slice(match[0].length).trim();
- } else {
- attr = attr.slice(1).trim();
- alert('error' + attr);
- }
- break;
- }
- case 'Z': {
- coordList.push(first ? startPt : coordList.lastElement());
- coordList.push(first ? startPt : coordList.lastElement());
- coordList.push(startPt);
- coordList.push(startPt);
- attr = attr.slice(1).trim();
- break;
- }
default:
- attr = attr.slice(1).trim();
+ // eslint-disable-next-line no-debugger
debugger;
- }
- first = false;
- }
+ } // prettier-ignore
+ } // prettier-ignore
return coordList;
}
case 'polygon': {
- const coords: RegExpMatchArray[] = Array.from(attributes.points.matchAll(/(-?\d+\.?\d*),(-?\d+\.?\d*)/g));
- let list: Point[] = [];
+ const coords = Array.from(attributes.points.matchAll(/(-?\d+\.?\d*),(-?\d+\.?\d*)/g));
+ const list: Point[] = [];
coords.forEach(coord => {
list.push({ X: +coord[1], Y: +coord[2] });
list.push({ X: +coord[1], Y: +coord[2] });
list.push({ X: +coord[1], Y: +coord[2] });
list.push({ X: +coord[1], Y: +coord[2] });
});
- const firstPts = list.splice(0, 2);
- list = list.concat(firstPts);
- return list;
+ return list.concat(list.splice(0, 2)); // repeat start point to close
}
default:
return [];
diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss
index 48fa86276..c2f6ae62d 100644
--- a/src/client/views/AntimodeMenu.scss
+++ b/src/client/views/AntimodeMenu.scss
@@ -6,7 +6,6 @@
height: global.$antimodemenu-height;
width: fit-content;
border-radius: global.$standard-border-radius;
- overflow: hidden;
// box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
// border-radius: 0px 6px 6px 6px;
display: flex;
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 0be847281..5698da785 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -93,7 +93,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
savedKeys.forEach(key => {
lightDoc[key] = this._savedState[key];
});
- this._savedState = {};
+ lightDoc !== doc && (this._savedState = {});
if (doc) {
lightDoc !== doc &&
@@ -136,9 +136,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
return this.SetLightboxDoc(
doc,
undefined,
- [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...this._future].sort(
- (a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)
- ),
+ [...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...this._future].sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)),
layoutTemplate
);
};
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 2170e0c34..e01e92101 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -1,5 +1,5 @@
@use 'global/globalCssVariables.module.scss' as global;
-// bcz: fix @import 'nodeModuleOverrides';
+@import 'nodeModuleOverrides';
html {
overscroll-behavior-x: none;
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 12dd80ec5..fddc0e40c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -75,10 +75,8 @@ import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
import ImageEditorBox from './nodes/imageEditor/ImageEditor';
import { PresBox } from './nodes/trails';
import { AnchorMenu } from './pdf/AnchorMenu';
-import { GPTPopup } from './pdf/GPTPopup/GPTPopup';
import { SmartDrawHandler } from './smartdraw/SmartDrawHandler';
import { TopBar } from './topbar/TopBar';
-import { OverlayView } from './OverlayView';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8e043baee..11adf7435 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -152,9 +152,16 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
return this.selectedDoc?.isGroup;
}
@computed get isStack() {
- return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Card, CollectionViewType.Carousel].includes(
- this.selectedDoc?.type_collection as CollectionViewType
- );
+ return [
+ CollectionViewType.Masonry,
+ CollectionViewType.Multicolumn,
+ CollectionViewType.Multirow,
+ CollectionViewType.Stacking,
+ CollectionViewType.NoteTaking,
+ CollectionViewType.Card,
+ CollectionViewType.Carousel,
+ CollectionViewType.Grid,
+ ].includes(this.selectedDoc?.type_collection as CollectionViewType);
}
rtfWidth = () => (!this.selectedLayoutDoc ? 0 : Math.min(NumCast(this.selectedLayoutDoc?._width), this._props.width - 20));
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index b70e21918..93d6fb684 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -398,8 +398,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
e.stopPropagation();
}}
type="text"
- placeholder="Input tags for document..."
- aria-label="tagsView-input"
+ placeholder="Enter #tags or @metadata"
className="tagsView-input"
style={{ width: '100%', borderRadius: '5px' }}
/>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 539b49c86..04e3b2663 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -48,8 +48,7 @@ export class CollectionDockingView extends CollectionSubView() {
// eslint-disable-next-line no-use-before-define
@observable public static Instance: CollectionDockingView | undefined = undefined;
- private _reactionDisposer?: IReactionDisposer;
- private _lightboxReactionDisposer?: IReactionDisposer;
+ private _disposers: { [key: string]: IReactionDisposer } = {};
private _containerRef = React.createRef<HTMLDivElement>();
private _flush: UndoManager.Batch | undefined;
private _unmounting = false;
@@ -66,6 +65,7 @@ export class CollectionDockingView extends CollectionSubView() {
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
+ console.log('CREATING DOCKING VIEW');
if (this._props.renderDepth < 0) CollectionDockingView.Instance = this;
// Why is this here?
(window as unknown as { React: unknown }).React = React;
@@ -282,6 +282,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
setupGoldenLayout = async () => {
if (this._unmounting) return;
+ console.log('SETUP LAYOUT');
// const config = StrCast(this.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.Document)));
const config = StrCast(this.Document.dockingConfig);
if (config) {
@@ -339,29 +340,36 @@ export class CollectionDockingView extends CollectionSubView() {
componentDidMount: () => void = async () => {
this._props.setContentViewBox?.(this);
this._unmounting = false;
+ console.log('MOUNTING');
SetPropSetterCb('title', this.titleChanged); // this overrides any previously assigned callback for the property
if (this._containerRef.current) {
- this._lightboxReactionDisposer = reaction(
+ this._disposers.lightbox = reaction(
() => DocumentView.LightboxDoc(),
doc => setTimeout(() => !doc && this.onResize())
);
new ResizeObserver(this.onResize).observe(this._containerRef.current);
- this._reactionDisposer = reaction(
+ this._disposers.docking = reaction(
() => StrCast(this.Document.dockingConfig),
config => {
if (!this._goldenLayout || this._ignoreStateChange !== config) {
+ console.log('CONFIG CHANGED');
// bcz: TODO! really need to diff config with ignoreStateChange and modify the current goldenLayout instead of building a new one.
this.setupGoldenLayout();
}
this._ignoreStateChange = '';
}
);
- reaction(
+ this._disposers.panel = reaction(
() => this._props.PanelWidth(),
- width => !this._goldenLayout && width > 20 && setTimeout(() => this.setupGoldenLayout()), // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
+ width => {
+ if (!this._goldenLayout && width > 20) {
+ console.log('PWIDTH = ' + width);
+ setTimeout(() => this.setupGoldenLayout());
+ }
+ }, // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
{ fireImmediately: true }
);
- reaction(
+ this._disposers.color = reaction(
() => [SnappingManager.userBackgroundColor, SnappingManager.userBackgroundColor],
() => {
clearStyleSheetRules(CollectionDockingView._highlightStyleSheet);
@@ -375,7 +383,9 @@ export class CollectionDockingView extends CollectionSubView() {
};
componentWillUnmount: () => void = () => {
+ console.log('UNMOUNTING');
this._unmounting = true;
+ Object.values(this._disposers).forEach(d => d());
try {
this._goldenLayout.unbind('stackCreated', this.stackCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
@@ -385,9 +395,6 @@ export class CollectionDockingView extends CollectionSubView() {
setTimeout(() => this._goldenLayout?.destroy());
window.removeEventListener('resize', this.onResize);
window.removeEventListener('mouseup', this.onPointerUp);
-
- this._reactionDisposer?.();
- this._lightboxReactionDisposer?.();
};
// ViewBoxInterface overrides
@@ -412,6 +419,7 @@ export class CollectionDockingView extends CollectionSubView() {
.map(f => f as Doc);
const changesMade = this.Document.dockingConfig !== json;
if (changesMade) {
+ console.log('WRITING CONFIG');
if (![AclAdmin, AclEdit].includes(GetEffectiveAcl(this.dataDoc))) {
this.layoutDoc.dockingConfig = json;
this.layoutDoc.data = new List<Doc>(docs);
@@ -426,7 +434,7 @@ export class CollectionDockingView extends CollectionSubView() {
@action
onPointerUp = (): void => {
- window.removeEventListener('pointerup', this.onPointerUp);
+ window.removeEventListener('mouseup', this.onPointerUp);
DragManager.CompleteWindowDrag = undefined;
setTimeout(this.endUndoBatch, 100);
};
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index f06e0b551..96125f2c2 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -111,7 +111,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// columnWidth handles the margin on the left and right side of the documents
@computed get columnWidth() {
const availableWidth = this._props.PanelWidth() - 2 * this.xMargin;
- const cwid = availableWidth / NumCast(this.Document._layout_columnCount, 1);
+ const cwid = availableWidth / (NumCast(this.Document._layout_columnCount) || this._props.PanelWidth() / NumCast(this.Document._layout_columnWidth, this._props.PanelWidth() / 4));
return Math.min(availableWidth, this.isStackingView ? Number.MAX_VALUE : cwid - this.gridGap);
}
@@ -241,7 +241,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
this._disposers.width = reaction(
() => [this._props.PanelWidth() - 2 * this.xMargin, NumCast(this.Document._layout_columnWidth, 250)],
([pw, cw]) => {
- if (!this.isStackingView) {
+ if (!this.isStackingView && Math.round(pw / cw)) {
this.layoutDoc._layout_columnCount = Math.round(pw / cw);
}
}
@@ -607,7 +607,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const rows = () => (!this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, Math.floor((this._props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))));
return (
<div key={(heading?.heading ?? '') + 'head'}>
- {this._props.isContentActive() && !this.isStackingView ? this.columnDragger : null}
+ {this._props.isContentActive() && !this.isStackingView && !this.chromeHidden ? this.columnDragger : null}
<div style={{ top: this.yMargin }}>
<CollectionMasonryViewFieldRow
showHandle={first}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b3d908da4..43addfc29 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -344,7 +344,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
focusOnPoint = (options: FocusViewOptions) => {
const { pointFocus, zoomTime, didMove } = options;
if (!this.Document.isGroup && pointFocus && !didMove) {
- const dfltScale = this.isAnnotationOverlay ? 1 : 0.5;
+ const dfltScale = this.isAnnotationOverlay ? 1 : 0.25;
if (this.layoutDoc[this.scaleFieldKey] !== dfltScale) {
this.zoomSmoothlyAboutPt(this.screenToFreeformContentsXf.transformPoint(pointFocus.X, pointFocus.Y), dfltScale, zoomTime);
options.didMove = true;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index 845b07c51..4edaf9745 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -21,11 +21,11 @@
width: 100%;
}
- .react-grid-item>.react-resizable-handle {
+ .react-grid-item > .react-resizable-handle {
z-index: 4; // doesn't work on prezi otherwise
}
- .react-grid-item>.react-resizable-handle::after {
+ .react-grid-item > .react-resizable-handle::after {
// grey so it can be seen on the audiobox
border-right: 2px solid slategrey;
border-bottom: 2px solid slategrey;
@@ -41,7 +41,6 @@
position: absolute;
height: 3;
left: 5;
- top: 30;
transform-origin: left;
transform: rotate(90deg);
outline: none;
@@ -103,7 +102,7 @@
span::before,
span::after {
- content: "";
+ content: '';
width: 50%;
position: relative;
display: inline-block;
@@ -128,10 +127,8 @@
}
}
}
-
}
-
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
@@ -140,6 +137,6 @@ input::-webkit-inner-spin-button {
}
/* Firefox */
-input[type=number] {
+input[type='number'] {
-moz-appearance: textfield;
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 6dffb80f1..80bf4bd12 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -9,7 +9,7 @@ import { emptyFunction } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoable } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ContextMenuProps } from '../../ContextMenuItem';
import { DocumentView } from '../../nodes/DocumentView';
@@ -22,9 +22,9 @@ export class CollectionGridView extends CollectionSubView() {
private _containerRef: React.RefObject<HTMLDivElement> = React.createRef();
private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
+ private _dropLocation: object = {}; // sets the drop location for external drops
@observable private _rowHeight: Opt<number> = undefined; // temporary store of row height to make change undoable
@observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
- private dropLocation: object = {}; // sets the drop location for external drops
constructor(props: SubCollectionViewProps) {
super(props);
@@ -48,14 +48,20 @@ export class CollectionGridView extends CollectionSubView() {
}
@computed get colWidthPlusGap() {
- return (this._props.PanelWidth() - this.margin) / this.numCols;
+ return (this._props.PanelWidth() - 2 * this.xMargin - this.gridGap) / this.numCols;
}
@computed get rowHeightPlusGap() {
- return this.rowHeight + this.margin;
+ return this.rowHeight + this.gridGap;
}
- @computed get margin() {
- return NumCast(this.Document.margin, 10);
+ @computed get xMargin() {
+ return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
+ }
+ @computed get yMargin() {
+ return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+ }
+ @computed get gridGap() {
+ return NumCast(this.Document._gridGap, 10);
} // sets the margin between grid nodes
@computed get flexGrid() {
@@ -77,10 +83,10 @@ export class CollectionGridView extends CollectionSubView() {
pairs.forEach((pair, i) => {
const existing = oldLayouts.find(l => l.i === pair.layout[Id]);
if (existing) newLayouts.push(existing);
- else if (Object.keys(this.dropLocation).length) {
+ else if (Object.keys(this._dropLocation).length) {
// external drop
- this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number; y: number }, !this.flexGrid));
- this.dropLocation = {};
+ this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this._dropLocation as { x: number; y: number }, !this.flexGrid));
+ this._dropLocation = {};
} else {
// internal drop
this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid));
@@ -115,30 +121,29 @@ export class CollectionGridView extends CollectionSubView() {
* @returns the default location of the grid node (i.e. when the grid is static)
* @param index
*/
- unflexedPosition(index: number): Omit<Layout, 'i'> {
- return {
- x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
- y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
- w: this.defaultW,
- h: this.defaultH,
- static: true,
- };
- }
+ unflexedPosition = (index: number): Omit<Layout, 'i'> => ({
+ x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
+ y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
+ w: this.defaultW,
+ h: this.defaultH,
+ static: true,
+ });
/**
* Maps the x- and y- coordinates of the event to a grid cell.
*/
- screenToCell(sx: number, sy: number) {
- const pt = this.ScreenToLocalBoxXf().transformPoint(sx, sy);
- const x = Math.floor(pt[0] / this.colWidthPlusGap);
- const y = Math.floor((pt[1] + this._scroll) / this.rowHeight);
+ screenToCell = (sx: number, sy: number) => {
+ const [ptx, pty] = this.ScreenToLocalBoxXf().transformPoint(sx, sy);
+ const x = Math.floor((ptx + this.xMargin) / this.colWidthPlusGap);
+ const y = Math.floor((pty + this.yMargin + this._scroll) / this.rowHeight);
return { x, y };
- }
+ };
/**
* Creates a layout object for a grid item
*/
- makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static });
+ makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) =>
+ ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static }); // prettier-ignore
/**
* Adds a layout to the list of layouts.
@@ -152,9 +157,9 @@ export class CollectionGridView extends CollectionSubView() {
/**
* @returns the transform that will correctly place the document decorations box.
*/
- private lookupIndividualTransform = (layout: Layout) => {
+ lookupIndividualTransform = (layout: Layout) => {
const xypos = this.flexGrid ? layout : this.unflexedPosition(this.renderedLayoutList.findIndex(l => l.i === layout.i));
- const pos = { x: xypos.x * this.colWidthPlusGap + this.margin, y: xypos.y * this.rowHeightPlusGap + this.margin - this._scroll };
+ const pos = { x: xypos.x * this.colWidthPlusGap + this.gridGap + this.xMargin, y: xypos.y * this.rowHeightPlusGap + this.gridGap - this._scroll + this.yMargin };
return this.ScreenToLocalBoxXf().translate(-pos.x, -pos.y);
};
@@ -169,9 +174,9 @@ export class CollectionGridView extends CollectionSubView() {
/**
* Stores the layout list on the Document as JSON
*/
- setLayoutList(layouts: Layout[]) {
+ setLayoutList = (layouts: Layout[]) => {
this.Document.gridLayoutString = JSON.stringify(layouts);
- }
+ };
isContentActive = () => this._props.isSelected() || this._props.isContentActive();
isChildContentActive = () => (this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
@@ -183,29 +188,27 @@ export class CollectionGridView extends CollectionSubView() {
* @param height
* @returns the `ContentFittingDocumentView` of the node
*/
- getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
- return (
- <DocumentView
- {...this._props}
- Document={layout}
- TemplateDataDocument={layout.resolvedDataDoc as Doc}
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- fitWidth={this._props.childLayoutFitWidth}
- containerViewPath={this.childContainerViewPath}
- renderDepth={this._props.renderDepth + 1}
- isContentActive={this.isChildContentActive}
- PanelWidth={width}
- PanelHeight={height}
- ScreenToLocalTransform={dxf}
- setContentViewBox={emptyFunction}
- whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- onClickScript={this.onChildClickHandler}
- dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
- showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
- />
- );
- }
+ getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => (
+ <DocumentView
+ {...this._props}
+ Document={layout}
+ TemplateDataDocument={layout.resolvedDataDoc as Doc}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
+ fitWidth={this._props.childLayoutFitWidth}
+ containerViewPath={this.childContainerViewPath}
+ renderDepth={this._props.renderDepth + 1}
+ isContentActive={this.isChildContentActive}
+ PanelWidth={width}
+ PanelHeight={height}
+ ScreenToLocalTransform={dxf}
+ setContentViewBox={emptyFunction}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ onClickScript={this.onChildClickHandler}
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
+ showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
+ />
+ );
/**
* Saves the layouts received from the Grid to the Document.
@@ -238,15 +241,14 @@ export class CollectionGridView extends CollectionSubView() {
* @returns a list of `ContentFittingDocumentView`s inside wrapper divs.
* The key of the wrapper div must be the same as the `i` value of the corresponding layout.
*/
- @computed
- private get contents(): JSX.Element[] {
+ @computed get contents(): JSX.Element[] {
const collector: JSX.Element[] = [];
if (this.renderedLayoutList.length === this.childLayoutPairs.length) {
this.renderedLayoutList.forEach(l => {
const child = this.childLayoutPairs.find(c => c.layout[Id] === l.i);
const dxf = () => this.lookupIndividualTransform(l);
- const width = () => (this.flexGrid ? l.w : this.defaultW) * this.colWidthPlusGap - this.margin;
- const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.margin;
+ const width = () => (this.flexGrid ? l.w : this.defaultW) * this.colWidthPlusGap - this.gridGap;
+ const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.gridGap;
child &&
collector.push(
<div key={child.layout[Id]} className={'document-wrapper' + (this.flexGrid && this._props.isSelected() ? '' : ' static')}>
@@ -295,7 +297,7 @@ export class CollectionGridView extends CollectionSubView() {
* Handles external drop of images/PDFs etc from outside Dash.
*/
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
- this.dropLocation = this.screenToCell(e.clientX, e.clientY);
+ this._dropLocation = this.screenToCell(e.clientX, e.clientY);
super.onExternalDrop(e, {});
};
@@ -316,12 +318,13 @@ export class CollectionGridView extends CollectionSubView() {
this,
e,
returnFalse,
- action(() => {
- undoable(() => {
+ undoable(
+ action(() => {
this.Document.gridRowHeight = this._rowHeight;
- }, 'changing row height')();
- this._rowHeight = undefined;
- }),
+ this._rowHeight = undefined;
+ }),
+ 'changing row height'
+ ),
emptyFunction,
false,
false
@@ -391,14 +394,14 @@ export class CollectionGridView extends CollectionSubView() {
<div
className="collectionGridView-gridContainer"
ref={this._containerRef}
- style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white') }}
+ style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white'), padding: `${this.yMargin} ${this.xMargin}` }}
onWheel={e => e.stopPropagation()}
onScroll={action(e => {
if (!this._props.isSelected()) e.currentTarget.scrollTop = this._scroll;
else this._scroll = e.currentTarget.scrollTop;
})}>
<Grid
- width={this._props.PanelWidth()}
+ width={this._props.PanelWidth() - 2 * this.xMargin}
nodeList={this.contents.length ? this.contents : null}
layout={this.contents.length ? this.renderedLayoutList : undefined}
childrenDraggable={!!this._props.isSelected()}
@@ -408,15 +411,15 @@ export class CollectionGridView extends CollectionSubView() {
transformScale={this.ScreenToLocalBoxXf().Scale}
compactType={this.compaction} // determines whether nodes should remain in position, be bound to the top, or to the left
preventCollision={BoolCast(this.Document.gridPreventCollision)} // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
- margin={this.margin}
+ margin={this.gridGap}
/>
<input
className="rowHeightSlider"
type="range"
- style={{ width: this._props.PanelHeight() - 30 }}
+ style={{ width: this._props.PanelHeight() - 2 * this.yMargin }}
min={1}
value={this.rowHeight}
- max={this._props.PanelHeight() - 30}
+ max={this._props.PanelHeight() - 2 * this.yMargin}
onPointerDown={this.onSliderDown}
onChange={this.onSliderChange}
/>
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 76d6ed80b..ce1e9280a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -180,7 +180,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
const timecode = Math.round(time);
Object.keys(vals).forEach(val => {
const findexed = Cast(d[`${val}_indexed`], listSpec('number'), []).slice();
- findexed[timecode] = vals[val] || 0;
+ findexed[timecode] = vals[val] as unknown as number;
d[`${val}_indexed`] = new List<number>(findexed);
});
}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 294af4d96..dd5fd0d0c 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -291,6 +291,7 @@
justify-items: center;
background-color: rgb(223, 223, 223);
transform-origin: top left;
+ background: transparent;
.documentView-editorView-resizer {
height: 5px;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5f5dd1210..595abc7f8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -279,16 +279,17 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
setTimeout(() => this._titleRef.current?.setIsFocused(true)); // use timeout in case title wasn't shown to allow re-render so that titleref will be defined
};
onBrowseClick = (e: React.MouseEvent) => {
- const browseTransitionTime = 500;
+ //const browseTransitionTime = 500;
DocumentView.DeselectAll();
DocumentView.showDocument(this.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
- const options: FocusViewOptions = { pointFocus: { X: e.clientX, Y: e.clientY }, zoomTime: browseTransitionTime };
+ // const options: FocusViewOptions = { pointFocus: { X: e.clientX, Y: e.clientY }, zoomTime: browseTransitionTime };
if (!focused && this._docView) {
- this._docView
- .docViewPath()
- .reverse()
- .forEach(cont => cont.ComponentView?.focus?.(cont.Document, options));
- Doc.linkFollowHighlight(this.Document, false);
+ DocumentView.showDocument(this.Document, { zoomScale: 0.3, willZoomCentered: true });
+ // this._docView
+ // .docViewPath()
+ // .reverse()
+ // .forEach(cont => cont.ComponentView?.focus?.(cont.Document, options));
+ // Doc.linkFollowHighlight(this.Document, false);
}
});
e.stopPropagation();
@@ -797,6 +798,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
<div
className="documentView-editorView"
style={{
+ background: SnappingManager.userVariantColor,
width: `${100 / this.uiBtnScaling}%`, //
transform: `scale(${this.uiBtnScaling})`,
}}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 671621bbe..3d6942e6f 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -40,6 +40,7 @@
max-height: 100%;
pointer-events: inherit;
background: transparent;
+ z-index: 0;
// z-index: -10000; // bcz: not sure why this was here. it broke dropping images on the image box alternate bullseye icon.
img {
@@ -103,6 +104,10 @@
margin: 0 auto;
display: flex;
height: 100%;
+ img {
+ object-fit: contain;
+ height: 100%;
+ }
.imageBox-fadeBlocker,
.imageBox-fadeBlocker-hover {
@@ -195,8 +200,9 @@
flex-direction: row;
gap: 5px;
width: 100%;
+ padding: 0 10;
.imageBox-aiView-regenerate-createBtn {
- max-width: 10%;
+ max-width: 20%;
.button-container {
width: 100% !important;
justify-content: left !important;
@@ -207,7 +213,7 @@
.imageBox-aiView-firefly {
overflow: hidden;
text-overflow: ellipsis;
- max-width: 10%;
+ max-width: 15%;
width: 100%;
}
.imageBox-aiView-regenerate-send {
@@ -218,7 +224,7 @@
text-align: center;
align-items: center;
display: flex;
- max-width: 25%;
+ max-width: 90%;
width: 100%;
.imageBox-aiView-similarity {
max-width: 65;
@@ -236,5 +242,6 @@
text-overflow: ellipsis;
max-width: 65%;
width: 100%;
+ color: black;
}
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 279317f49..017ef7191 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -436,6 +436,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
getScrollHeight = () => (this._props.fitWidth?.(this.Document) !== false && NumCast(this.layoutDoc._freeform_scale, 1) === NumCast(this.dataDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined);
+ @computed get usingAlternate() {
+ const usePath = StrCast(this.Document[this.fieldKey + '_usePath']);
+ return 'alternate' === usePath || ('alternate:hover' === usePath && this._isHovering) || (':hover' === usePath && !this._isHovering);
+ }
+
@computed get nativeSize() {
TraceMobx();
if (this.paths.length && this.paths[0].includes('icon-hi')) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0 };
@@ -471,10 +476,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<span style={{ color: usePath === 'alternate' ? 'black' : undefined }}>
<em>alternate, </em>
</span>
- and show
<span style={{ color: usePath === 'alternate:hover' ? 'black' : undefined }}>
<em> alternate on hover</em>
</span>
+ and show
+ <span style={{ color: usePath === ':hover' ? 'black' : undefined }}>
+ <em> primary on hover</em>
+ </span>
</div>
}>
<div
@@ -482,7 +490,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ref={this._overlayIconRef}
onPointerDown={e =>
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, () => {
- this.layoutDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
+ this.layoutDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : usePath === 'alternate:hover' ? ':hover' : undefined;
})
}
style={{
@@ -527,7 +535,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
.filter(url => url)
.map(url => this.choosePath(url)) ?? []; // acc ess the primary layout data of the alternate documents
const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
- return paths.length ? paths : [defaultUrl.href];
+ return paths.length ? paths.reverse() : [defaultUrl.href];
}
@computed get content() {
@@ -552,7 +560,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
transformOrigin = 'right top';
transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
}
- const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`];
return (
<div
@@ -571,7 +578,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ref={action((r: HTMLImageElement | null) => (this.imageRef = r))}
key="paths"
src={srcpath}
- style={{ transform, transformOrigin, objectFit: 'fill', height: '100%' }}
+ style={{ transform, transformOrigin }}
onError={action(e => {
this._error = e.toString();
})}
@@ -579,7 +586,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
width={nativeWidth}
/>
{fadepath === srcpath ? null : (
- <div className={`imageBox-fadeBlocker${(this._isHovering && usePath === 'alternate:hover') || usePath === 'alternate' ? '-hover' : ''}`} style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
+ <div className={`imageBox-fadeBlocker${this.usingAlternate ? '-hover' : ''}`} style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
<img alt="" className="imageBox-fadeaway" key="fadeaway" src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
</div>
)}
@@ -619,37 +626,24 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return (
<div className="imageBox-aiView">
<div className="imageBox-aiView-regenerate">
- <span className="imageBox-aiView-firefly">Firefly:</span>
+ <span className="imageBox-aiView-firefly" style={{ color: SnappingManager.userColor }}>
+ Firefly:
+ </span>
<input
+ style={{ color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}
className="imageBox-aiView-input"
aria-label="Edit instructions input"
type="text"
- value={this._regenInput}
+ value={this._regenInput || StrCast(this.Document.title)}
onChange={action(e => this._canInteract && (this._regenInput = e.target.value))}
placeholder={this._regenInput || StrCast(this.Document.title)}
/>
- <div className="imageBox-aiView-strength">
- <span className="imageBox-aiView-similarity">Similarity</span>
- <Slider
- className="imageBox-aiView-slider"
- sx={{
- '& .MuiSlider-track': { color: SettingsManager.userVariantColor },
- '& .MuiSlider-rail': { color: SettingsManager.userBackgroundColor },
- '& .MuiSlider-thumb': { color: SettingsManager.userVariantColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
- }}
- min={0}
- max={100}
- step={1}
- size="small"
- value={this._fireflyRefStrength}
- onChange={action((e, val) => this._canInteract && (this._fireflyRefStrength = val as number))}
- valueLabelDisplay="auto"
- />
- </div>
<div className="imageBox-aiView-regenerate-createBtn">
<Button
text="Create"
- type={Type.SEC}
+ type={Type.TERT}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userBackgroundColor}
// style={{ alignSelf: 'flex-end' }}
icon={this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
iconPlacement="right"
@@ -680,61 +674,24 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
/>
</div>
</div>
- <div className="imageBox-aiView-options">
- <span className="imageBox-aiView-subtitle"> More: </span>
- <Button
- type={Type.TERT}
- text="Get Text"
- icon={<FontAwesomeIcon icon="font" />}
- color={SettingsManager.userBackgroundColor}
- iconPlacement="right"
- onClick={() => {
- Networking.PostToServer('/queryFireflyImageText', {
- file: (file => {
- const ext = extname(file);
- return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
- })(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
- }).then(text => alert(text));
- }}
- />
- <Button
- type={Type.TERT}
- text="Generative Fill"
- icon={<FontAwesomeIcon icon="fill" />}
- color={SettingsManager.userBackgroundColor}
- iconPlacement="right"
- onClick={action(() => {
- ImageEditorData.Open = true;
- ImageEditorData.Source = (field && this.choosePath(field.url)) || '';
- ImageEditorData.AddDoc = this._props.addDocument;
- ImageEditorData.RootDoc = this.Document;
- })}
- />
- <Button
- type={Type.TERT}
- text="Expand"
- icon={<FontAwesomeIcon icon="expand" />}
- color={SettingsManager.userBackgroundColor}
- iconPlacement="right"
- onClick={() => {
- Networking.PostToServer('/expandImage', {
- prompt: 'sunny skies',
- file: (file => {
- const ext = extname(file);
- return file.replace(ext, (this._error ? '_o' : this._curSuffix) + ext);
- })(ImageCast(this.Document[Doc.LayoutFieldKey(this.Document)])?.url.href),
- }).then(res => {
- const info = res as Upload.ImageInformation;
- const img = Docs.Create.ImageDocument(info.accessPaths.agnostic.client, { title: 'expand:' + this.Document.title });
- DocUtils.assignImageInfo(info, img);
- const genratedDocs = this.Document.generatedDocs
- ? DocCast(this.Document.generatedDocs)
- : Docs.Create.MasonryDocument([], { _width: 400, _height: 400, x: NumCast(this.Document.x) + NumCast(this.Document.width), y: NumCast(this.Document.y) });
- Doc.AddDocToList(genratedDocs, undefined, img);
- this.Document[DocData].generatedDocs = genratedDocs;
- if (!DocumentView.getFirstDocumentView(genratedDocs)) this._props.addDocTab(genratedDocs, OpenWhere.addRight);
- });
+ <div className="imageBox-aiView-strength">
+ <span className="imageBox-aiView-similarity" style={{ color: SnappingManager.userColor }}>
+ Similarity
+ </span>
+ <Slider
+ className="imageBox-aiView-slider"
+ sx={{
+ '& .MuiSlider-track': { color: SettingsManager.userColor },
+ '& .MuiSlider-rail': { color: SettingsManager.userBackgroundColor },
+ '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
}}
+ min={0}
+ max={100}
+ step={1}
+ size="small"
+ value={this._fireflyRefStrength}
+ onChange={action((e, val) => this._canInteract && (this._fireflyRefStrength = val as number))}
+ valueLabelDisplay="auto"
/>
</div>
</div>
@@ -782,11 +739,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return { width, height };
};
savedAnnotations = () => this._savedAnnotations;
-
render() {
TraceMobx();
const borderRad = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding) as string;
const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this._props.NativeDimScaling?.() || 1)}px` : borderRad;
+ const alts = DocListCast(this.dataDoc[this.fieldKey + '_alternates']);
+ const doc = this.usingAlternate ? (alts.lastElement() ?? this.Document) : this.Document;
return (
<div
className="imageBox"
@@ -811,6 +769,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<CollectionFreeFormView
ref={this._ffref}
{...this._props}
+ Document={doc}
setContentViewBox={emptyFunction}
NativeWidth={returnZero}
NativeHeight={returnZero}
diff --git a/src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts b/src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts
index ef4bbbc47..754d230c8 100644
--- a/src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts
+++ b/src/client/views/nodes/chatbot/tools/CreateAnyDocTool.ts
@@ -13,63 +13,63 @@ const standardOptions = ['title', 'backgroundColor'];
* Description of document options and data field for each type.
*/
const documentTypesInfo: { [key in supportedDocTypes]: { options: string[]; dataDescription: string } } = {
- [supportedDocumentTypes.flashcard]: {
+ [supportedDocTypes.flashcard]: {
options: [...standardOptions, 'fontColor', 'text_align'],
dataDescription: 'an array of two strings. the first string contains a question, and the second string contains an answer',
},
- [supportedDocumentTypes.text]: {
+ [supportedDocTypes.text]: {
options: [...standardOptions, 'fontColor', 'text_align'],
dataDescription: 'The text content of the document.',
},
- [supportedDocumentTypes.html]: {
+ [supportedDocTypes.html]: {
options: [],
dataDescription: 'The HTML-formatted text content of the document.',
},
- [supportedDocumentTypes.equation]: {
+ [supportedDocTypes.equation]: {
options: [...standardOptions, 'fontColor'],
dataDescription: 'The equation content as a string.',
},
- [supportedDocumentTypes.functionplot]: {
+ [supportedDocTypes.functionplot]: {
options: [...standardOptions, 'function_definition'],
dataDescription: 'The function definition(s) for plotting. Provide as a string or array of function definitions.',
},
- [supportedDocumentTypes.dataviz]: {
+ [supportedDocTypes.dataviz]: {
options: [...standardOptions, 'chartType'],
dataDescription: 'A string of comma-separated values representing the CSV data.',
},
- [supportedDocumentTypes.notetaking]: {
+ [supportedDocTypes.notetaking]: {
options: standardOptions,
dataDescription: 'The initial content or structure for note-taking.',
},
- [supportedDocumentTypes.rtf]: {
+ [supportedDocTypes.rtf]: {
options: standardOptions,
dataDescription: 'The rich text content in RTF format.',
},
- [supportedDocumentTypes.image]: {
+ [supportedDocTypes.image]: {
options: standardOptions,
dataDescription: 'The image content as an image file URL.',
},
- [supportedDocumentTypes.pdf]: {
+ [supportedDocTypes.pdf]: {
options: standardOptions,
dataDescription: 'the pdf content as a PDF file url.',
},
- [supportedDocumentTypes.audio]: {
+ [supportedDocTypes.audio]: {
options: standardOptions,
dataDescription: 'The audio content as a file url.',
},
- [supportedDocumentTypes.video]: {
+ [supportedDocTypes.video]: {
options: standardOptions,
dataDescription: 'The video content as a file url.',
},
- [supportedDocumentTypes.message]: {
+ [supportedDocTypes.message]: {
options: standardOptions,
dataDescription: 'The message content of the document.',
},
- [supportedDocumentTypes.diagram]: {
+ [supportedDocTypes.diagram]: {
options: ['title', 'backgroundColor'],
dataDescription: 'diagram content as a text string in Mermaid format.',
},
- [supportedDocumentTypes.script]: {
+ [supportedDocTypes.script]: {
options: ['title', 'backgroundColor'],
dataDescription: 'The compilable JavaScript code. Use this for creating scripts.',
},
diff --git a/src/client/views/nodes/chatbot/tools/ImageCreationTool.ts b/src/client/views/nodes/chatbot/tools/ImageCreationTool.ts
index e92d87dfd..37907fd4f 100644
--- a/src/client/views/nodes/chatbot/tools/ImageCreationTool.ts
+++ b/src/client/views/nodes/chatbot/tools/ImageCreationTool.ts
@@ -5,6 +5,7 @@ import { ParametersType, ToolInfo } from '../types/tool_types';
import { Observation } from '../types/types';
import { BaseTool } from './BaseTool';
import { Upload } from '../../../../../server/SharedMediaTypes';
+import { List } from '../../../../../fields/List';
const imageCreationToolParams = [
{
@@ -41,7 +42,7 @@ export class ImageCreationTool extends BaseTool<ImageCreationToolParamsType> {
image_prompt,
})) as { result: Upload.FileInformation & Upload.InspectionResults; url: string };
console.log('Image generation result:', result);
- this._createImage(result, { text: RTFCast(image_prompt) });
+ this._createImage(result, { text: RTFCast(image_prompt), ai: 'dall-e-3', tags: new List<string>(['@ai']) });
return url
? [
{
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index da2131940..c2a2caecf 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1283,13 +1283,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this._disposers.componentHeights = reaction(
// set the document height when one of the component heights changes and layout_autoHeight is on
() => ({ border: this._props.PanelHeight(), sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layoutAutoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }),
- ({ sidebarHeight, textHeight, layoutAutoHeight, marginsHeight }) => {
+ ({ border, sidebarHeight, textHeight, layoutAutoHeight, marginsHeight }) => {
const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
if (
(!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this._props.isSelected()) && //
layoutAutoHeight &&
newHeight &&
- newHeight !== this.layoutDoc.height &&
+ (newHeight !== this.layoutDoc.height || border < NumCast(this.layoutDoc.height)) &&
!this._props.dontRegisterView
) {
this._props.setHeight?.(newHeight);
@@ -1669,7 +1669,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
for (let target: HTMLElement | Element | null = clickTarget as HTMLElement; target instanceof HTMLElement && !target.dataset?.targethrefs; target = target.parentElement);
while (clickTarget instanceof HTMLElement && !clickTarget.dataset?.targethrefs) clickTarget = clickTarget.parentElement;
const dataset = clickTarget instanceof HTMLElement ? clickTarget?.dataset : undefined;
- FormattedTextBoxComment.update(this, this.EditorView!, undefined, dataset?.targethrefs, dataset?.linkdoc, dataset?.nopreview === 'true');
+
+ if (dataset?.targethrefs)
+ window
+ .open(
+ dataset?.targethrefs
+ ?.trim()
+ .split(' ')
+ .filter(h => h)
+ .lastElement(),
+ '_blank'
+ )
+ ?.focus();
+ else FormattedTextBoxComment.update(this, this.EditorView!, undefined, dataset?.targethrefs, dataset?.linkdoc, dataset?.nopreview === 'true');
}
};
@action
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 28371594e..9aa8fe649 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -18,6 +18,7 @@ import { DocumentView } from '../nodes/DocumentView';
import { DrawingOptions, SmartDrawHandler } from '../smartdraw/SmartDrawHandler';
import './AnchorMenu.scss';
import { GPTPopup } from './GPTPopup/GPTPopup';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -241,6 +242,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
)}
+ {this._selectedText && RichTextMenu.Instance?.createLinkButton()}
{AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
<IconButton
tooltip="Click to Record Annotation" //
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 79f5121ed..4dc45e6a0 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -30,6 +30,7 @@ import { Upload } from '../../../../server/SharedMediaTypes';
import { OpenWhere } from '../../nodes/OpenWhere';
import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
import { ImageField } from '../../../../fields/URLField';
+import { List } from '../../../../fields/List';
export enum GPTPopupMode {
SUMMARY, // summary of seleted document text
@@ -352,6 +353,8 @@ export class GPTPopup extends ObservableReactComponent<object> {
y: NumCast(textAnchor.y),
_height: 200,
_width: 200,
+ ai: 'dall-e',
+ tags: new List<string>(['@ai']),
data_nativeWidth: 1024,
data_nativeHeight: 1024,
});
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index 0a30b14b8..c672bc718 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -1,6 +1,7 @@
import { imageUrlToBase64 } from '../../../ClientUtils';
import { Doc, StrListCast } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
+import { List } from '../../../fields/List';
import { DocCast, ImageCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { Upload } from '../../../server/SharedMediaTypes';
@@ -53,7 +54,10 @@ export class DrawingFillHandler {
undefined,
Docs.Create.ImageDocument(info.accessPaths.agnostic.client, {
ai: 'firefly',
+ tags: new List<string>(['@ai']),
title: newPrompt,
+ _data_usePath: 'alternate:hover',
+ data_alternates: new List<Doc>([drawing]),
ai_firefly_prompt: newPrompt,
_width: 500,
data_nativeWidth: info.nativeWidth,
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index ca308015d..1cceabed3 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -28,6 +28,7 @@ import { FireflyDimensionsMap, FireflyImageData, FireflyImageDimensions } from '
import './SmartDrawHandler.scss';
import { Upload } from '../../../server/SharedMediaTypes';
import { PointData } from '../../../pen-gestures/GestureTypes';
+import { List } from '../../../fields/List';
export interface DrawingOptions {
text?: string;
@@ -293,15 +294,17 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
return undefined;
}
const newseed = img.accessPaths.agnostic.client.match(/\/(\d+)upload/)?.[1];
- const imgDoc: Doc = Docs.Create.ImageDocument(img.accessPaths.agnostic.client, {
+ return Docs.Create.ImageDocument(img.accessPaths.agnostic.client, {
title: input,
nativeWidth: dims.width,
nativeHeight: dims.height,
+ tags: new List<string>(['@ai']),
+ _width: Math.min(400, dims.width),
+ _height: (Math.min(400, dims.width) * dims.height) / dims.width,
ai: 'firefly',
ai_firefly_seed: +(newseed ?? 0),
ai_firefly_prompt: input,
});
- return imgDoc;
})
.catch(e => {
alert('create image failed: ' + e.toString());
@@ -568,6 +571,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
color={SettingsManager.userColor}
/>
<input
+ style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}
aria-label="Smart Draw Input"
className="smartdraw-input"
type="text"