diff options
author | bobzel <zzzman@gmail.com> | 2024-09-17 22:11:22 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-09-17 22:11:22 -0400 |
commit | 4e0ea65c08ff07429cb32f3907268206c80889fa (patch) | |
tree | feb04e318ba52bb41b22f3cb6f94f4b21c35b5a3 | |
parent | 31321f6def982305ea89cdfe5a873b942060eb84 (diff) |
linted cardView files after merge.
-rw-r--r-- | src/client/apis/gpt/GPT.ts | 16 | ||||
-rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 51 | ||||
-rw-r--r-- | src/client/views/FilterPanel.tsx | 352 | ||||
-rw-r--r-- | src/client/views/StyleProvider.tsx | 10 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 4 |
5 files changed, 208 insertions, 225 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 2657bf12e..5be9d84ff 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -1,4 +1,4 @@ -import { ChatCompletionMessageParam } from 'openai/resources'; +import { ChatCompletionMessageParam, Image } from 'openai/resources'; import { openai } from './setup'; enum GPTCallType { @@ -15,7 +15,7 @@ enum GPTCallType { RUBRIC = 'rubric', TYPE = 'type', SUBSET = 'subset', - INFO = 'info' + INFO = 'info', } type GPTCallOpts = { @@ -48,7 +48,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { model: 'gpt-4o', maxTokens: 2048, temp: 0.25, - prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas" + prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas", }, describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' }, chatcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Answer the following question as a short flashcard response. Do not include a label.' }, @@ -77,17 +77,15 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { model: 'gpt-4-turbo', maxTokens: 1024, temp: 0, - prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Based on the question the user asks, provide a subset of the given descriptions that best matches the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning in the 2nd person (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and no commas" + prompt: "I'm going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Based on the question the user asks, provide a subset of the given descriptions that best matches the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning in the 2nd person (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and no commas", }, info: { model: 'gpt-4-turbo', maxTokens: 1024, temp: 0, - prompt: "Answer the user's question with a short (<100 word) response. If a particular document is selected I will provide that information (which may help with your response)" + prompt: "Answer the user's question with a short (<100 word) response. If a particular document is selected I will provide that information (which may help with your response)", }, - - }; let lastCall = ''; @@ -98,7 +96,7 @@ let lastResp = ''; * @param inputText Text to process * @returns AI Output */ -const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: any) => { +const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: string) => { const inputText = [GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ].includes(callType) ? inputTextIn + '.' : inputTextIn; const opts: GPTCallOpts = callTypeMap[callType]; if (lastCall === inputText) return lastResp; @@ -131,7 +129,7 @@ const gptImageCall = async (prompt: string, n?: number) => { n: n ?? 1, size: '1024x1024', }); - return response.data.map((data: any) => data.url); + return response.data.map((data: Image) => data.url); // return response.data.data[0].url; } catch (err) { console.error(err); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index ccde1de80..87dd5f45a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -298,16 +298,12 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( key={icon.toString()} size="sm" icon={icon} - onClick={e => { - if (name === 'tags'){ - (targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags)) + onClick={() => { + if (name === 'tags') { + targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags); } else { - (targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels)) + targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels); } - - - - }} /> </div> @@ -315,31 +311,24 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( ); }; - - - return !targetDoc ? null : ( - <div className='documentButtonBar-icon'> - <div className="documentButtonBar-pinTypes" style = {{width: '40px'}}> - {metaBtn('tags', 'star')} - {metaBtn("keywords", 'id-card')} - </div> - - <Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}> - <div - className="documentButtonBar-icon" - style={{ color: 'white' }} - onClick={() => { - // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags; - }} - > - - - <FontAwesomeIcon className="documentdecorations-icon" icon="tag" /> + <div className="documentButtonBar-icon"> + <div className="documentButtonBar-pinTypes" style={{ width: '40px' }}> + {metaBtn('tags', 'star')} + {metaBtn('keywords', 'id-card')} </div> - </Tooltip> - </div> + <Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}> + <div + className="documentButtonBar-icon" + style={{ color: 'white' }} + onClick={() => { + // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags; + }}> + <FontAwesomeIcon className="documentdecorations-icon" icon="tag" /> + </div> + </Tooltip> + </div> ); } @@ -519,4 +508,4 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( </div> ); } -}
\ No newline at end of file +} diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 8133a4d0d..3795a4a6d 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { action, computed, makeObservable, observable, ObservableMap } from 'mobx'; @@ -24,14 +23,186 @@ import { DocumentView } from './nodes/DocumentView'; import { ButtonType } from './nodes/FontIconBox/FontIconBox'; import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +interface HotKeyButtonProps { + hotKey: string; + selected?: Doc; +} + +/** + * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon, + * title, and delete. + */ +const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, selected */ }) => { + const state = useLocalObservable(() => ({ + isActive: false, + isEditing: false, + myHotKey: hotKey, + + toggleActive() { + this.isActive = !this.isActive; + }, + deactivate() { + this.isActive = false; + }, + startEditing() { + this.isEditing = true; + }, + stopEditing() { + this.isEditing = false; + }, + setHotKey(newHotKey: string) { + this.myHotKey = newHotKey; + }, + })); + + const panelRef = useRef<HTMLDivElement>(null); + const inputRef = useRef<HTMLInputElement>(null); + + const handleClick = () => { + state.toggleActive(); + }; + + const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); + const buttons = DocCast(Doc.UserDoc().myContextMenuBtns); + const filter = DocCast(buttons.Filter); + + /** + * The doc of the button in the context menu that corresponds to the current hotkey + * @returns + */ + const myHotKeyDoc = () => { + const hotKeyDocs = DocListCast(filter.data); + return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0]; + }; + + /** + * Removes a hotkey from list + */ + const removeHotKey = () => { + Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc()); + }; + + /** + * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new + * hotkey with the current icon + */ + const updateFromInput = undoable(() => { + const myDoc = myHotKeyDoc(); + Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => (k === hotKey ? state.myHotKey : k))); + Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]); + Doc.UserDoc()[hotKey] = ''; + myDoc.title = state.myHotKey; + myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`; + }, ''); + + /** + * Deselects if the user clicks outside the button + * @param event + */ + const handleClickOutside = (event: MouseEvent) => { + if (panelRef.current && !panelRef.current.contains(event.target as Node)) { + state.deactivate(); + if (state.isEditing) { + state.stopEditing(); + + updateFromInput(); + } + } + }; + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard'] as IconProp[]; + + /** + * Panel of icons the user can choose from to represent their tag + */ + const iconPanel = iconOpts.map((icon, i) => ( + <button + key={i} + onClick={undoable((e: React.MouseEvent) => { + e.stopPropagation; + Doc.UserDoc()[hotKey] = icon.toString(); + myHotKeyDoc().icon = icon.toString(); + }, '')} + className="icon-panel-button"> + <FontAwesomeIcon icon={icon} color={SnappingManager.userColor} /> + </button> + )); + + /** + * Actually renders the buttons + */ + + return ( + <div + className={`filterHotKey-button`} + onClick={e => { + e.stopPropagation(); + state.startEditing(); + setTimeout(() => inputRef.current?.focus(), 0); + }}> + <div className={`hotKey-icon-button ${state.isActive ? 'active' : ''}`} ref={panelRef}> + <Tooltip title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> + <button + type="button" + className="hotKey-icon" + onClick={(e: React.MouseEvent) => { + e.stopPropagation(); + handleClick(); + }}> + <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as IconProp} size="2xl" color={SnappingManager.userColor} /> + </button> + </Tooltip> + {state.isActive && <div className="icon-panel">{iconPanel}</div>} + </div> + {state.isEditing ? ( + <input + ref={inputRef} + type="text" + value={state.myHotKey.toUpperCase()} + onChange={e => state.setHotKey(e.target.value)} + onBlur={() => { + state.stopEditing(); + updateFromInput(); + }} + onKeyDown={e => { + if (e.key === 'Enter') { + state.stopEditing(); + updateFromInput(); + } + }} + className="hotkey-title-input" + /> + ) : ( + <p className="hotkey-title">{hotKey.toUpperCase()}</p> + )} + <button + className="hotKey-close" + onClick={(e: React.MouseEvent) => { + e.stopPropagation(); + Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey)); + removeHotKey(); + }}> + <FontAwesomeIcon icon={'x' as IconProp} color={SnappingManager.userColor} /> + </button> + </div> + ); +}); interface filterProps { Document: Doc; } @observer export class FilterPanel extends ObservableReactComponent<filterProps> { - @observable _selectedFacetHeaders = new Set<string>(); + // eslint-disable-next-line no-use-before-define public static Instance: FilterPanel; constructor(props: filterProps) { @@ -40,6 +211,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { FilterPanel.Instance = this; } + @observable _selectedFacetHeaders = new Set<string>(); /** * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection */ @@ -263,8 +435,8 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); // Selecting a button should make it so that the icon on the top filter panel becomes said icon - const buttons = hotKeys.map((hotKey, i) => ( - <Tooltip key={hotKey} title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> + const buttons = hotKeys.map(hotKey => ( + <Tooltip key={hotKey} title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> <HotKeyIconButton hotKey={hotKey} selected={selected} /> </Tooltip> )); @@ -457,175 +629,3 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { return undefined; } } - -interface HotKeyButtonProps { - hotKey: string; - selected?: Doc; -} - -/** - * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon, - * title, and delete. - */ -const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, selected }) => { - const state = useLocalObservable(() => ({ - isActive: false, - isEditing: false, - myHotKey: hotKey, - - toggleActive() { - this.isActive = !this.isActive; - }, - deactivate() { - this.isActive = false; - }, - startEditing() { - this.isEditing = true; - }, - stopEditing() { - this.isEditing = false; - }, - setHotKey(newHotKey: string) { - this.myHotKey = newHotKey; - }, - })); - - const panelRef = useRef<HTMLDivElement>(null); - const inputRef = useRef<HTMLInputElement>(null); - - const handleClick = () => { - state.toggleActive(); - }; - - const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); - const buttons = DocCast(Doc.UserDoc().myContextMenuBtns); - const filter = DocCast(buttons.Filter); - - /** - * The doc of the button in the context menu that corresponds to the current hotkey - * @returns - */ - const myHotKeyDoc = () => { - const hotKeyDocs = DocListCast(filter.data); - return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0]; - }; - - /** - * Removes a hotkey from list - */ - const removeHotKey = () => { - Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc()); - }; - - /** - * Deselects if the user clicks outside the button - * @param event - */ - const handleClickOutside = (event: MouseEvent) => { - if (panelRef.current && !panelRef.current.contains(event.target as Node)) { - state.deactivate(); - if (state.isEditing) { - state.stopEditing(); - - updateFromInput(); - } - } - }; - - /** - * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new - * hotkey with the current icon - */ - const updateFromInput = undoable(() => { - const myDoc = myHotKeyDoc(); - Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => (k === hotKey ? state.myHotKey : k))); - Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]); - Doc.UserDoc()[hotKey] = ''; - myDoc.title = state.myHotKey; - myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`; - }, ''); - - useEffect(() => { - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, []); - - const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard']; - - /** - * Panel of icons the user can choose from to represent their tag - */ - const iconPanel = iconOpts.map((icon, i) => ( - <button - key={i} - onClick={undoable((e: React.MouseEvent) => { - e.stopPropagation; - Doc.UserDoc()[hotKey] = icon; - myHotKeyDoc().icon = icon; - }, '')} - className="icon-panel-button"> - <FontAwesomeIcon icon={icon as any} color={SnappingManager.userColor} /> - </button> - )); - - /** - * Actually renders the buttons - */ - - return ( - <div - className={`filterHotKey-button`} - onClick={e => { - e.stopPropagation(); - state.startEditing(); - setTimeout(() => inputRef.current?.focus(), 0); - }}> - <div className={`hotKey-icon-button ${state.isActive ? 'active' : ''}`} ref={panelRef}> - <Tooltip title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> - <button - type="button" - className="hotKey-icon" - onClick={(e: React.MouseEvent) => { - e.stopPropagation(); - handleClick(); - }}> - <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as any} size="2xl" color={SnappingManager.userColor} /> - </button> - </Tooltip> - {state.isActive && <div className="icon-panel">{iconPanel}</div>} - </div> - {state.isEditing ? ( - <input - ref={inputRef} - type="text" - value={state.myHotKey.toUpperCase()} - onChange={e => state.setHotKey(e.target.value)} - onBlur={() => { - state.stopEditing(); - updateFromInput(); - }} - onKeyDown={e => { - if (e.key === 'Enter') { - state.stopEditing(); - updateFromInput(); - } - }} - className="hotkey-title-input" - /> - ) : ( - <p className="hotkey-title">{hotKey.toUpperCase()}</p> - )} - <button - className="hotKey-close" - onClick={(e: React.MouseEvent) => { - e.stopPropagation(); - Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey)); - removeHotKey(); - }}> - <FontAwesomeIcon icon={'x' as any} color={SnappingManager.userColor} /> - </button> - </div> - ); -}); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 13fef101a..eb434db40 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -326,7 +326,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & type={Type.TERT} dropdownType={DropdownType.CLICK} fillWidth - // eslint-disable-next-line react/no-unstable-nested-components iconProvider={() => <div className='styleProvider-filterShift'><FaFilter/></div>} closeOnSelect setSelectedVal={((dvValue: unknown) => { @@ -366,12 +365,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & </Tooltip> ); }; - const tags = () => props?.DocumentView?.() ? <TagsView Views={[props.DocumentView()]}/> : null; - - const iconTags = () => { - if (doc && doc![DocData].showIconTags) - {return (<IconTagBox doc= {doc}></IconTagBox>)} - } + const tags = () => docView?.() ? <TagsView Views={[docView?.()]}/> : null; + const iconTags = () => doc?.[DocData].showIconTags ? <IconTagBox doc= {doc}/> : null; + return ( <> {paint()} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c269c7bcb..683edba16 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/no-unused-prop-types */ -/* eslint-disable react/require-default-props */ import { Property } from 'csstype'; import { computed } from 'mobx'; import { observer } from 'mobx-react'; @@ -21,6 +19,7 @@ export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number> // eslint-disable-next-line no-use-before-define export type StyleProviderFuncType = ( doc: Opt<Doc>, + // eslint-disable-next-line no-use-before-define props: Opt<FieldViewProps>, property: string ) => @@ -65,6 +64,7 @@ export interface FieldViewSharedProps { containerViewPath?: () => DocumentView[]; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document isGroupActive?: () => string | undefined; // is this document part of a group that is active + // eslint-disable-next-line no-use-before-define setContentViewBox?: (view: ViewBoxInterface<FieldViewProps>) => void; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox PanelWidth: () => number; PanelHeight: () => number; |