// Shared utilities and constants used across all template modules 'use strict'; const { validDateFormats, validDatetimeFormats } = require('../settingsService'); const { renderMarkdown: renderMarkdownImpl } = require('../markdownRenderer'); const decodeTitleEntities = value => `${value || ''}` .replaceAll(' ', ' ') .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll(''', "'"); const escapeHtml = value => `${value}` .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll('\'', '''); const appleSplashLinks = [ ['1320x2868.png', 'screen and (device-width: 440px) and (device-height: 956px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)'], ['2868x1320.png', 'screen and (device-width: 440px) and (device-height: 956px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)'], ['1290x2796.png', 'screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)'], ['2796x1290.png', 'screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)'], ['1179x2556.png', 'screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)'], ['2556x1179.png', 'screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)'], ['1170x2532.png', 'screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)'], ['2532x1170.png', 'screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)'], ['1125x2436.png', 'screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)'], ['2436x1125.png', 'screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)'], ['1242x2688.png', 'screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)'], ['2688x1242.png', 'screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)'], ['828x1792.png', 'screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)'], ['1792x828.png', 'screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)'], ['1536x2048.png', 'screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)'], ['2048x1536.png', 'screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)'], ['1668x2388.png', 'screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)'], ['2388x1668.png', 'screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)'], ['1640x2360.png', 'screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)'], ['2360x1640.png', 'screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)'], ['2048x2732.png', 'screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)'], ['2732x2048.png', 'screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)'], ].map(([fileName, media]) => ``).join('\n\t'); const folderOutlineIcon = ''; const allNotesIcon = '📄'; const trashFolderId = 'de1e7ede1e7ede1e7ede1e7ede1e7ede'; const themeOptions = [['matrix','Matrix'],['matrix-blue','Dark Blue'],['matrix-purple','Dark Purple'],['matrix-amber','Dark Amber'],['matrix-orange','Dark Orange'],['dark-grey','Dark Grey'],['dark-red','Dark Red'],['dark','Dark'],['light','Light'],['oled-dark','OLED Dark'],['solarized-light','Solarized Light'],['solarized-dark','Solarized Dark'],['nord','Nord'],['dracula','Dracula'],['aritim-dark','Aritim Dark']]; const stripMarkdownForTitle = value => { let text = decodeTitleEntities(value).trim(); text = text.replace(/]*>[\s\S]*?<\/system-reminder>/gi, ' '); text = text.replace(/<[^>]+>/g, ' '); while (text.startsWith('#')) text = text.slice(1).trimStart(); text = text .replaceAll('**', '') .replaceAll('__', '') .replaceAll('++', '') .replaceAll('*', '') .replaceAll('_', '') .replaceAll('~~', '') .replaceAll('`', ''); let output = ''; for (let i = 0; i < text.length; i += 1) { const ch = text[i]; if (ch === '!' && text[i + 1] === '[') { const altEnd = text.indexOf(']', i + 2); const imgOpen = altEnd >= 0 ? text.indexOf('(', altEnd + 1) : -1; const imgClose = imgOpen >= 0 ? text.indexOf(')', imgOpen + 1) : -1; if (altEnd >= 0 && imgOpen === altEnd + 1 && imgClose >= 0) { output += text.slice(i + 2, altEnd); i = imgClose; continue; } } if (ch === '[') { const labelEnd = text.indexOf(']', i + 1); const linkOpen = labelEnd >= 0 ? text.indexOf('(', labelEnd + 1) : -1; const linkClose = linkOpen >= 0 ? text.indexOf(')', linkOpen + 1) : -1; if (labelEnd >= 0 && linkOpen === labelEnd + 1 && linkClose >= 0) { output += text.slice(i + 1, labelEnd); i = linkClose; continue; } } output += ch; } return output.replace(/\s+/g, ' ').trim(); }; // Render only inline markdown (bold, italic, strikethrough, inline code) — no block elements const renderInlineMarkdown = (text) => { if (!text) return ''; let html = text; html = html.replace(/\*\*(.+?)\*\*/g, '$1'); html = html.replace(/\*(.+?)\*/g, '$1'); html = html.replace(/~~(.+?)~~/g, '$1'); html = html.replace(/\+\+(.+?)\+\+/g, '$1'); html = html.replace(/`(.+?)`/g, '$1'); return html; }; // renderMarkdown delegates to app/markdownRenderer.js (markdown-it engine). // Old regex implementation removed; markdownRenderer.js is the source of truth. const renderMarkdown = renderMarkdownImpl; // Renders a password input + eye-toggle button pair. // name — the input's name attribute // opts — { placeholder, id, autocomplete, required, extraClass } // The eye button uses parentNode.querySelector('input') so it works regardless of id. // Special case: if opts.id is set, the eye button on the login page uses getElementById // because the input is not a direct sibling but shares a wrapper. const passwordField = (name, opts = {}) => { const { placeholder = '', id = '', autocomplete = '', required = true, extraClass = '' } = opts; const idAttr = id ? ` id="${escapeHtml(id)}"` : ''; const autocompleteAttr = autocomplete ? ` autocomplete="${escapeHtml(autocomplete)}"` : ''; const requiredAttr = required ? ' required' : ''; const classes = ['login-input', extraClass].filter(Boolean).join(' '); const eyeTarget = id ? `var p=document.getElementById('${escapeHtml(id)}')` : `var p=this.parentNode.querySelector('input')`; return `` + ``; }; const svgLockClosed = ''; const svgLockOpen = ''; module.exports = { escapeHtml, appleSplashLinks, folderOutlineIcon, allNotesIcon, trashFolderId, themeOptions, validDateFormats, validDatetimeFormats, stripMarkdownForTitle, renderInlineMarkdown, renderMarkdown, passwordField, svgLockClosed, svgLockOpen, };