Added checklists to database, rendering and toggling items

This commit is contained in:
Magnus Åhall 2024-01-10 23:19:40 +01:00
parent 5c27f9ed1c
commit f98a6ab863
9 changed files with 455 additions and 31 deletions

24
main.go
View File

@ -104,6 +104,7 @@ func main() { // {{{
service.Register("/node/delete", true, true, nodeDelete) service.Register("/node/delete", true, true, nodeDelete)
service.Register("/node/download", true, true, nodeDownload) service.Register("/node/download", true, true, nodeDownload)
service.Register("/node/search", true, true, nodeSearch) service.Register("/node/search", true, true, nodeSearch)
service.Register("/node/checklist_item/state", true, true, nodeChecklistItemState)
service.Register("/key/retrieve", true, true, keyRetrieve) service.Register("/key/retrieve", true, true, keyRetrieve)
service.Register("/key/create", true, true, keyCreate) service.Register("/key/create", true, true, keyCreate)
service.Register("/key/counter", true, true, keyCounter) service.Register("/key/counter", true, true, keyCounter)
@ -477,6 +478,29 @@ func nodeSearch(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{
"Nodes": nodes, "Nodes": nodes,
}) })
} // }}} } // }}}
func nodeChecklistItemState(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
logger.Info("webserver", "request", "/node/checklist_item/state")
var err error
req := struct {
ChecklistItemID int
State bool
}{}
if err = parseRequest(r, &req); err != nil {
responseError(w, err)
return
}
err = ChecklistItemState(sess.UserID, req.ChecklistItemID, req.State)
if err != nil {
responseError(w, err)
return
}
responseData(w, map[string]interface{}{
"OK": true,
})
} // }}}
func keyRetrieve(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{ func keyRetrieve(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
logger.Info("webserver", "request", "/key/retrieve") logger.Info("webserver", "request", "/key/retrieve")

111
node.go
View File

@ -8,6 +8,22 @@ import (
"time" "time"
) )
type ChecklistItem struct {
ID int
GroupID int `db:"checklist_group_id"`
Order int
Label string
Checked bool
}
type ChecklistGroup struct {
ID int
NodeID int `db:"node_id"`
Order int
Label string
Items []ChecklistItem
}
type Node struct { type Node struct {
ID int ID int
UserID int `db:"user_id"` UserID int `db:"user_id"`
@ -22,6 +38,8 @@ type Node struct {
Complete bool Complete bool
Level int Level int
ChecklistGroups []ChecklistGroup
ContentEncrypted string `db:"content_encrypted" json:"-"` ContentEncrypted string `db:"content_encrypted" json:"-"`
Markdown bool Markdown bool
} }
@ -211,6 +229,8 @@ func RetrieveNode(userID, nodeID int) (node Node, err error) { // {{{
} else { } else {
node.Content = row.Content node.Content = row.Content
} }
node.retrieveChecklist()
} }
if row.Level == 1 { if row.Level == 1 {
@ -421,5 +441,96 @@ func SearchNodes(userID int, search string) (nodes []Node, err error) { // {{{
return return
} // }}} } // }}}
func ChecklistItemState(userID, checklistItemID int, state bool) (err error) {// {{{
_, err = service.Db.Conn.Exec(
`
UPDATE checklist_item i
SET checked = $3
FROM checklist_group g, node n
WHERE
i.checklist_group_id = g.id AND
g.node_id = n.id AND
n.user_id = $1 AND
i.id = $2;
`,
userID,
checklistItemID,
state,
)
return
}// }}}
func (node *Node) retrieveChecklist() (err error) { // {{{
var rows *sqlx.Rows
rows, err = service.Db.Conn.Queryx(`
SELECT
g.id AS group_id,
g.order AS group_order,
g.label AS group_label,
i.id AS item_id,
i.order AS item_order,
i.label AS item_label,
i.checked
FROM public.checklist_group g
LEFT JOIN public.checklist_item i ON i.checklist_group_id = g.id
WHERE
g.node_id = $1
ORDER BY
g.order DESC,
i.order DESC
`, node.ID)
if err != nil {
return
}
defer rows.Close()
groups := make(map[int]*ChecklistGroup)
var found bool
var group *ChecklistGroup
var item ChecklistItem
for rows.Next() {
row := struct {
GroupID int `db:"group_id"`
GroupOrder int `db:"group_order"`
GroupLabel string `db:"group_label"`
ItemID int `db:"item_id"`
ItemOrder int `db:"item_order"`
ItemLabel string `db:"item_label"`
Checked bool
}{}
err = rows.StructScan(&row)
if err != nil {
return
}
if group, found = groups[row.GroupID]; !found {
group = new(ChecklistGroup)
group.ID = row.GroupID
group.NodeID = node.ID
group.Order = row.GroupOrder
group.Label = row.GroupLabel
group.Items = []ChecklistItem{}
groups[group.ID] = group
}
item = ChecklistItem{}
item.ID = row.ItemID
item.GroupID = row.GroupID
item.Order = row.ItemOrder
item.Label = row.ItemLabel
item.Checked = row.Checked
group.Items = append(group.Items, item)
}
node.ChecklistGroups = []ChecklistGroup{}
for _, group := range groups {
node.ChecklistGroups = append(node.ChecklistGroups, *group)
}
return
} // }}}
// vim: foldmethod=marker // vim: foldmethod=marker

18
sql/00014.sql Normal file
View File

@ -0,0 +1,18 @@
CREATE TABLE checklist_group (
id serial NOT NULL,
node_id int4 NOT NULL,
"order" int NOT NULL DEFAULT 0,
label varchar NOT NULL,
CONSTRAINT checklist_group_pk PRIMARY KEY (id),
CONSTRAINT checklist_group_node_fk FOREIGN KEY (node_id) REFERENCES public."node"(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE checklist_item (
id serial NOT NULL,
checklist_group_id int4 NOT NULL,
"order" int NOT NULL DEFAULT 0,
label varchar NOT NULL,
checked bool NOT NULL DEFAULT false,
CONSTRAINT checklist_item_pk PRIMARY KEY (id),
CONSTRAINT checklist_group_item_fk FOREIGN KEY (checklist_group_id) REFERENCES public."checklist_group"(id) ON DELETE CASCADE ON UPDATE CASCADE
)

View File

@ -26,12 +26,12 @@ body {
height: 100%; height: 100%;
} }
h1 { h1 {
margin-top: 0px;
font-size: 1.5em; font-size: 1.5em;
color: #518048;
} }
h2 { h2 {
margin-top: 32px;
font-size: 1.25em; font-size: 1.25em;
color: #518048;
} }
button { button {
font-size: 1em; font-size: 1em;
@ -307,10 +307,15 @@ header .menu {
padding-top: 16px; padding-top: 16px;
} }
#markdown { #markdown {
padding: 16px;
color: #333; color: #333;
grid-area: content; grid-area: content;
justify-self: center;
width: calc(100% - 32px);
max-width: 900px; max-width: 900px;
padding: 0.5rem;
border-radius: 8px;
margin-top: 8px;
margin-bottom: 0px;
} }
#markdown table { #markdown table {
border-collapse: collapse; border-collapse: collapse;
@ -330,6 +335,68 @@ header .menu {
padding: 0px; padding: 0px;
border-radius: 0px; border-radius: 0px;
} }
#checklist {
grid-area: checklist;
color: #333;
justify-self: center;
width: calc(100% - 32px);
max-width: 900px;
padding: 0.5rem;
border-radius: 8px;
margin-top: 8px;
margin-bottom: 0px;
}
#checklist .checklist-group {
margin-top: 1em;
font-weight: bold;
}
#checklist .checklist-item {
display: grid;
grid-template-columns: min-content 1fr;
align-items: center;
margin-top: 0.5em;
}
#checklist .checklist-item.checked {
text-decoration: line-through;
color: #888;
}
#checklist .checklist-item input[type="checkbox"] {
margin-left: 0px;
margin-right: 8px;
-webkit-appearance: none;
appearance: none;
background-color: #fff;
margin: 0 8px 0 0;
font: inherit;
color: currentColor;
width: 1.25em;
height: 1.25em;
border: 0.15em solid currentColor;
border-radius: 0.15em;
transform: translateY(-0.075em);
display: grid;
place-content: center;
}
#checklist .checklist-item input[type="checkbox"].ok {
border: 0.15em solid #54b356;
}
#checklist .checklist-item input[type="checkbox"].ok::before {
box-shadow: inset 1em 1em #54b356;
}
#checklist .checklist-item input[type="checkbox"]::before {
content: "";
width: 0.7em;
height: 0.7em;
transform: scale(0);
transition: 120ms transform ease-in-out;
box-shadow: inset 1em 1em #666;
}
#checklist .checklist-item input[type="checkbox"]:checked::before {
transform: scale(1);
}
#checklist .checklist-item label {
user-select: none;
}
/* ============================================================= * /* ============================================================= *
* Textarea replicates the height of an element expanding height * * Textarea replicates the height of an element expanding height *
* ============================================================= */ * ============================================================= */
@ -476,9 +543,9 @@ header .menu {
} }
.layout-tree { .layout-tree {
display: grid; display: grid;
grid-template-areas: "header header" "tree crumbs" "tree child-nodes" "tree name" "tree content" "tree files" "tree blank"; grid-template-areas: "header header" "tree crumbs" "tree child-nodes" "tree name" "tree content" "tree checklist" "tree files" "tree blank";
grid-template-columns: min-content 1fr; grid-template-columns: min-content 1fr;
grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* files */ 1fr; grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr;
/* blank */ /* blank */
color: #fff; color: #fff;
min-height: 100%; min-height: 100%;
@ -511,14 +578,17 @@ header .menu {
display: block; display: block;
} }
.layout-crumbs { .layout-crumbs {
grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "files" "blank"; grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "files" "blank";
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* files */ 1fr; grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr;
/* blank */ /* blank */
} }
.layout-crumbs #tree { .layout-crumbs #tree {
display: none; display: none;
} }
.layout-crumbs #checklist {
padding: 16px;
}
.layout-keys { .layout-keys {
display: grid; display: grid;
grid-template-areas: "header" "keys"; grid-template-areas: "header" "keys";
@ -557,22 +627,25 @@ header .menu {
} }
#app.node { #app.node {
display: grid; display: grid;
grid-template-areas: "header header" "tree crumbs" "tree child-nodes" "tree name" "tree content" "tree files" "tree blank"; grid-template-areas: "header header" "tree crumbs" "tree child-nodes" "tree name" "tree content" "tree checklist" "tree files" "tree blank";
grid-template-columns: min-content 1fr; grid-template-columns: min-content 1fr;
grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* files */ 1fr; grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr;
/* blank */ /* blank */
color: #fff; color: #fff;
min-height: 100%; min-height: 100%;
} }
#app.node.toggle-tree { #app.node.toggle-tree {
grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "files" "blank"; grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "files" "blank";
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* files */ 1fr; grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr;
/* blank */ /* blank */
} }
#app.node.toggle-tree #tree { #app.node.toggle-tree #tree {
display: none; display: none;
} }
#app.node.toggle-tree #checklist {
padding: 16px;
}
#profile-settings { #profile-settings {
color: #333; color: #333;
padding: 16px; padding: 16px;
@ -588,14 +661,17 @@ header .menu {
} }
@media only screen and (max-width: 932px) { @media only screen and (max-width: 932px) {
#app.node { #app.node {
grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "files" "blank"; grid-template-areas: "header" "crumbs" "child-nodes" "name" "content" "checklist" "files" "blank";
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* files */ 1fr; grid-template-rows: min-content /* header */ min-content /* crumbs */ min-content /* child-nodes */ min-content /* name */ min-content /* content */ min-content /* checklist */ min-content /* files */ 1fr;
/* blank */ /* blank */
} }
#app.node #tree { #app.node #tree {
display: none; display: none;
} }
#app.node #checklist {
padding: 16px;
}
#app.node.toggle-tree { #app.node.toggle-tree {
display: grid; display: grid;
grid-template-areas: "header" "tree"; grid-template-areas: "header" "tree";
@ -628,7 +704,9 @@ header .menu {
padding: 16px; padding: 16px;
justify-self: start; justify-self: start;
} }
#file-section { #file-section,
#checklist,
#markdown {
width: calc(100% - 32px); width: calc(100% - 32px);
padding: 16px; padding: 16px;
margin-left: 16px; margin-left: 16px;

