diff options
author | bobzel <zzzman@gmail.com> | 2023-08-26 13:36:56 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-26 13:36:56 -0400 |
commit | 603e437d4964c2f104a04f4f977802e241347344 (patch) | |
tree | ffe3e12746749f64b5d47a2c2fbafbe1056bd76a /src | |
parent | 1108eee6c72b1b7f74a400a7af55c2f71d09c333 (diff) | |
parent | 778ea6d9d88c06e5584b5b4d4989a011ad5424c2 (diff) |
Merge pull request #216 from brown-dash/sophie-ai-images
Image Editor fix for browndash, small icon/ui fixes
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/RTFMarkup.tsx | 12 | ||||
-rw-r--r-- | src/client/views/ContextMenuItem.tsx | 25 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/FontIconBox/TrailsIcon.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 10 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/generativeFill/GenerativeFill.tsx | 31 | ||||
-rw-r--r-- | src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts | 28 |
11 files changed, 92 insertions, 39 deletions
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx index a0fc617ab..78069d323 100644 --- a/src/client/util/RTFMarkup.tsx +++ b/src/client/util/RTFMarkup.tsx @@ -3,6 +3,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { MainViewModal } from '../views/MainViewModal'; import { SettingsManager } from './SettingsManager'; +import { Doc } from '../../fields/Doc'; +import { StrCast } from '../../fields/Types'; @observer export class RTFMarkup extends React.Component<{}> { @@ -133,6 +135,14 @@ export class RTFMarkup extends React.Component<{}> { } render() { - return <MainViewModal contents={this.cheatSheet} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} />; + return ( + <MainViewModal + dialogueBoxStyle={{ backgroundColor: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor), padding: '16px' }} + contents={this.cheatSheet} + isDisplayed={this.isOpen} + interactive={true} + closeOnExternalClick={this.close} + /> + ); } } diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index daa2c152a..b53379435 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -6,12 +6,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { UndoManager } from '../util/UndoManager'; import { Doc } from '../../fields/Doc'; import { StrCast } from '../../fields/Types'; +import { SettingsManager } from '../util/SettingsManager'; export interface OriginalMenuProps { description: string; event: (stuff?: any) => void; undoable?: boolean; - icon: IconProp; //maybe should be optional (icon?) + icon: IconProp | JSX.Element; //maybe should be optional (icon?) closeMenu?: () => void; } @@ -82,19 +83,20 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select ); }; + isJSXElement(val: any): val is JSX.Element { + return React.isValidElement(val); + } + render() { if ('event' in this.props) { return ( <div className={'contextMenu-item' + (this.props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent}> - {this.props.icon ? ( - <span className="contextMenu-item-icon-background"> - <FontAwesomeIcon icon={this.props.icon} size="sm" /> - </span> - ) : null} + {this.props.icon ? <span className="contextMenu-item-icon-background">{this.isJSXElement(this.props.icon) ? this.props.icon : <FontAwesomeIcon icon={this.props.icon} size="sm" />}</span> : null} <div className="contextMenu-description">{this.props.description.replace(':', '')}</div> - <div className={`contextMenu-item-background`} + <div + className={`contextMenu-item-background`} style={{ - background: StrCast(Doc.UserDoc().userColor) + background: SettingsManager.Instance.userColor, }} /> </div> @@ -110,7 +112,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select style={{ marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? '90%' : '20%', marginTop, - background: StrCast(Doc.UserDoc().userBackgroundColor) + background: SettingsManager.Instance.userBackgroundColor, }}> {this._items.map(prop => ( <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} /> @@ -141,9 +143,10 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select {this.props.description} <FontAwesomeIcon icon={'angle-right'} size="lg" style={{ position: 'absolute', right: '10px' }} /> </div> - <div className={`contextMenu-item-background`} + <div + className={`contextMenu-item-background`} style={{ - background: StrCast(Doc.UserDoc().userColor) + background: SettingsManager.Instance.userColor, }} /> {submenu} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a785ffd42..8565941fd 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -447,6 +447,7 @@ export class MainView extends React.Component { fa.faSortUp, fa.faSortDown, fa.faTable, + fa.faTableColumns, fa.faTh, fa.faThList, fa.faProjectDiagram, @@ -1019,7 +1020,7 @@ export class MainView extends React.Component { <OverlayView /> <GPTPopup key="gptpopup" /> <GenerativeFill imageEditorOpen={ImageBox.imageEditorOpen} imageEditorSource={ImageBox.imageEditorSource} imageRootDoc={ImageBox.imageRootDoc} addDoc={ImageBox.addDoc} /> - {/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */} + {/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */} </div> ); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 533a047b1..b72864567 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -748,7 +748,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps event: undoBatch(action(() => (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))), icon: 'hand-point-up', }); - !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'compass' }); + !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'layer-group' }); } onClicks.push({ description: 'Enter Portal', event: this.makeIntoPortal, icon: 'window-restore' }); @@ -806,7 +806,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' }); } } - constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' }); + constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'table-columns' }); cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' }); const help = cm.findByDescription('Help...'); diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 225df555b..d132707fa 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -7,6 +7,7 @@ import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { colorMapping } from '../../../../server/DashSession/Session/utilities/session_config'; import { Utils } from '../../../../Utils'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; @@ -14,7 +15,6 @@ import { undoable, UndoManager } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { DocComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; -import { Colors } from '../../global/globalEnums'; import { SelectedDocView } from '../../selectedDoc'; import { StyleProp } from '../../StyleProvider'; import { OpenWhere } from '../DocumentView'; @@ -93,7 +93,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { else return null; } icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any; - return !icon ? null : icon === 'pres-trail' ? <TrailsIcon /> : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />; + return !icon ? null : icon === 'pres-trail' ? TrailsIcon(color) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />; }; @computed get dropdown() { return BoolCast(this.rootDoc.dropDownOpen); diff --git a/src/client/views/nodes/FontIconBox/TrailsIcon.tsx b/src/client/views/nodes/FontIconBox/TrailsIcon.tsx index 99063b4a0..09fd6e3ae 100644 --- a/src/client/views/nodes/FontIconBox/TrailsIcon.tsx +++ b/src/client/views/nodes/FontIconBox/TrailsIcon.tsx @@ -1,10 +1,8 @@ import * as React from 'react'; -import { StrCast } from '../../../../fields/Types'; -import { Doc } from '../../../../fields/Doc'; -const TrailsIcon = () => ( +const TrailsIcon = (fill: string) => ( <svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 1080.000000 1080.000000" preserveAspectRatio="xMidYMid meet"> - <g transform="translate(0.000000,1080.000000) scale(0.100000,-0.100000)" fill={StrCast(Doc.UserDoc().userColor)} stroke="none"> + <g transform="translate(0.000000,1080.000000) scale(0.100000,-0.100000)" fill={fill} stroke="none"> <path d="M665 9253 c-74 -10 -157 -38 -240 -81 -74 -37 -107 -63 -186 -141 -104 -104 -156 -191 -201 -334 l-23 -72 0 -3215 c0 -3072 1 -3218 18 -3280 10 diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 2c8e97512..f5c6a9273 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -59,8 +59,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp @observable public static imageEditorOpen: boolean = false; @observable public static imageEditorSource: string = ''; @observable public static addDoc: ((doc: Doc | Doc[], annotationKey?: string) => boolean) | undefined; - @action public static setImageEditorOpen(open: boolean) {ImageBox.imageEditorOpen = open;} - @action public static setImageEditorSource(source: string) {ImageBox.imageEditorSource = source;} + @action public static setImageEditorOpen(open: boolean) { + ImageBox.imageEditorOpen = open; + } + @action public static setImageEditorSource(source: string) { + ImageBox.imageEditorSource = source; + } private _ignoreScroll = false; private _forcedScroll = false; private _dropDisposer?: DragManager.DragDropDisposer; @@ -307,7 +311,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp } choosePath(url: URL) { - if (!url?.href) return ""; + if (!url?.href) return ''; const lower = url.href.toLowerCase(); if (url.protocol === 'data') return url.href; if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf('dashblobstore') === -1) return Utils.CorsProxy(url.href); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index da277826a..200d06a0b 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -71,6 +71,7 @@ import { SummaryView } from './SummaryView'; import applyDevTools = require('prosemirror-dev-tools'); import React = require('react'); import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup'; +import { BsMarkdownFill } from 'react-icons/bs'; const translateGoogleApi = require('translate-google-api'); export const GoogleRef = 'googleDocId'; type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void; @@ -899,7 +900,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: this.Document._layout_autoHeight ? 'lock' : 'unlock', }); - optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: 'text' }); + optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: <BsMarkdownFill /> }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); this._downX = this._downY = Number.NaN; }; @@ -930,7 +931,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps }); generateImage = async () => { - console.log('Generate image from text: ', (this.dataDoc.text as RichTextField)?.Text); GPTPopup.Instance?.setTextAnchor(this.getAnchor(false)); GPTPopup.Instance?.setImgTargetDoc(this.rootDoc); GPTPopup.Instance.addToCollection = this.props.addDocument; diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 9c03600cf..1400d0f6d 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -166,20 +166,27 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // first load useEffect(() => { - if (!imageEditorSource || imageEditorSource === '') return; - const img = new Image(); - img.src = imageEditorSource; - currImg.current = img; - originalImg.current = img; - img.onload = () => { - const imgWidth = img.naturalWidth; - const imgHeight = img.naturalHeight; - const scale = Math.min(canvasSize / imgWidth, canvasSize / imgHeight); - const width = imgWidth * scale; - const height = imgHeight * scale; - setCanvasDims({ width, height }); + const loadInitial = async () => { + if (!imageEditorSource || imageEditorSource === '') return; + const img = new Image(); + const res = await ImageUtility.urlToBase64(imageEditorSource); + if (!res) return; + img.src = `data:image/png;base64,${res}`; + + img.onload = () => { + currImg.current = img; + originalImg.current = img; + const imgWidth = img.naturalWidth; + const imgHeight = img.naturalHeight; + const scale = Math.min(canvasSize / imgWidth, canvasSize / imgHeight); + const width = imgWidth * scale; + const height = imgHeight * scale; + setCanvasDims({ width, height }); + }; }; + loadInitial(); + // cleanup return () => { setInput(''); diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx index 0dfcebea3..10eca358e 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx @@ -2,7 +2,8 @@ import './GenerativeFillButtons.scss'; import React = require('react'); import ReactLoading from 'react-loading'; import { activeColor } from './generativeFillUtils/generativeFillConstants'; -import { Button, Type } from 'browndash-components'; +import { Button, IconButton, Type } from 'browndash-components'; +import { AiOutlineInfo } from 'react-icons/ai'; interface ButtonContainerProps { getEdit: () => Promise<void>; @@ -35,6 +36,7 @@ const Buttons = ({ loading, getEdit, onReset }: ButtonContainerProps) => { }} /> )} + <IconButton type={Type.SEC} color={activeColor} tooltip="Open Documentation" icon={<AiOutlineInfo size={'16px'} />} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/generativeai/#editing', '_blank')} /> </div> ); }; diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts index 2ede625f6..47a14135f 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/ImageHandler.ts @@ -283,4 +283,32 @@ export class ImageUtility { } return canvas; }; + + /** + * Converts a url to base64 (tainted canvas workaround) + */ + static urlToBase64 = async (imageUrl: string): Promise<string | undefined> => { + try { + const res = await fetch(imageUrl); + const blob = await res.blob(); + + return new Promise<string>((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + const base64Data = reader.result?.toString().split(',')[1]; + if (base64Data) { + resolve(base64Data); + } else { + reject(new Error('Failed to convert.')); + } + }; + reader.onerror = () => { + reject(new Error('Error reading image data')); + }; + reader.readAsDataURL(blob); + }); + } catch (err) { + console.error(err); + } + }; } |