From 444b51794a89f80136674e0c6609936bb64b9b59 Mon Sep 17 00:00:00 2001 From: mikael-lovqvists-claude-agent Date: Sun, 17 May 2026 18:19:29 +0000 Subject: [PATCH] Add Gitea markdown rendering and Edit/Preview tabs - New /api/render-markdown proxy to Gitea (configured via config.yaml) - lib/config.mjs loads config.yaml with yaml-js - Edit/Preview tabs on the body field in the task dialog - Title and body rendered as markdown inline in the task list - Gitea markup+chroma CSS extracted and vendored to public/gitea-markup.css - Markdown cached in memory per text string Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 + config.yaml.example | 3 + lib/config.mjs | 16 ++++++ package-lock.json | 12 +++- package.json | 3 +- public/app.mjs | 72 ++++++++++++++++++++++- public/gitea-markup.css | 123 ++++++++++++++++++++++++++++++++++++++++ public/index.html | 1 + public/lib/api.mjs | 9 +-- public/style.css | 94 ++++++++++++++++++++++++++++++ public/templates.html | 21 +++++-- server.mjs | 26 +++++++++ 12 files changed, 369 insertions(+), 12 deletions(-) create mode 100644 config.yaml.example create mode 100644 lib/config.mjs create mode 100644 public/gitea-markup.css diff --git a/.gitignore b/.gitignore index 0d8fc6d..75bbf96 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ data/ *.tmp +config.yaml diff --git a/config.yaml.example b/config.yaml.example new file mode 100644 index 0000000..bd533f9 --- /dev/null +++ b/config.yaml.example @@ -0,0 +1,3 @@ +gitea: + url: https://gitea.efforting.tech + token: your_token_here diff --git a/lib/config.mjs b/lib/config.mjs new file mode 100644 index 0000000..de9e368 --- /dev/null +++ b/lib/config.mjs @@ -0,0 +1,16 @@ +import * as fs from 'node:fs'; +import yaml from 'yaml-js'; + +let _config = null; + +export function get_config() { + if (_config !== null) { return _config; } + try { + const content = fs.readFileSync('./config.yaml', 'utf-8'); + _config = yaml.load(content); + } catch (e) { + if (e.code === 'ENOENT') { _config = {}; } + else { throw e; } + } + return _config; +} diff --git a/package-lock.json b/package-lock.json index e1bba2c..214698a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "name": "task-inventory", "version": "0.1.0", "dependencies": { - "express": "^5.2.1" + "express": "^5.2.1", + "yaml-js": "^0.3.1" }, "engines": { "node": ">=25" @@ -841,6 +842,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" + }, + "node_modules/yaml-js": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/yaml-js/-/yaml-js-0.3.1.tgz", + "integrity": "sha512-LjoIFHCtSfkGzPsmYmfDsW+TShtQBcY7lwH1yLQ5brJHXRhjteUnVE2ThCbz1madq8JUZIAjFiavfnIFeTO8CQ==", + "license": "WTFPL", + "engines": { + "node": ">=8" + } } } } diff --git a/package.json b/package.json index 3feaf3c..e986a2d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "start": "node server.mjs" }, "dependencies": { - "express": "^5.2.1" + "express": "^5.2.1", + "yaml-js": "^0.3.1" } } diff --git a/public/app.mjs b/public/app.mjs index 67c1c80..38d3bda 100644 --- a/public/app.mjs +++ b/public/app.mjs @@ -17,6 +17,30 @@ class App_State { const state = new App_State(); +// --------------------------------------------------------------------------- +// Markdown +// --------------------------------------------------------------------------- + +const md_cache = new Map(); + +async function render_markdown(text) { + if (md_cache.has(text)) { return md_cache.get(text); } + const { html } = await api.render_markdown(text); + md_cache.set(text, html); + return html; +} + +function fill_markdown(el, text) { + if (!text) { el.innerHTML = ''; return; } + const cached = md_cache.get(text); + if (cached !== undefined) { + el.innerHTML = cached; + return; + } + el.textContent = text; // placeholder while loading + render_markdown(text).then(html => { el.innerHTML = html; }); +} + // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- @@ -54,6 +78,18 @@ class Task_Dialog { this.form = el.querySelector('form'); this.on_save = null; + // Tab switching + const tab_btns = el.querySelectorAll('.tab-btn'); + const tab_panes = el.querySelectorAll('.tab-pane'); + for (const btn of tab_btns) { + btn.addEventListener('click', () => { + const target = btn.dataset.tab; + for (const b of tab_btns) { b.classList.toggle('active', b.dataset.tab === target); } + for (const p of tab_panes) { p.hidden = p.dataset.tab !== target; } + if (target === 'preview') { this._load_preview(); } + }); + } + el.querySelector('.btn-cancel').addEventListener('click', () => el.close()); this.form.addEventListener('submit', (e) => { e.preventDefault(); @@ -62,6 +98,21 @@ class Task_Dialog { }); } + _load_preview() { + const text = this.form.elements['body'].value; + const preview = this.dialog.querySelector('.preview-content'); + if (!text.trim()) { preview.innerHTML = 'Nothing to preview.'; return; } + preview.classList.add('loading'); + preview.textContent = 'Loading…'; + render_markdown(text).then(html => { + preview.classList.remove('loading'); + preview.innerHTML = html; + }).catch(err => { + preview.classList.remove('loading'); + preview.textContent = 'Error: ' + err.message; + }); + } + _read_form() { const fd = new FormData(this.form); const tags_raw = fd.get('tags').trim(); @@ -81,12 +132,17 @@ class Task_Dialog { this.form.elements['status'].value = initial.status ?? 'open'; this.form.elements['priority'].value = initial.priority ?? 'normal'; this.form.elements['tags'].value = (initial.tags ?? []).join(', '); + // Always open on edit tab + const tab_btns = this.dialog.querySelectorAll('.tab-btn'); + const tab_panes = this.dialog.querySelectorAll('.tab-pane'); + for (const b of tab_btns) { b.classList.toggle('active', b.dataset.tab === 'edit'); } + for (const p of tab_panes) { p.hidden = p.dataset.tab !== 'edit'; } this.on_save = on_save; this.dialog.showModal(); } } -const task_dialog = new Task_Dialog(); +let task_dialog; // --------------------------------------------------------------------------- // Render @@ -179,7 +235,14 @@ function render_tasks(container) { priority_el.textContent = task.priority; priority_el.dataset.val = task.priority; - set_text(row, '.task-title', task.title); + const title_el = row.querySelector('.task-title'); + fill_markdown(title_el, task.title); + + const body_el = row.querySelector('.task-body'); + if (task.body) { + show(body_el); + fill_markdown(body_el, task.body); + } const tags_el = row.querySelector('.task-tags'); for (const tag of task.tags) { @@ -225,6 +288,9 @@ function open_edit_dialog(task) { const { task: updated } = await api.update_task(task.id, data); const idx = state.tasks.findIndex(t => t.id === task.id); if (idx !== -1) { state.tasks[idx] = updated; } + // Invalidate cache for changed fields + md_cache.delete(task.title); + md_cache.delete(task.body); render(); } catch (err) { alert(err.message); @@ -254,6 +320,8 @@ async function init() { document.body.appendChild(tmpl); } + task_dialog = new Task_Dialog(); + // Load data const { tasks } = await api.get_tasks(); state.tasks = tasks; diff --git a/public/gitea-markup.css b/public/gitea-markup.css new file mode 100644 index 0000000..108ea4f --- /dev/null +++ b/public/gitea-markup.css @@ -0,0 +1,123 @@ +.markup pre.is-loading,.editor-loading.is-loading{height:var(--height-loading)} +.markup .is-loading>*{visibility:hidden} +.markup .is-loading{color:transparent;background:transparent} +.markup{overflow:hidden;font-size:14px;line-height:1.5!important;overflow-wrap:break-word} +.markup>*:first-child{margin-top:0!important} +.markup>*:last-child{margin-bottom:0!important} +.markup a:not([href]){color:inherit;text-decoration:none} +.markup .absent{color:var(--color-red)} +.markup .anchor{float:left;padding-right:4px;margin-left:-20px;color:inherit} +.markup .anchor .svg{vertical-align:middle} +.markup .anchor:focus{outline:none} +.markup h1 .anchor{margin-top:-2px} +.markup h1 .anchor .svg,.markup h2 .anchor .svg,.markup h3 .anchor .svg,.markup h4 .anchor .svg,.markup h5 .anchor .svg,.markup h6 .anchor .svg{visibility:hidden} +.markup h1:hover .anchor .svg,.markup h2:hover .anchor .svg,.markup h3:hover .anchor .svg,.markup h4:hover .anchor .svg,.markup h5:hover .anchor .svg,.markup h6:hover .anchor .svg{visibility:visible} +.markup h2 .anchor .svg,.markup h3 .anchor .svg,.markup h4 .anchor .svg{position:relative;top:-2px} +.markup h1,.markup h2,.markup h3,.markup h4,.markup h5,.markup h6{margin-top:24px;margin-bottom:16px;font-weight:var(--font-weight-semibold);line-height:1.25} +.markup h1 tt,.markup h1 code,.markup h2 tt,.markup h2 code,.markup h3 tt,.markup h3 code,.markup h4 tt,.markup h4 code,.markup h5 tt,.markup h5 code,.markup h6 tt,.markup h6 code{font-size:inherit} +.markup h1{padding-bottom:.3em;font-size:2em;border-bottom:1px solid var(--color-secondary)} +.markup h2{padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid var(--color-secondary)} +.markup h3{font-size:1.25em} +.markup h4{font-size:1em} +.markup h5{font-size:.875em} +.markup h6{font-size:.85em;color:var(--color-text-light-2)} +.markup p,.markup blockquote,.markup details,.markup ul,.markup ol,.markup dl,.markup table,.markup pre{margin-top:0;margin-bottom:16px} +.markup p:last-child{margin-bottom:16px} +.markup hr{height:4px;padding:0;margin:16px 0;background-color:var(--color-secondary);border:0} +.markup ul,.markup ol{padding-left:2em} +.markup ul.no-list,.markup ol.no-list{padding:0;list-style-type:none} +.markup .task-list-item{list-style-type:none} +.markup .task-list-item p+ul{margin-top:16px} +.markup .task-list-item input[type=checkbox]{margin:0 .3em .25em -1.4em;vertical-align:middle;padding:0} +.markup .task-list-item input[type=checkbox]+p{margin-left:-.2em;display:inline} +.markup .task-list-item>p{margin-inline:16px} +.markup .task-list-item+.task-list-item{margin-top:4px} +.markup input[type=checkbox]{-webkit-appearance:none;-moz-appearance:none;appearance:none;position:relative;border:1px solid var(--color-secondary);border-radius:var(--border-radius);background:var(--color-input-background);height:14px;width:14px;opacity:1!important;pointer-events:auto!important;vertical-align:middle!important;-webkit-print-color-adjust:exact;color-adjust:exact} +.markup input[type=checkbox]:not([disabled]):hover,.markup input[type=checkbox]:not([disabled]):active{border-color:var(--color-primary)} +.markup input[type=checkbox]:after{position:absolute;inset:0;pointer-events:none;background:var(--color-text);mask-size:cover;-webkit-mask-size:cover} +.markup input[type=checkbox]:checked:after{content:"";mask-image:var(--checkbox-mask-checked);-webkit-mask-image:var(--checkbox-mask-checked);-webkit-print-color-adjust:exact;color-adjust:exact} +.markup input[type=checkbox]:indeterminate:after{content:"";mask-image:var(--checkbox-mask-indeterminate);-webkit-mask-image:var(--checkbox-mask-indeterminate)} +.markup ul ul,.markup ul ol,.markup ol ol,.markup ol ul{margin-top:0;margin-bottom:0} +.markup ol ol,.markup ul ol{list-style-type:lower-roman} +.markup li>p{margin-top:16px} +.markup li+li{margin-top:.25em} +.markup dl{padding:0} +.markup dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:var(--font-weight-semibold)} +.markup dl dd{padding:0 16px;margin-bottom:16px} +.markup blockquote{margin-left:0;padding:0 15px;color:var(--color-text-light-2);border-left:.25em solid var(--color-secondary)} +.markup blockquote>:first-child{margin-top:0} +.markup blockquote>:last-child{margin-bottom:0} +.markup table{display:block;width:100%;max-width:100%;overflow:auto} +.markup table th{font-weight:var(--font-weight-semibold)} +.markup table th,.markup table td{padding:6px 13px!important;border:1px solid var(--color-secondary)!important} +.markup table tr{border-top:1px solid var(--color-secondary)} +.markup table tr:nth-child(2n){background-color:var(--color-markup-table-row)} +.markup img,.markup video{max-width:100%;box-sizing:initial} +.file-view.markup{padding:1em 2em;font-size:16px} +.file-view.markup:has(.file-not-rendered-prompt){padding:0} +.file-view.markup img,.file-view.markup video,.comment-body .markup img,.comment-body .markup video,.comment-content .markup img,.comment-content .markup video,.wiki .markup img,.wiki .markup video{background:var(--color-box-body)} +.markup img[align=right],.markup video[align=right]{padding-left:20px} +.markup img[align=left],.markup video[align=left]{padding-right:28px} +.markup span.frame{display:block;overflow:hidden} +.markup span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid var(--color-secondary)} +.markup span.frame span img,.markup span.frame span video{display:block;float:left} +.markup span.frame span span{display:block;padding:5px 0 0;clear:both;color:var(--color-text)} +.markup span.align-center{display:block;overflow:hidden;clear:both} +.markup span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center} +.markup span.align-center span img,.markup span.align-center span video{margin:0 auto;text-align:center} +.markup span.align-right{display:block;overflow:hidden;clear:both} +.markup span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right} +.markup span.align-right span img,.markup span.align-right span video{margin:0;text-align:right} +.markup span.float-left{display:block;float:left;margin-right:13px;overflow:hidden} +.markup span.float-left span{margin:13px 0 0} +.markup span.float-right{display:block;float:right;margin-left:13px;overflow:hidden} +.markup span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right} +.markup code,.markup tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:var(--color-markup-code-inline);border-radius:var(--border-radius)} +.markup code br,.markup tt br{display:none} +.markup del code{text-decoration:inherit} +.markup pre>code{font-size:100%} +.markup .code-block,.markup .code-block-container{position:relative} +.markup .code-block-container.code-overflow-wrap pre>code{white-space:pre-wrap} +.markup .code-block-container.code-overflow-scroll pre{overflow-x:auto} +.markup .code-block-container.code-overflow-scroll pre>code{white-space:pre;overflow-wrap:normal} +.markup .highlight{margin-bottom:16px} +.markup .highlight pre,.markup pre{padding:16px;font-size:85%;line-height:1.45;background-color:var(--color-markup-code-block);border-radius:var(--border-radius)} +.markup .highlight pre{margin-bottom:0;word-break:normal} +.markup pre code,.markup pre tt{display:inline;padding:0;line-height:inherit;background-color:transparent;border:0} +.markup pre code:before,.markup pre code:after,.markup pre tt:before,.markup pre tt:after{content:normal} +.markup kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:var(--color-text-light);vertical-align:middle;background-color:var(--color-markup-code-inline);border:1px solid var(--color-secondary);border-radius:var(--border-radius);box-shadow:inset 0 -1px 0 var(--color-secondary)} +.markup .ui.list .list,.markup ol.ui.list ol,.markup ul.ui.list ul{padding-left:2em} +.markup details.frontmatter-content summary{text-overflow:ellipsis;overflow:hidden;white-space:nowrap;margin-bottom:.25em} +.markup details.frontmatter-content svg{vertical-align:middle;margin:0 .25em} +.markup-content-iframe{display:block;border:none;width:100%;height:var(--height-loading);overflow:hidden;color-scheme:normal} +.markup-block-error{display:block!important;border:none!important;margin-bottom:0!important;border-bottom-left-radius:0!important;border-bottom-right-radius:0!important;box-shadow:none!important;font-size:85%!important;white-space:pre-wrap!important;padding:.5rem 1rem!important;text-align:left!important} +.markup-block-error+pre{border-top:none!important;margin-top:0!important;border-top-left-radius:0!important;border-top-right-radius:0!important} +.file-view.markup.orgmode li.unchecked:before{content:"[ ] "} +.file-view.markup.orgmode li.checked:before{content:"[x] "} +.file-view.markup.orgmode li.indeterminate:before{content:"[-] "} +.file-view.markup.orgmode li.unchecked>p,.file-view.markup.orgmode li.checked>p,.file-view.markup.orgmode li.indeterminate>p{display:inline-block} +.markup .code-copy{position:absolute;top:8px;right:6px;padding:9px;visibility:hidden;animation:fadeout .2s both} +.repository.view.issue .comment-list .comment .markup .code-copy{right:5px;padding:8px} +.markup .code-copy:hover{background:var(--color-secondary)!important} +.markup .code-copy:active{background:var(--color-secondary-dark-1)!important} +.markup .code-block-container:hover .code-copy,.markup .code-block:hover .code-copy{visibility:visible;animation:fadein .2s both} +.markup .code-preview-container{border:1px solid var(--color-secondary);border-radius:var(--border-radius);margin:.25em 0} +.markup .code-preview-container .code-preview-header{color:var(--color-text-light-1);border-bottom:1px solid var(--color-secondary);padding:.5em;font-size:12px} +.markup .code-preview-container table{width:100%;max-height:240px;overflow-y:auto;margin:0} +.markup p:empty:has(+.code-preview-container){display:none} +.markup .code-preview-container table tr{border:0!important} +.markup .code-preview-container table th,.markup .code-preview-container table td{border:0!important;padding:0 0 0 5px!important} +.markup .code-preview-container table tr:nth-child(2n){background:none!important} +.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0} +.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block} +.chroma .hl{display:block;width:100%} +.chroma .lnt,.chroma .ln{margin-right:.4em;padding:0 .4em} +.chroma .gs{font-weight:var(--font-weight-semibold)} +.chroma .gl{text-decoration:underline} +.repository.view.issue .comment-list .comment .ui.form .field .tab.markup{min-height:5rem} +.repository.new.release .markup{min-height:240px} +.repository.wiki .markup{overflow:visible} +.repository.wiki .markup[data-tab-panel=markdown-previewer]{min-height:340px} +.repository.wiki .wiki-content-parts .markup{border:1px solid var(--color-secondary);border-radius:var(--border-radius);padding:1em;margin-top:1em;font-size:1em} +.combo-markdown-editor .ui.tab.markup[data-tab-panel=markdown-previewer]{border-bottom:1px solid var(--color-secondary);padding-bottom:1rem} +.comment-code-cloud .ui.active.tab.markup{padding:1em;min-height:168px}@media (prefers-color-scheme: light){.chroma .bp{color:#999}.chroma .c,.chroma .c1,.chroma .ch{color:#6a737d}.chroma .cm{color:#998}.chroma .cp{color:#109295}.chroma .cpf{color:#4c4dbc}.chroma .cs{color:#999}.chroma .dl{color:#106303}.chroma .gd{color:#000;background-color:#fdd}.chroma .ge{color:#000}.chroma .gh{color:#999}.chroma .gi{color:#000;background-color:#dfd}.chroma .go{color:#888}.chroma .gp{color:#555}.chroma .gr,.chroma .gt{color:#a00}.chroma .gu{color:#aaa}.chroma .il{color:#099}.chroma .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#d73a49}.chroma .kt{color:#458}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#099}.chroma .na{color:#d73a49}.chroma .nb{color:#005cc5}.chroma .nc{color:#458}.chroma .nd{color:#3c5d5d}.chroma .ne{color:#900}.chroma .nf{color:#005cc5}.chroma .ni{color:#6f42c1}.chroma .nl{color:#900}.chroma .nn{color:#555}.chroma .no{color:teal}.chroma .nt{color:#22863a}.chroma .nv{color:teal}.chroma .nx{color:#24292e}.chroma .o,.chroma .ow{color:#d73a49}.chroma .s,.chroma .s1,.chroma .s2{color:#106303}.chroma .sa{color:#cc7a00}.chroma .sb{color:#106303}.chroma .sc{color:#cc7a00}.chroma .sd{color:#106303}.chroma .se{color:#940}.chroma .sh{color:#106303}.chroma .si{color:#cc7a00}.chroma .sr{color:#4c4dbc}.chroma .ss{color:#940}.chroma .sx{color:#106303}.chroma .vc,.chroma .vg,.chroma .vi{color:teal}.chroma .w{color:#bbb}}@media (prefers-color-scheme: light){gitea-theme-meta-info{--theme-display-name: "Light"}:root{--is-dark-theme: false;--color-primary: #4183c4;--color-primary-contrast: #ffffff;--color-primary-dark-1: #3876b3;--color-primary-dark-2: #31699f;--color-primary-dark-3: #2b5c8b;--color-primary-dark-4: #254f77;--color-primary-dark-5: #193450;--color-primary-dark-6: #0c1a28;--color-primary-dark-7: #04080c;--color-primary-light-1: #548fca;--color-primary-light-2: #679cd0;--color-primary-light-3: #7aa8d6;--color-primary-light-4: #8db5dc;--color-primary-light-5: #b3cde7;--color-primary-light-6: #d9e6f3;--color-primary-light-7: #f4f8fb;--color-primary-alpha-10: #4183c419;--color-primary-alpha-20: #4183c433;--color-primary-alpha-30: #4183c44b;--color-primary-alpha-40: #4183c466;--color-primary-alpha-50: #4183c480;--color-primary-alpha-60: #4183c499;--color-primary-alpha-70: #4183c4b3;--color-primary-alpha-80: #4183c4cc;--color-primary-alpha-90: #4183c4e1;--color-primary-hover: var(--color-primary-dark-1);--color-primary-active: var(--color-primary-dark-2);--color-secondary: #d0d7de;--color-secondary-dark-1: #c7ced5;--color-secondary-dark-2: #b9c0c7;--color-secondary-dark-3: #99a0a7;--color-secondary-dark-4: #899097;--color-secondary-dark-5: #7a8188;--color-secondary-dark-6: #6a7178;--color-secondary-dark-7: #5b6269;--color-secondary-dark-8: #4b5259;--color-secondary-dark-9: #3c434a;--color-secondary-dark-10: #2c333a;--color-secondary-dark-11: #1d242b;--color-secondary-dark-12: #0d141b;--color-secondary-dark-13: #00040b;--color-secondary-light-1: #dee5ec;--color-secondary-light-2: #e4ebf2;--color-secondary-light-3: #ebf2f9;--color-secondary-light-4: #f1f8ff;--color-secondary-alpha-10: #d0d7de19;--color-secondary-alpha-20: #d0d7de33;--color-secondary-alpha-30: #d0d7de4b;--color-secondary-alpha-40: #d0d7de66;--color-secondary-alpha-50: #d0d7de80;--color-secondary-alpha-60: #d0d7de99;--color-secondary-alpha-70: #d0d7deb3;--color-secondary-alpha-80: #d0d7decc;--color-secondary-alpha-90: #d0d7dee1;--color-secondary-button: var(--color-secondary-dark-4);--color-secondary-hover: var(--color-secondary-dark-5);--color-secondary-active: var(--color-secondary-dark-6);--color-console-fg: #f7f8f9;--color-console-fg-subtle: #bdc4cc;--color-console-bg: #171b1e;--color-console-border: #2e353b;--color-console-hover-bg: #272d33;--color-console-active-bg: #2e353b;--color-console-menu-bg: #262b31;--color-console-menu-border: #414b55;--color-red: #db2828;--color-orange: #f2711c;--color-yellow: #fbbd08;--color-olive: #b5cc18;--color-green: #21ba45;--color-teal: #00b5ad;--color-blue: #2185d0;--color-violet: #6435c9;--color-purple: #a333c8;--color-pink: #e03997;--color-brown: #a5673f;--color-black: #1d2328;--color-red-light: #e45e5e;--color-orange-light: #f59555;--color-yellow-light: #fcce46;--color-olive-light: #d3e942;--color-green-light: #46de6a;--color-teal-light: #08fff4;--color-blue-light: #51a5e3;--color-violet-light: #8b67d7;--color-purple-light: #bb64d8;--color-pink-light: #e86bb1;--color-brown-light: #c58b66;--color-black-light: #4b5b68;--color-red-dark-1: #c82121;--color-orange-dark-1: #e6630d;--color-yellow-dark-1: #e5ac04;--color-olive-dark-1: #a3b816;--color-green-dark-1: #1ea73e;--color-teal-dark-1: #00a39c;--color-blue-dark-1: #1e78bb;--color-violet-dark-1: #5a30b5;--color-purple-dark-1: #932eb4;--color-pink-dark-1: #db228a;--color-brown-dark-1: #955d39;--color-black-dark-1: #2c3339;--color-red-dark-2: #b11e1e;--color-orange-dark-2: #cc580c;--color-yellow-dark-2: #cc9903;--color-olive-dark-2: #91a313;--color-green-dark-2: #1a9537;--color-teal-dark-2: #00918a;--color-blue-dark-2: #1a6aa6;--color-violet-dark-2: #502aa1;--color-purple-dark-2: #8229a0;--color-pink-dark-2: #c21e7b;--color-brown-dark-2: #845232;--color-black-dark-2: #131619;--color-ansi-black: #1e2327;--color-ansi-red: #cc4848;--color-ansi-green: #87ab63;--color-ansi-yellow: #cc9903;--color-ansi-blue: #3a8ac6;--color-ansi-magenta: #d22e8b;--color-ansi-cyan: #00918a;--color-ansi-white: var(--color-console-fg-subtle);--color-ansi-bright-black: #46494d;--color-ansi-bright-red: #d15a5a;--color-ansi-bright-green: #93b373;--color-ansi-bright-yellow: #eaaf03;--color-ansi-bright-blue: #4e96cc;--color-ansi-bright-magenta: #d74397;--color-ansi-bright-cyan: #00b6ad;--color-ansi-bright-white: var(--color-console-fg);--color-grey: #697077;--color-grey-light: #7c838a;--color-gold: #a1882b;--color-white: #ffffff;--color-diff-added-linenum-bg: #d1f8d9;--color-diff-added-row-bg: #e6ffed;--color-diff-added-row-border: #e6ffed;--color-diff-added-word-bg: #acf2bd;--color-diff-moved-row-bg: #f1f8d1;--color-diff-moved-row-border: #d0e27f;--color-diff-removed-linenum-bg: #ffcecb;--color-diff-removed-row-bg: #ffeef0;--color-diff-removed-row-border: #f1c0c0;--color-diff-removed-word-bg: #fdb8c0;--color-diff-inactive: #f0f2f4;--color-error-border: #e0b4b4;--color-error-bg: #fff6f6;--color-error-bg-active: #fbb;--color-error-bg-hover: #fdd;--color-error-text: #9f3a38;--color-success-border: #a3c293;--color-success-bg: #fcfff5;--color-success-text: #2c662d;--color-warning-border: #c9ba9b;--color-warning-bg: #fffaf3;--color-warning-text: #573a08;--color-info-border: #a9d5de;--color-info-bg: #f8ffff;--color-info-text: #276f86;--color-red-badge: #db2828;--color-red-badge-bg: #db28281a;--color-red-badge-hover-bg: #db28284d;--color-green-badge: #21ba45;--color-green-badge-bg: #21ba451a;--color-green-badge-hover-bg: #21ba454d;--color-yellow-badge: #fbbd08;--color-yellow-badge-bg: #fbbd081a;--color-yellow-badge-hover-bg: #fbbd084d;--color-orange-badge: #f2711c;--color-orange-badge-bg: #f2711c1a;--color-orange-badge-hover-bg: #f2711c4d;--color-git: #f05133;--color-logo: #609926;--color-body: #ffffff;--color-box-header: #f1f3f5;--color-box-body: #ffffff;--color-box-body-highlight: #ecf5fd;--color-text-dark: #01050a;--color-text: #181c21;--color-text-light: #30363b;--color-text-light-1: #40474d;--color-text-light-2: #5b6167;--color-text-light-3: #747c84;--color-footer: var(--color-nav-bg);--color-timeline: #d0d7de;--color-input-text: var(--color-text-dark);--color-input-background: #fff;--color-input-toggle-background: #d0d7de;--color-input-border: var(--color-secondary);--color-input-border-hover: var(--color-secondary-dark-1);--color-light: #00001706;--color-light-mimic-enabled: rgba(0, 0, 0, calc(6 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border: #0000171d;--color-hover: #00001708;--color-hover-opaque: #f1f3f5;--color-active: #00001714;--color-menu: #f8f9fb;--color-card: #f8f9fb;--color-markup-table-row: #0030600a;--color-markup-code-block: #00306010;--color-markup-code-inline: #00306012;--color-button: #f8f9fb;--color-code-bg: #fafdff;--color-shadow: #00001726;--color-shadow-opaque: #c7ced5;--color-secondary-bg: #f2f5f8;--color-expand-button: #cfe8fa;--color-placeholder-text: var(--color-text-light-3);--color-editor-line-highlight: var(--color-primary-light-6);--color-project-column-bg: var(--color-secondary-light-4);--color-caret: var(--color-text-dark);--color-reaction-bg: #0000170a;--color-reaction-hover-bg: var(--color-primary-light-5);--color-reaction-active-bg: var(--color-primary-light-6);--color-tooltip-text: #fbfdff;--color-tooltip-bg: #000017f0;--color-nav-bg: #f6f7fa;--color-nav-hover-bg: var(--color-secondary-light-1);--color-nav-text: var(--color-text);--color-secondary-nav-bg: #f9fafb;--color-label-text: var(--color-text);--color-label-bg: #949da64b;--color-label-hover-bg: #949da6a0;--color-label-active-bg: #949da6ff;--color-accent: var(--color-primary-light-1);--color-small-accent: var(--color-primary-light-6);--color-highlight-fg: #eed200;--color-highlight-bg: #fffbdd;--color-overlay-backdrop: #080808c0;accent-color:var(--color-accent);color-scheme:light}}@media (prefers-color-scheme: dark){.chroma .bp{color:#fabd2f}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm{color:#777e94}.chroma .cp{color:#8ec07c}.chroma .cpf{color:#649bc4}.chroma .cs{color:#9075cd}.chroma .dl{color:#649bc4}.chroma .gd{color:#fff;background-color:#5f3737}.chroma .ge{color:#ddee30}.chroma .gh{color:#ffaa10}.chroma .gi{color:#fff;background-color:#3a523a}.chroma .go{color:#777e94}.chroma .gp{color:#ebdbb2}.chroma .gr{color:#f43}.chroma .gs{color:#ebdbb2}.chroma .gt{color:#ff7540}.chroma .gu{color:#b8bb26}.chroma .il{color:#649bc4}.chroma .k{color:#ff7540}.chroma .kc{color:#649bc4}.chroma .kd{color:#ff7540}.chroma .kn{color:#ffaa10}.chroma .kp{color:#5f8700}.chroma .kr{color:#ff7540}.chroma .kt{color:#ff7b72}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#649bc4}.chroma .n{color:#c9d1d9}.chroma .na,.chroma .nb{color:#fabd2f}.chroma .nc{color:#ffaa10}.chroma .nd{color:#8ec07c}.chroma .ne{color:#ff7540}.chroma .nf,.chroma .ni{color:#fabd2f}.chroma .nl{color:#ff7540}.chroma .nn{color:#c9d1d9}.chroma .no{color:#649bc4}.chroma .nt{color:#ff7540}.chroma .nv{color:#ebdbb2}.chroma .nx{color:#b6bac5}.chroma .o{color:#ff7540}.chroma .ow{color:#5f8700}.chroma .p{color:#d2d4db}.chroma .s,.chroma .s1,.chroma .s2{color:#b8bb26}.chroma .sa{color:#ffaa10}.chroma .sb{color:#b8bb26}.chroma .sc{color:#ffaa10}.chroma .sd{color:#b8bb26}.chroma .se{color:#ff8540}.chroma .sh{color:#b8bb26}.chroma .si{color:#ffaa10}.chroma .sr{color:#9075cd}.chroma .ss{color:#ff8540}.chroma .sx{color:#ffaa10}.chroma .vc,.chroma .vg,.chroma .vi{color:#649bee}.chroma .w{color:#7f8699}}@media (prefers-color-scheme: dark){.CodeMirror.cm-s-default .cm-property,.CodeMirror.cm-s-paper .cm-property{color:#a0cc75}.CodeMirror.cm-s-default .cm-header,.CodeMirror.cm-s-paper .cm-header{color:#9daccc}.CodeMirror.cm-s-default .cm-quote,.CodeMirror.cm-s-paper .cm-quote{color:#090}.CodeMirror.cm-s-default .cm-keyword,.CodeMirror.cm-s-paper .cm-keyword{color:#cc8a61}.CodeMirror.cm-s-default .cm-atom,.CodeMirror.cm-s-paper .cm-atom{color:#ef5e77}.CodeMirror.cm-s-default .cm-number,.CodeMirror.cm-s-paper .cm-number{color:#ff5656}.CodeMirror.cm-s-default .cm-def,.CodeMirror.cm-s-paper .cm-def{color:#e4e4e4}.CodeMirror.cm-s-default .cm-variable-2,.CodeMirror.cm-s-paper .cm-variable-2{color:#00bdbf}.CodeMirror.cm-s-default .cm-variable-3,.CodeMirror.cm-s-paper .cm-variable-3{color:#085}.CodeMirror.cm-s-default .cm-comment,.CodeMirror.cm-s-paper .cm-comment{color:#8e9ab3}.CodeMirror.cm-s-default .cm-string,.CodeMirror.cm-s-paper .cm-string{color:#a77272}.CodeMirror.cm-s-default .cm-string-2,.CodeMirror.cm-s-paper .cm-string-2{color:#f50}.CodeMirror.cm-s-default .cm-meta,.CodeMirror.cm-s-paper .cm-meta,.CodeMirror.cm-s-default .cm-qualifier,.CodeMirror.cm-s-paper .cm-qualifier{color:#ffb176}.CodeMirror.cm-s-default .cm-builtin,.CodeMirror.cm-s-paper .cm-builtin{color:#b7c951}.CodeMirror.cm-s-default .cm-bracket,.CodeMirror.cm-s-paper .cm-bracket{color:#997}.CodeMirror.cm-s-default .cm-tag,.CodeMirror.cm-s-paper .cm-tag{color:#f1d273}.CodeMirror.cm-s-default .cm-attribute,.CodeMirror.cm-s-paper .cm-attribute{color:#bfcc70}.CodeMirror.cm-s-default .cm-hr,.CodeMirror.cm-s-paper .cm-hr{color:#999}.CodeMirror.cm-s-default .cm-url,.CodeMirror.cm-s-paper .cm-url{color:#c5cfd0}.CodeMirror.cm-s-default .cm-link,.CodeMirror.cm-s-paper .cm-link{color:#d8c792}.CodeMirror.cm-s-default .cm-error,.CodeMirror.cm-s-paper .cm-error{color:#dbdbeb}}@media (prefers-color-scheme: dark){gitea-theme-meta-info{--theme-display-name: "Dark"}:root{--is-dark-theme: true;--color-primary: #4183c4;--color-primary-contrast: #ffffff;--color-primary-dark-1: #548fca;--color-primary-dark-2: #679cd0;--color-primary-dark-3: #7aa8d6;--color-primary-dark-4: #8db5dc;--color-primary-dark-5: #b3cde7;--color-primary-dark-6: #d9e6f3;--color-primary-dark-7: #f4f8fb;--color-primary-light-1: #3876b3;--color-primary-light-2: #31699f;--color-primary-light-3: #2b5c8b;--color-primary-light-4: #254f77;--color-primary-light-5: #193450;--color-primary-light-6: #0c1a28;--color-primary-light-7: #04080c;--color-primary-alpha-10: #4183c419;--color-primary-alpha-20: #4183c433;--color-primary-alpha-30: #4183c44b;--color-primary-alpha-40: #4183c466;--color-primary-alpha-50: #4183c480;--color-primary-alpha-60: #4183c499;--color-primary-alpha-70: #4183c4b3;--color-primary-alpha-80: #4183c4cc;--color-primary-alpha-90: #4183c4e1;--color-primary-hover: var(--color-primary-light-1);--color-primary-active: var(--color-primary-light-2);--color-secondary: #3b444c;--color-secondary-dark-1: #414b54;--color-secondary-dark-2: #49545f;--color-secondary-dark-3: #576471;--color-secondary-dark-4: #677685;--color-secondary-dark-5: #758594;--color-secondary-dark-6: #8392a0;--color-secondary-dark-7: #929eab;--color-secondary-dark-8: #a2acb7;--color-secondary-dark-9: #a9b3bd;--color-secondary-dark-10: #b7bfc7;--color-secondary-dark-11: #c5cbd2;--color-secondary-dark-12: #cfd4da;--color-secondary-dark-13: #d2d7dc;--color-secondary-light-1: #313940;--color-secondary-light-2: #292f35;--color-secondary-light-3: #1d2226;--color-secondary-light-4: #171b1e;--color-secondary-alpha-10: #3b444c19;--color-secondary-alpha-20: #3b444c33;--color-secondary-alpha-30: #3b444c4b;--color-secondary-alpha-40: #3b444c66;--color-secondary-alpha-50: #3b444c80;--color-secondary-alpha-60: #3b444c99;--color-secondary-alpha-70: #3b444cb3;--color-secondary-alpha-80: #3b444ccc;--color-secondary-alpha-90: #3b444ce1;--color-secondary-button: var(--color-secondary-dark-4);--color-secondary-hover: var(--color-secondary-dark-3);--color-secondary-active: var(--color-secondary-dark-2);--color-console-fg: #f7f8f9;--color-console-fg-subtle: #bdc4cc;--color-console-bg: #171b1e;--color-console-border: #2e353b;--color-console-hover-bg: #272d33;--color-console-active-bg: #2e353b;--color-console-menu-bg: #262b31;--color-console-menu-border: #414b55;--color-red: #cc4848;--color-orange: #cc580c;--color-yellow: #cc9903;--color-olive: #91a313;--color-green: #87ab63;--color-teal: #00918a;--color-blue: #3a8ac6;--color-violet: #906ae1;--color-purple: #b259d0;--color-pink: #d22e8b;--color-brown: #a47252;--color-black: #1d2328;--color-red-light: #d15a5a;--color-orange-light: #f6a066;--color-yellow-light: #eaaf03;--color-olive-light: #abc016;--color-green-light: #93b373;--color-teal-light: #00b6ad;--color-blue-light: #4e96cc;--color-violet-light: #9b79e4;--color-purple-light: #ba6ad5;--color-pink-light: #d74397;--color-brown-light: #b08061;--color-black-light: #424851;--color-red-dark-1: #c23636;--color-orange-dark-1: #f38236;--color-yellow-dark-1: #b88a03;--color-olive-dark-1: #839311;--color-green-dark-1: #7a9e55;--color-teal-dark-1: #00837c;--color-blue-dark-1: #347cb3;--color-violet-dark-1: #7b4edb;--color-purple-dark-1: #a742c9;--color-pink-dark-1: #be297d;--color-brown-dark-1: #94674a;--color-black-dark-1: #292e38;--color-red-dark-2: #ad3030;--color-orange-dark-2: #f16e17;--color-yellow-dark-2: #a37a02;--color-olive-dark-2: #74820f;--color-green-dark-2: #6c8c4c;--color-teal-dark-2: #00746e;--color-blue-dark-2: #2e6e9f;--color-violet-dark-2: #6733d6;--color-purple-dark-2: #9834b9;--color-pink-dark-2: #a9246f;--color-brown-dark-2: #835b42;--color-black-dark-2: #272930;--color-ansi-black: #1e2327;--color-ansi-red: #cc4848;--color-ansi-green: #87ab63;--color-ansi-yellow: #cc9903;--color-ansi-blue: #3a8ac6;--color-ansi-magenta: #d22e8b;--color-ansi-cyan: #00918a;--color-ansi-white: var(--color-console-fg-subtle);--color-ansi-bright-black: #424851;--color-ansi-bright-red: #d15a5a;--color-ansi-bright-green: #93b373;--color-ansi-bright-yellow: #eaaf03;--color-ansi-bright-blue: #4e96cc;--color-ansi-bright-magenta: #d74397;--color-ansi-bright-cyan: #00b6ad;--color-ansi-bright-white: var(--color-console-fg);--color-grey: #384149;--color-grey-light: #818f9e;--color-gold: #b1983b;--color-white: #ffffff;--color-diff-added-linenum-bg: #274227;--color-diff-added-row-bg: #203224;--color-diff-added-row-border: #314a37;--color-diff-added-word-bg: #3c653c;--color-diff-moved-row-bg: #818044;--color-diff-moved-row-border: #bcca6f;--color-diff-removed-linenum-bg: #482121;--color-diff-removed-row-bg: #301e1e;--color-diff-removed-row-border: #634343;--color-diff-removed-word-bg: #6f3333;--color-diff-inactive: #22282d;--color-error-border: #a04141;--color-error-bg: #522;--color-error-bg-active: #744;--color-error-bg-hover: #633;--color-error-text: #f9cbcb;--color-success-border: #458a57;--color-success-bg: #284034;--color-success-text: #6cc664;--color-warning-border: #bb9d00;--color-warning-bg: #3a3a30;--color-warning-text: #fbbd08;--color-info-border: #306090;--color-info-bg: #26354c;--color-info-text: #38a8e8;--color-red-badge: #db2828;--color-red-badge-bg: #db28281a;--color-red-badge-hover-bg: #db28284d;--color-green-badge: #21ba45;--color-green-badge-bg: #21ba451a;--color-green-badge-hover-bg: #21ba454d;--color-yellow-badge: #fbbd08;--color-yellow-badge-bg: #fbbd081a;--color-yellow-badge-hover-bg: #fbbd084d;--color-orange-badge: #f2711c;--color-orange-badge-bg: #f2711c1a;--color-orange-badge-hover-bg: #f2711c4d;--color-git: #f05133;--color-logo: #609926;--color-body: #1b1f23;--color-box-header: #1a1d1f;--color-box-body: #14171a;--color-box-body-highlight: #1e2226;--color-text-dark: #f7f8f9;--color-text: #d0d5da;--color-text-light: #bcc3cb;--color-text-light-1: #a5afb9;--color-text-light-2: #8f9ba8;--color-text-light-3: #788797;--color-footer: var(--color-nav-bg);--color-timeline: #343c44;--color-input-text: var(--color-text-dark);--color-input-background: #171a1e;--color-input-toggle-background: #2e353c;--color-input-border: var(--color-secondary);--color-input-border-hover: var(--color-secondary-dark-1);--color-light: #00001728;--color-light-mimic-enabled: rgba(0, 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border: #e8f3ff28;--color-hover: #e8f3ff19;--color-hover-opaque: #21252a;--color-active: #e8f3ff24;--color-menu: #171a1e;--color-card: #171a1e;--color-markup-table-row: #e8f3ff0f;--color-markup-code-block: #e8f3ff12;--color-markup-code-inline: #e8f3ff28;--color-button: #171a1e;--color-code-bg: #14171a;--color-shadow: #00001758;--color-shadow-opaque: #000017;--color-secondary-bg: #2a3137;--color-expand-button: #2f363d;--color-placeholder-text: var(--color-text-light-3);--color-editor-line-highlight: var(--color-primary-light-5);--color-project-column-bg: var(--color-secondary-light-2);--color-caret: var(--color-text);--color-reaction-bg: #e8f3ff12;--color-reaction-hover-bg: var(--color-primary-light-4);--color-reaction-active-bg: var(--color-primary-light-5);--color-tooltip-text: #f9fafb;--color-tooltip-bg: #000b17f0;--color-nav-bg: #16191d;--color-nav-hover-bg: var(--color-secondary-light-1);--color-nav-text: var(--color-text);--color-secondary-nav-bg: #181c20;--color-label-text: var(--color-text);--color-label-bg: #7282924b;--color-label-hover-bg: #728292a0;--color-label-active-bg: #728292ff;--color-accent: var(--color-primary-light-1);--color-small-accent: var(--color-primary-light-5);--color-highlight-fg: #87651e;--color-highlight-bg: #352c1c;--color-overlay-backdrop: #080808c0;accent-color:var(--color-accent);color-scheme:dark}.emoji[aria-label="check mark"],.emoji[aria-label="currency exchange"],.emoji[aria-label="TOP arrow"],.emoji[aria-label="END arrow"],.emoji[aria-label="ON! arrow"],.emoji[aria-label="SOON arrow"],.emoji[aria-label="heavy dollar sign"],.emoji[aria-label=copyright],.emoji[aria-label=registered],.emoji[aria-label="trade mark"],.emoji[aria-label=multiply],.emoji[aria-label=plus],.emoji[aria-label=minus],.emoji[aria-label=divide],.emoji[aria-label="curly loop"],.emoji[aria-label="double curly loop"],.emoji[aria-label="wavy dash"],.emoji[aria-label="paw prints"],.emoji[aria-label="musical note"],.emoji[aria-label="musical notes"]{filter:invert(100%) hue-rotate(180deg)}}gitea-theme-meta-info{--theme-display-name: "Auto"} diff --git a/public/index.html b/public/index.html index 5c2a7cc..f0fea3c 100644 --- a/public/index.html +++ b/public/index.html @@ -4,6 +4,7 @@ Task Inventory + diff --git a/public/lib/api.mjs b/public/lib/api.mjs index 74af5c9..deff753 100644 --- a/public/lib/api.mjs +++ b/public/lib/api.mjs @@ -10,7 +10,8 @@ async function req(method, path, body) { return data; } -export const get_tasks = () => req('GET', '/api/tasks'); -export const create_task = (body) => req('POST', '/api/tasks', body); -export const update_task = (id, body) => req('PUT', `/api/tasks/${id}`, body); -export const delete_task = (id) => req('DELETE', `/api/tasks/${id}`); +export const get_tasks = () => req('GET', '/api/tasks'); +export const create_task = (body) => req('POST', '/api/tasks', body); +export const update_task = (id, body) => req('PUT', `/api/tasks/${id}`, body); +export const delete_task = (id) => req('DELETE', `/api/tasks/${id}`); +export const render_markdown = (text) => req('POST', '/api/render-markdown', { text }); diff --git a/public/style.css b/public/style.css index 8ddb606..2a21631 100644 --- a/public/style.css +++ b/public/style.css @@ -152,6 +152,7 @@ button:hover { background: #444; color: #fff; } /* Dialog */ dialog { + margin: auto; background: #242424; border: 1px solid #444; border-radius: 8px; @@ -202,3 +203,96 @@ dialog input:focus, dialog textarea:focus, dialog select:focus { padding: 2rem; text-align: center; } + +/* Markdown inline in task list */ +.task-title.markup, .task-body.markup { + padding: 0; + font-size: 14px; + overflow: visible; +} + +.task-title.markup p { margin: 0; display: inline; } +.task-title.markup > *:first-child { margin-top: 0; } + +.task-body.markup { + margin-top: 0.25rem; + font-size: 12px; + color: #aaa; + max-height: 3.5em; + overflow: hidden; +} + +.task-body.markup > *:first-child { margin-top: 0; } +.task-body.markup > *:last-child { margin-bottom: 0; } + +/* Field with edit/preview tabs */ +.field-with-tabs { + margin-bottom: 0.75rem; +} + +.tab-bar { + display: flex; + align-items: center; + gap: 2px; + margin-bottom: 0; + border-bottom: 1px solid #444; + padding-bottom: 0; +} + +.tab-btn { + padding: 0.25rem 0.75rem; + border-radius: 4px 4px 0 0; + border: 1px solid transparent; + border-bottom: none; + background: transparent; + color: #888; + font-size: 12px; + margin-bottom: -1px; +} + +.tab-btn.active { + background: #1a1a1a; + color: #e0e0e0; + border-color: #444; + border-bottom-color: #1a1a1a; +} + +.tab-btn:hover:not(.active) { color: #ccc; } + +.tab-label { + margin-left: auto; + font-size: 11px; + color: #666; + padding-right: 0.25rem; +} + +.tab-pane textarea { + width: 100%; + padding: 0.4rem 0.6rem; + background: #1a1a1a; + border: 1px solid #444; + border-top: none; + border-radius: 0 0 4px 4px; + color: #e0e0e0; + font-size: 13px; + font-family: monospace; + resize: vertical; +} + +.tab-pane textarea:focus { + outline: none; + border-color: #5588e0; +} + +.preview-content { + min-height: 8rem; + padding: 0.6rem 0.75rem; + background: #1a1a1a; + border: 1px solid #444; + border-top: none; + border-radius: 0 0 4px 4px; + font-size: 13px; + overflow: auto; +} + +.preview-content.loading { color: #555; font-style: italic; } diff --git a/public/templates.html b/public/templates.html index 8253d1c..f0a6a8d 100644 --- a/public/templates.html +++ b/public/templates.html @@ -4,7 +4,10 @@ - +
+
+ +
@@ -21,9 +24,19 @@ - +
+
+ + + Body +
+
+ +
+ +