diff options
| author | bobzel <zzzman@gmail.com> | 2024-03-19 17:08:46 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2024-03-19 17:08:46 -0400 |
| commit | a974aa4e6573c8becf93f78610406747fec14c1c (patch) | |
| tree | 73d90441a811d48b7a701273522d5cfabcb9159f /src/client/views/nodes/formattedText/RichTextRules.ts | |
| parent | 7d31c773ab85697b8a5eeecc752b567c8235efec (diff) | |
cleaned up user templates to not get changed on reload. made setting a template add it to the template tools list and as a tools button. fixed linking to parts of a template. fixed disappearing templates caused by stacking view set a field with an empty key. updated field assignment syntax in trees, dash field views, and key value box to all use :,:=,=,=:= syntax. added text elide button. added @(title) syntax for hyperlinking. made using a template both inherit from the template to get default values and use the template to render. fixed submenu placement of context menu. updated RTF markdown doc.
Diffstat (limited to 'src/client/views/nodes/formattedText/RichTextRules.ts')
| -rw-r--r-- | src/client/views/nodes/formattedText/RichTextRules.ts | 97 |
1 files changed, 54 insertions, 43 deletions
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index c798ae4b3..b97141e92 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -267,7 +267,7 @@ export class RichTextRules { // toggle alternate text UI %/ new InputRule(new RegExp(/%\//), (state, match, start, end) => { - setTimeout(this.TextBox.cycleAlternateText); + setTimeout(() => this.TextBox.cycleAlternateText(true)); return state.tr.deleteRange(start, end); }), @@ -283,40 +283,26 @@ export class RichTextRules { : tr; }), - new InputRule(new RegExp(/(^|[^=])(\(\(.*\)\))/), (state, match, start, end) => { - var count = 0; // ignore first return value which will be the notation that chat is pending a result - KeyValueBox.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => { - count && this.TextBox.EditorView?.dispatch(this.TextBox.EditorView!.state.tr.insertText(' ' + (gptval as string))); - count++; - }); - return null; - }), - - // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document - // [[<fieldKey> : <Doc>]] - // [[:docTitle]] => hyperlink - // [[fieldKey]] => show field - // [[fieldKey{:,=:}=value]] => show field and also set its value - // [[fieldKey:docTitle]] => show field of doc - new InputRule( - new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)((=:|:)?=)([a-z,A-Z_@\?+\-*/\ 0-9\(\)]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), - (state, match, start, end) => { - const fieldKey = match[1]; - const assign = match[2] === '=' ? '' : match[2]; - const value = match[4]; - const docTitle = match[5]?.replace(':', ''); + // create a hyperlink to a titled document + // @(<doctitle>) + new InputRule(new RegExp(/(^|\s)@\(([a-zA-Z_@:\.\? \-0-9]+)\)/), (state, match, start, end) => { + const docTitle = match[2]; + const prefixLength = '@('.length; + if (docTitle) { const linkToDoc = (target: Doc) => { - const rstate = this.TextBox.EditorView?.state; - const selection = rstate?.selection.$from.pos; - if (rstate) { - this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3)))); + const editor = this.TextBox.EditorView; + const selection = editor?.state?.selection.$from.pos; + if (editor) { + const estate = editor.state; + editor.dispatch(estate.tr.setSelection(new TextSelection(estate.doc.resolve(start), estate.doc.resolve(end - prefixLength)))); } DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' }); - const fstate = this.TextBox.EditorView?.state; - if (fstate && selection) { - this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection)))); + const teditor = this.TextBox.EditorView; + if (teditor && selection) { + const tstate = teditor.state; + teditor.dispatch(tstate.tr.setSelection(new TextSelection(tstate.doc.resolve(selection)))); } }; const getTitledDoc = (docTitle: string) => { @@ -326,32 +312,57 @@ export class RichTextRules { const titledDoc = DocServer.FindDocByTitle(docTitle); return titledDoc ? Doc.BestEmbedding(titledDoc) : titledDoc; }; - if (!fieldKey) { - if (docTitle) { - const target = getTitledDoc(docTitle); - if (target) { - setTimeout(() => linkToDoc(target)); - return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3); - } - } - return state.tr; + const target = getTitledDoc(docTitle); + if (target) { + setTimeout(() => linkToDoc(target)); + return state.tr.insertText(' ').deleteRange(start, start + prefixLength); } + } + return state.tr; + }), + + // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document + // [@{this,doctitle,}.fieldKey{:,=,:=,=:=}value] + // [@{this,doctitle,}.fieldKey] + new InputRule( + new RegExp(/\[(@|@this\.|@[a-zA-Z_\? \-0-9]+\.)([a-zA-Z_\?\-0-9]+)((:|=|:=|=:=)([a-zA-Z,_@\?\+\-\*\/\ 0-9\(\)]*))?\]/), + (state, match, start, end) => { + const docTitle = match[1].substring(1).replace(/\.$/, ''); + const fieldKey = match[2]; + const assign = match[4] === ':' ? (match[4] = '') : match[4]; + const value = match[5]; + const dataDoc = value === undefined ? !fieldKey.startsWith('_') : !assign?.startsWith('='); + const getTitledDoc = (docTitle: string) => { + if (!DocServer.FindDocByTitle(docTitle)) { + Doc.AddToMyPublished(Docs.Create.TextDocument('', { title: docTitle, _width: 400, _layout_autoHeight: true })); + } + return DocServer.FindDocByTitle(docTitle); + }; // if the value has commas assume its an array (unless it's part of a chat gpt call indicated by '((' ) if (value?.includes(',') && !value.startsWith('((')) { const values = value.split(','); const strs = values.some(v => !v.match(/^[-]?[0-9.]$/)); this.Document[DocData][fieldKey] = strs ? new List<string>(values) : new List<number>(values.map(v => Number(v))); } else if (value) { - KeyValueBox.SetField(this.Document, fieldKey, assign + value, Doc.IsDataProto(this.Document) ? true : undefined, assign ? undefined: - (gptval: FieldResult) => this.Document[DocData][fieldKey] = gptval as string ); // prettier-ignore + KeyValueBox.SetField(this.Document, fieldKey, assign + value, Doc.IsDataProto(this.Document) ? true : undefined, assign.includes(":=") ? undefined: + (gptval: FieldResult) => (dataDoc ? this.Document[DocData]:this.Document)[fieldKey] = gptval as string ); // prettier-ignore } - const target = getTitledDoc(docTitle); - const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false }); + const target = docTitle ? getTitledDoc(docTitle) : undefined; + const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false, dataDoc }); return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true); }, { inCode: true } ), + new InputRule(new RegExp(/(^|[^=])(\(\(.*\)\))/), (state, match, start, end) => { + var count = 0; // ignore first return value which will be the notation that chat is pending a result + KeyValueBox.SetField(this.Document, '', match[2], false, (gptval: FieldResult) => { + count && this.TextBox.EditorView?.dispatch(this.TextBox.EditorView!.state.tr.insertText(' ' + (gptval as string))); + count++; + }); + return null; + }), + // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document // wiki:title new InputRule(new RegExp(/wiki:([a-zA-Z_@:\.\?\-0-9]+ )$/), (state, match, start, end) => { |