View File

@ -20,6 +20,7 @@
"node": "/js/{{ .VERSION }}/node.mjs", "node": "/js/{{ .VERSION }}/node.mjs",
"key": "/js/{{ .VERSION }}/key.mjs", "key": "/js/{{ .VERSION }}/key.mjs",
"crypto": "/js/{{ .VERSION }}/crypto.mjs", "crypto": "/js/{{ .VERSION }}/crypto.mjs",
"checklist": "/js/{{ .VERSION }}/checklist.mjs",
"ws": "/_js/{{ .VERSION }}/websocket.mjs" "ws": "/_js/{{ .VERSION }}/websocket.mjs"
} }
} }

94
static/js/checklist.mjs Normal file
View File

@ -0,0 +1,94 @@
import { h, Component, createRef } from 'preact'
import htm from 'htm'
import { signal } from 'preact/signals'
const html = htm.bind(h)
export class ChecklistGroup {
static sort(a, b) {//{{{
if (a.Order < b.Order) return -1
if (a.Order > b.Order) return 1
return 0
}//}}}
constructor(data) {//{{{
Object.keys(data).forEach(key => {
if (key == 'Items')
this.items = data[key].map(itemData =>
new ChecklistItem(itemData)
).sort(ChecklistItem.sort)
else
this[key] = data[key]
})
}//}}}
}
export class ChecklistItem {
static sort(a, b) {//{{{
if (a.Order < b.Order) return -1
if (a.Order > b.Order) return 1
return 0
}//}}}
constructor(data) {//{{{
Object.keys(data).forEach(key => {
this[key] = data[key]
})
}//}}}
}
export class Checklist extends Component {
render({ groups }) {//{{{
if (groups.length == 0)
return
groups.sort(ChecklistGroup.sort)
let groupElements = groups.map(group => html`<${ChecklistGroupElement} group=${group} />`)
return html`
<div id="checklist">
<h1>Checklist</h1>
${groupElements}
</div>
`
}//}}}
}
class ChecklistGroupElement extends Component {
render({ group }) {//{{{
let items = group.items.map(item => html`<${ChecklistItemElement} item=${item} />`)
return html`
<div class="checklist-group">${group.Label}</div>
${items}
`
}//}}}
}
class ChecklistItemElement extends Component {
constructor(props) {//{{{
super(props)
this.state = {
checked: props.item.Checked,
}
this.checkbox = createRef()
}//}}}
render({ item }, { checked }) {//{{{
return html`
<div class="checklist-item ${checked ? 'checked' : ''}">
<input type="checkbox" ref=${this.checkbox} key="checkbox-${item.ID}" id="checkbox-${item.ID}" checked=${checked} onchange=${evt => this.update(evt.target.checked)} />
<label for="checkbox-${item.ID}">${item.Label}</label>
</div>
`
}//}}}
update(checked) {//{{{
this.setState({ checked })
window._app.current.request('/node/checklist_item/state', {
ChecklistItemID: this.props.item.ID,
State: checked,
})
.then(res => {
this.checkbox.current.classList.add('ok')
setTimeout(()=>this.checkbox.current.classList.remove('ok'), 500)
})
.catch(window._app.current.responseError)
}//}}}
}

