From 01b3d91232d944fc88d0591838823097b2bcba8a Mon Sep 17 00:00:00 2001 From: Petrinich Sergey Date: Tue, 21 Apr 2026 22:54:09 +0300 Subject: [PATCH 1/6] add copy-url-with-page to share/edit pages Adds a "copy url (page N)" option to the Share dropdown (edit page) and the source dropdown (share page). Copies a URL with a #pN hash anchor. The existing scrollToHash handler in brewRenderer already navigates to the correct page when the URL is opened. --- client/homebrew/navbar/share.navitem.jsx | 8 +++++++- client/homebrew/pages/editPage/editPage.jsx | 2 +- client/homebrew/pages/sharePage/sharePage.jsx | 13 +++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/client/homebrew/navbar/share.navitem.jsx b/client/homebrew/navbar/share.navitem.jsx index d0c659e2c..e329a4560 100644 --- a/client/homebrew/navbar/share.navitem.jsx +++ b/client/homebrew/navbar/share.navitem.jsx @@ -17,7 +17,7 @@ const getRedditLink = (brew)=>{ return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(brew.title.toWellFormed())}&text=${encodeURIComponent(text)}`; }; -export default ({ brew })=>( +export default ({ brew, currentPage })=>( share @@ -28,6 +28,12 @@ export default ({ brew })=>( {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${getShareId(brew)}`);}}> copy url + {currentPage > 1 && + {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${getShareId(brew)}#p${currentPage}`);}}> + copy url (page {currentPage}) + } post to reddit diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 176158e2c..b41f1bc74 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -365,7 +365,7 @@ const EditPage = (props)=>{ - + diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 093fc8965..7f22e6c86 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -92,6 +92,19 @@ const SharePage = (props)=>{ clone to new + {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${processShareId()}`);}}> + copy url + + {currentBrewRendererPageNum > 1 && + {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${processShareId()}#p${currentBrewRendererPageNum}`);}}> + copy url (page {currentBrewRendererPageNum}) + } )} From 899737b5583d4f2bcd7a81b0931f05b2cfc620cc Mon Sep 17 00:00:00 2001 From: Petrinich Sergey Date: Tue, 21 Apr 2026 23:01:24 +0300 Subject: [PATCH 2/6] use fa-hashtag icon for copy-url-with-page item --- client/homebrew/pages/sharePage/sharePage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 7f22e6c86..8df241d7b 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -101,7 +101,7 @@ const SharePage = (props)=>{ {currentBrewRendererPageNum > 1 && {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${processShareId()}#p${currentBrewRendererPageNum}`);}}> copy url (page {currentBrewRendererPageNum}) } From 1cd4a698540d302bc1d2597c30fcd5be54ad6db7 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 23 Apr 2026 13:00:32 -0400 Subject: [PATCH 3/6] Make common "wrapSelected" function --- client/components/codeEditor/customKeyMaps.js | 171 ++++-------------- 1 file changed, 37 insertions(+), 134 deletions(-) diff --git a/client/components/codeEditor/customKeyMaps.js b/client/components/codeEditor/customKeyMaps.js index 7da06b119..a05cbacd9 100644 --- a/client/components/codeEditor/customKeyMaps.js +++ b/client/components/codeEditor/customKeyMaps.js @@ -17,125 +17,28 @@ const indentLess = (view)=>{ return true; }; -const makeBold = (view)=>{ +const wrapSelection = (prefix, suffix) => (view) => { const { from, to } = view.state.selection.main; const selected = view.state.doc.sliceString(from, to); - let text, cursor; + let text, selection; if(from === to) { - text = '****'; - cursor = from + 2; - } else if(selected.startsWith('**') && selected.endsWith('**')) { - text = selected.slice(2, -2); - cursor = from + text.length; - } else { - text = `**${selected}**`; - cursor = from + text.length; + text = prefix + suffix; + selection = { anchor: from + prefix.length, head: from + prefix.length }; + } + else if(selected.startsWith(prefix) && selected.endsWith(suffix)) { + text = selected.slice(prefix.length, -suffix.length); + selection = { anchor: from, head: from + text.length }; + } + else { + text = `${prefix}${selected}${suffix}`; + selection = { anchor: from, head: from + text.length }; } view.dispatch({ changes : { from, to, insert: text }, - selection : { anchor: cursor }, - }); - - return true; -}; - -const makeItalic = (view)=>{ - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); - - let text, cursor; - - if(from === to) { - text = '**'; - cursor = from + 1; - } else if(selected.startsWith('*') && selected.endsWith('*')) { - text = selected.slice(2, -2); - cursor = from + text.length; - } else { - text = `*${selected}*`; - cursor = from + text.length; - } - - view.dispatch({ - changes : { from, to, insert: text }, - selection : { anchor: cursor }, - }); - - return true; -}; - -const makeUnderline = (view)=>{ - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); - - let text, cursor; - - if(from === to) { - text = ''; - cursor = from + 3; - } else if(selected.startsWith('') && selected.endsWith('')) { - text = selected.slice(3, -4); - cursor = from + text.length; - } else { - text = `${selected}`; - cursor = from + text.length; - } - - view.dispatch({ - changes : { from, to, insert: text }, - selection : { anchor: cursor }, - }); - - return true; -}; -const makeSuper = (view)=>{ - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); - - let text, cursor; - - if(from === to) { - text = '^^'; - cursor = from + 1; - } else if(selected.startsWith('^') && selected.endsWith('^')) { - text = selected.slice(1, -1); - cursor = from + text.length; - } else { - text = `^${selected}^`; - cursor = from + text.length; - } - - view.dispatch({ - changes : { from, to, insert: text }, - selection : { anchor: cursor }, - }); - - return true; -}; - -const makeSub = (view)=>{ - const { from, to } = view.state.selection.main; - const selected = view.state.doc.sliceString(from, to); - - let text, cursor; - - if(from === to) { - text = '^^^^'; - cursor = from + 2; - } else if(selected.startsWith('^^') && selected.endsWith('^^')) { - text = selected.slice(2, -2); - cursor = from + text.length; - } else { - text = `^^${selected}^^`; - cursor = from + text.length; - } - - view.dispatch({ - changes : { from, to, insert: text }, - selection : { anchor: cursor }, + selection }); return true; @@ -151,8 +54,8 @@ const makeNbsp = (view) => { const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>'; view.dispatch({ - changes: { from, to: from, insert }, - selection: { anchor: from + insert.length }, + changes : { from, to: from, insert }, + selection : { anchor: from + insert.length }, }); return true; @@ -268,27 +171,27 @@ export const generalKeymap = Prec.high(keymap.of([ export const markdownKeymap = Prec.highest(keymap.of([ //{ key: 'Shift-Tab', run: indentMore }, - { key: 'Shift-Tab', run: indentLess }, - { key: 'Mod-b', run: makeBold }, - { key: 'Mod-i', run: makeItalic }, - { key: 'Mod-u', run: makeUnderline }, - { key: 'Shift-Mod-=', run: makeSuper }, - { key: 'Mod-=', run: makeSub }, - { key: 'Mod-.', run: makeNbsp }, - { key: 'Shift-Mod-.', run: makeSpace }, - { key: 'Shift-Mod-,', run: removeSpace }, - { key: 'Mod-m', run: makeSpan }, - { key: 'Shift-Mod-m', run: makeDiv }, - { key: 'Mod-/', run: makeComment }, - { key: 'Mod-k', run: makeLink }, - { key: 'Mod-l', run: makeList('UL') }, - { key: 'Shift-Mod-l', run: makeList('OL') }, - { key: 'Shift-Mod-1', run: makeHeader(1) }, - { key: 'Shift-Mod-2', run: makeHeader(2) }, - { key: 'Shift-Mod-3', run: makeHeader(3) }, - { key: 'Shift-Mod-4', run: makeHeader(4) }, - { key: 'Shift-Mod-5', run: makeHeader(5) }, - { key: 'Shift-Mod-6', run: makeHeader(6) }, + { key: 'Shift-Tab', run: indentLess }, + { key: 'Mod-b', run: wrapSelection('**', '**') }, // makeBold + { key: 'Mod-i', run: wrapSelection('*', '*') }, // makeItalic + { key: 'Mod-u', run: wrapSelection('', '') }, // makeUnderline + { key: 'Shift-Mod-=', run: wrapSelection('^', '^') }, // makeSuper + { key: 'Mod-=', run: wrapSelection('^^', '^^') }, // makeSub + { key: 'Mod-.', run: makeNbsp }, + { key: 'Shift-Mod-.', run: makeSpace }, + { key: 'Shift-Mod-,', run: removeSpace }, + { key: 'Mod-m', run: makeSpan }, + { key: 'Shift-Mod-m', run: makeDiv }, + { key: 'Mod-/', run: makeComment }, + { key: 'Mod-k', run: makeLink }, + { key: 'Mod-l', run: makeList('UL') }, + { key: 'Shift-Mod-l', run: makeList('OL') }, + { key: 'Shift-Mod-1', run: makeHeader(1) }, + { key: 'Shift-Mod-2', run: makeHeader(2) }, + { key: 'Shift-Mod-3', run: makeHeader(3) }, + { key: 'Shift-Mod-4', run: makeHeader(4) }, + { key: 'Shift-Mod-5', run: makeHeader(5) }, + { key: 'Shift-Mod-6', run: makeHeader(6) }, + { key: 'Mod-Enter', run: newPage }, { key: 'Shift-Mod-Enter', run: newColumn }, - { key: 'Mod-Enter', run: newPage }, ])); From 4fee82d3b73d717c1d5b7cb72f0863552326815c Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 23 Apr 2026 14:36:55 -0400 Subject: [PATCH 4/6] Simplify regex, fix crash on empty desc.length, only highlight if there is a Term line with content --- client/components/codeEditor/customHighlight.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/components/codeEditor/customHighlight.js b/client/components/codeEditor/customHighlight.js index 345e253d3..8797704cc 100644 --- a/client/components/codeEditor/customHighlight.js +++ b/client/components/codeEditor/customHighlight.js @@ -137,14 +137,14 @@ export function tokenizeCustomMarkdown(text) { for (let i = lineNumber + 1; i < lines.length; i++) { const nextLine = lines[i]; const onlyColonsMatch = /^:*$/.test(nextLine); - const defMatch = /^(::)(.*\S.*)?\s*$/.exec(nextLine); + const defMatch = /^(::)(.+)$/.exec(nextLine); if(!onlyColonsMatch && defMatch) { defs.push({ colons: defMatch[1], desc: defMatch[2], line: i }); endLine = i; } else break; } - if(defs.length > 0) { + if(defs.length > 0 && lineText.trim().length > 0) { tokens.push({ line : startLine, type : customTags.definitionList, @@ -175,7 +175,7 @@ export function tokenizeCustomMarkdown(text) { line : d.line, type : customTags.definitionDesc, from : d.colons.length, - to : d.colons.length + d.desc.length, + to : d.colons.length + d.desc?.length, }); }); } From cadf9ca63bdc0d8e3b26ccd678fe414b1fd9751f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Fri, 24 Apr 2026 01:05:29 +0200 Subject: [PATCH 5/6] update codemirror themes --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 565fe43bb..f7657daf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.4.0", + "codemirror-5-themes": "^1.5.1", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", @@ -6094,9 +6094,9 @@ } }, "node_modules/codemirror-5-themes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.4.0.tgz", - "integrity": "sha512-3WKAmpTLqcE1MXFLHdNtwQYaxlWZXS69MY79UiNsFpW9KedMkf/vBYBjUAmacDXPKAJjdPSNMGmCPIZi21yoXA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.5.1.tgz", + "integrity": "sha512-vHUyvOYy8yhqCDkps5LwoUFXHXIuKjE32n6EPP5v004fXmkZkrAC9mUsr+anNyOniaixE1W+xwcM4lgnbYfWsQ==", "dependencies": { "@codemirror/view": "^6.41.1" } diff --git a/package.json b/package.json index b25885497..6989c444f 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.4.0", + "codemirror-5-themes": "^1.5.1", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", From 00efbb7244470ea06deae2bf16638b17e1ba476a Mon Sep 17 00:00:00 2001 From: Gazook89 Date: Sun, 26 Apr 2026 21:09:18 -0500 Subject: [PATCH 6/6] fix state variable `themeSelector` --- client/homebrew/editor/snippetbar/snippetbar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 07a20fa08..597a01045 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -170,7 +170,7 @@ const Snippetbar = createReactClass({ this.props.updateEditorTheme(e.target.value); this.setState({ - showThemeSelector : false, + themeSelector : false, }); },