View File

@ -3,7 +3,7 @@ import htm from 'htm'
import { signal } from 'preact/signals' import { signal } from 'preact/signals'
import { Keys, Key } from 'key' import { Keys, Key } from 'key'
import Crypto from 'crypto' import Crypto from 'crypto'
//import { marked } from 'marked' import { Checklist, ChecklistGroup } from 'checklist'
const html = htm.bind(h) const html = htm.bind(h)
export class NodeUI extends Component { export class NodeUI extends Component {
@ -65,12 +65,14 @@ export class NodeUI extends Component {
let padlock = '' let padlock = ''
if (node.CryptoKeyID > 0) if (node.CryptoKeyID > 0)
padlock = html`<img src="/images/${window._VERSION}/padlock-black.svg" style="height: 24px;" />` padlock = html`<img src="/images/${window._VERSION}/padlock-black.svg" style="height: 24px;" />`
page = html` page = html`
${children.length > 0 ? html`<div class="child-nodes">${children}</div>` : html``} ${children.length > 0 ? html`<div class="child-nodes">${children}</div>` : html``}
<div class="node-name"> <div class="node-name">
${node.Name} ${padlock} ${node.Name} ${padlock}
</div> </div>
<${NodeContent} key=${node.ID} node=${node} ref=${this.nodeContent} /> <${NodeContent} key=${node.ID} node=${node} ref=${this.nodeContent} />
<${Checklist} groups=${node.ChecklistGroups} />
<${NodeFiles} node=${this.node.value} /> <${NodeFiles} node=${this.node.value} />
` `
} }
@ -342,16 +344,6 @@ class NodeContent extends Component {
let textarea = document.getElementById('node-content') let textarea = document.getElementById('node-content')
if (textarea) if (textarea)
textarea.parentNode.dataset.replicatedValue = textarea.value textarea.parentNode.dataset.replicatedValue = textarea.value
let crumbsEl = document.getElementById('crumbs')
let markdown = document.getElementById('markdown')
if (markdown) {
let margins = (crumbsEl.clientWidth - 900) / 2.0
if (margins < 0)
margins = 0
markdown.style.marginLeft = `${margins}px`
markdown.style.marginRight = `${margins}px`
}
}//}}} }//}}}
unlock() {//{{{ unlock() {//{{{
let pass = prompt(`Password for "${this.props.model.description}"`) let pass = prompt(`Password for "${this.props.model.description}"`)
@ -368,9 +360,9 @@ class NodeContent extends Component {
} }
class MarkdownContent extends Component { class MarkdownContent extends Component {
render({ content }) { render({ content }) {//{{{
return html`<div id="markdown"></div>` return html`<div id="markdown"></div>`
} }//}}}
componentDidMount() {//{{{ componentDidMount() {//{{{
const markdown = document.getElementById('markdown') const markdown = document.getElementById('markdown')
if (markdown) if (markdown)
@ -430,6 +422,7 @@ export class Node {
this.Files = [] this.Files = []
this._decrypted = false this._decrypted = false
this._expanded = false // start value for the TreeNode component, this._expanded = false // start value for the TreeNode component,
this.ChecklistGroups = {}
// it doesn't control it afterwards. // it doesn't control it afterwards.
// Used to expand the crumbs upon site loading. // Used to expand the crumbs upon site loading.
}//}}} }//}}}
@ -446,6 +439,7 @@ export class Node {
this.Files = res.Node.Files this.Files = res.Node.Files
this.Markdown = res.Node.Markdown this.Markdown = res.Node.Markdown
this.RenderMarkdown.value = this.Markdown this.RenderMarkdown.value = this.Markdown
this.initChecklist(res.Node.ChecklistGroups)
callback(this) callback(this)
}) })
.catch(this.app.responseError) .catch(this.app.responseError)
@ -605,6 +599,13 @@ export class Node {
this._decrypted = false this._decrypted = false
return this._content return this._content
}//}}} }//}}}
initChecklist(checklistData) {//{{{
if (checklistData === undefined || checklistData === null)
return
this.ChecklistGroups = checklistData.map(groupData=>{
return new ChecklistGroup(groupData)
})
}//}}}
} }
class Menu extends Component { class Menu extends Component {

View File

@ -23,13 +23,13 @@ html, body {
} }
h1 { h1 {
margin-top: 0px;
font-size: 1.5em; font-size: 1.5em;
color: @header_1;
} }
h2 { h2 {
margin-top: 32px;
font-size: 1.25em; font-size: 1.25em;
color: @header_1;
} }
button { button {
@ -352,10 +352,17 @@ header {
} }
#markdown { #markdown {
padding: 16px;
color: #333; color: #333;
grid-area: content; grid-area: content;
justify-self: center;
width: calc(100% - 32px);
max-width: 900px; max-width: 900px;
padding: 0.5rem;
border-radius: 8px;
margin-top: 8px;
margin-bottom: 0px;
table { table {
border-collapse: collapse; border-collapse: collapse;
@ -379,6 +386,83 @@ header {
} }
} }
#checklist {
grid-area: checklist;
color: #333;
justify-self: center;
width: calc(100% - 32px);
max-width: 900px;
padding: 0.5rem;
border-radius: 8px;
margin-top: 8px;
margin-bottom: 0px;
.checklist-group {
margin-top: 1em;
font-weight: bold;
}
.checklist-item {
display: grid;
grid-template-columns: min-content 1fr;
align-items: center;
margin-top: 0.50em;
&.checked {
text-decoration: line-through;
color: #888;
}
input[type="checkbox"] {
margin-left: 0px;
margin-right: 8px;
-webkit-appearance: none;
appearance: none;
background-color: #fff;
margin: 0 8px 0 0;
font: inherit;
color: currentColor;
width: 1.25em;
height: 1.25em;
border: 0.15em solid currentColor;
border-radius: 0.15em;
transform: translateY(-0.075em);
display: grid;
place-content: center;
}
input[type="checkbox"].ok {
border: 0.15em solid #54b356;
}
input[type="checkbox"].ok::before {
box-shadow: inset 1em 1em #54b356;
}
input[type="checkbox"]::before {
content: "";
width: 0.70em;
height: 0.70em;
transform: scale(0);
transition: 120ms transform ease-in-out;
box-shadow: inset 1em 1em @checkbox_1;
}
input[type="checkbox"]:checked::before {
transform: scale(1);
}
label {
user-select: none;
}
}
}
/* ============================================================= * /* ============================================================= *
* Textarea replicates the height of an element expanding height * * Textarea replicates the height of an element expanding height *
* ============================================================= */ * ============================================================= */
@ -555,6 +639,7 @@ header {
"tree child-nodes" "tree child-nodes"
"tree name" "tree name"
"tree content" "tree content"
"tree checklist"
"tree files" "tree files"
"tree blank" "tree blank"
; ;
@ -565,6 +650,7 @@ header {
min-content /* child-nodes */ min-content /* child-nodes */
min-content /* name */ min-content /* name */
min-content /* content */ min-content /* content */
min-content /* checklist */
min-content /* files */ min-content /* files */
1fr; /* blank */ 1fr; /* blank */
color: #fff; color: #fff;
@ -597,6 +683,7 @@ header {
"child-nodes" "child-nodes"
"name" "name"
"content" "content"
"checklist"
"files" "files"
"blank" "blank"
; ;
@ -607,9 +694,14 @@ header {
min-content /* child-nodes */ min-content /* child-nodes */
min-content /* name */ min-content /* name */
min-content /* content */ min-content /* content */
min-content /* checklist */
min-content /* files */ min-content /* files */
1fr; /* blank */ 1fr; /* blank */
#tree { display: none } #tree { display: none }
#checklist {
padding: 16px;
}
}// }}} }// }}}
.layout-keys { .layout-keys {
display: grid; display: grid;
@ -681,7 +773,7 @@ header {
justify-self: start; justify-self: start;
} }
#file-section { #file-section, #checklist, #markdown {
width: calc(100% - 32px); width: calc(100% - 32px);
padding: 16px; padding: 16px;
margin-left: 16px; margin-left: 16px;

View File

@ -4,6 +4,11 @@
@accent_2: #ecbf00; @accent_2: #ecbf00;
@accent_3: #c84a37; @accent_3: #c84a37;
@header_1: #518048;
@header_2: #518048;
@checkbox_1: #666;
/* /*
@theme_gradient: linear-gradient(to right, #009fff, #ec2f4b); @theme_gradient: linear-gradient(to right, #009fff, #ec2f4b);
@theme_gradient: linear-gradient(to right, #f5af19, #f12711); @theme_gradient: linear-gradient(to right, #f5af19, #f12711);