From ee6869df55efe187bb525d94a3b898e72684dfbb Mon Sep 17 00:00:00 2001 From: m5x5 Date: Wed, 13 May 2026 01:23:30 +0200 Subject: [PATCH 1/2] a11y: replace table-as-layout markup with semantic elements The outline + data-content panes used //s with rowspan tricks for repeated subjects. - outlineObjectTD / outlinePredicateTD take an optional elementName argument so callers in non-table contexts (folder-pane list items, property-list
s) get a plain
instead of a stray
structures for content that wasn't tabular -- predicate/value lists, rdf:List members, and per-subject blocks. Screen readers announced them as data tables with rows and columns, which they weren't. - appendPropertyTRs (manager.js) now emits
with one
per predicate and one
per value, instead of
. - The object-cell expand affordance is now a native
/ with a lazy 'toggle' listener -- removes a custom expand-icon image and the associated event wiring. - rdf:List rendering (Collection branch) switches from to
    with browser-native marker numbering. - dataContentPane's per-subject blocks become
    s with a
    ; nested bnodes get a .nestedBnode class instead of being rendered as nested striped tables. - Fix Graph branch crash: paneRegistry.byName('dataContentPane') is not a registered name -- use 'dataContents' and fall back to a plain text node if the lookup still fails. - Surface swallowed errors: catch sites in manager.js and userInput.js now console.error(e) in addition to rendering the exception pane, so DevTools shows the stack trace. - manager.css: drop margin/border/padding hacks that were applied to
s and are no-ops on
/
; add .property-list grid layout and .rdf-collection / .data-content / .obj-disclosure rules. --- src/dataContentPane.js | 92 ++++---- src/outline/manager.css | 76 +++++- src/outline/manager.js | 486 ++++++++++++++++----------------------- src/outline/userInput.js | 2 + 4 files changed, 313 insertions(+), 343 deletions(-) diff --git a/src/dataContentPane.js b/src/dataContentPane.js index 749ebc75..9a72f656 100644 --- a/src/dataContentPane.js +++ b/src/dataContentPane.js @@ -45,7 +45,10 @@ export const dataContentPane = { statementsAsTables: function statementsAsTables (sts, context, initialRoots) { const myDocument = context.dom // const outliner = context.getOutliner(myDocument) - const rep = myDocument.createElement('table') + // The outer container groups one block per "root" subject. Each block holds + // a subject label and a
of its predicates/values. + const rep = myDocument.createElement('section') + rep.classList.add('data-content') const sz = $rdf.Serializer(context.session.store) const res = sz.rootSubjects(sts) let roots = res.roots @@ -57,10 +60,12 @@ export const dataContentPane = { const doneBnodes = {} // For preventing looping const referencedBnodes = {} // Bnodes which need to be named alas - // The property tree for a single subject or anonymous node + // The property tree for a single subject or anonymous node. Returns a + //
with one
per predicate followed by one + //
per value. Replaces the previous //
with rowspan. function propertyTree (subject) { - // print('Proprty tree for '+subject) - const rep = myDocument.createElement('table') + const rep = myDocument.createElement('dl') + rep.classList.add('property-list') let lastPred = null const sts = subjects[sz.toStr(subject)] // relevant statements if (!sts) { @@ -69,17 +74,10 @@ export const dataContentPane = { return rep } sts.sort() - let same = 0 - let predicateTD // The cell which holds the predicate - for (let i = 0; i < sts.length; i++) { - const st = sts[i] - const tr = myDocument.createElement('tr') + for (const st of sts) { if (st.predicate.uri !== lastPred) { - if (lastPred && same > 1) { - predicateTD.setAttribute('rowspan', '' + same) - } - predicateTD = myDocument.createElement('td') - predicateTD.setAttribute('class', 'pred') + const dt = myDocument.createElement('dt') + dt.classList.add('pred') const anchor = myDocument.createElement('a') anchor.setAttribute('href', st.predicate.uri) anchor.addEventListener( @@ -92,18 +90,15 @@ export const dataContentPane = { UI.utils.predicateLabelForXML(st.predicate) ) ) - predicateTD.appendChild(anchor) - tr.appendChild(predicateTD) + dt.appendChild(anchor) + rep.appendChild(dt) lastPred = st.predicate.uri - same = 0 } - same++ - const objectTD = myDocument.createElement('td') - objectTD.appendChild(objectTree(st.object)) - tr.appendChild(objectTD) - rep.appendChild(tr) + const dd = myDocument.createElement('dd') + dd.classList.add('obj') + dd.appendChild(objectTree(st.object)) + rep.appendChild(dd) } - if (lastPred && same > 1) predicateTD.setAttribute('rowspan', '' + same) return rep } @@ -150,26 +145,19 @@ export const dataContentPane = { return anchor } doneBnodes[obj.toNT()] = true // Flag to prevent infinite recursion in propertyTree - const newTable = propertyTree(obj) - doneBnodes[obj.toNT()] = newTable // Track where we mentioned it first - if ( - UI.utils.ancestor(newTable, 'TABLE') && - UI.utils.ancestor(newTable, 'TABLE').style.backgroundColor === - 'white' - ) { - newTable.style.backgroundColor = '#eee' - } else { - newTable.style.backgroundColor = 'white' - } - return newTable + const newTree = propertyTree(obj) + newTree.classList.add('nestedBnode') + doneBnodes[obj.toNT()] = newTree // Track where we mentioned it first + return newTree } case 'Collection': - res = myDocument.createElement('table') - res.setAttribute('class', 'collectionAsTables') - for (let i = 0; i < obj.elements.length; i++) { - const tr = myDocument.createElement('tr') - res.appendChild(tr) - tr.appendChild(objectTree(obj.elements[i])) + // rdf:List → semantic ordered list with browser-provided numbering. + res = myDocument.createElement('ol') + res.classList.add('rdf-collection') + for (const elt of obj.elements) { + const li = myDocument.createElement('li') + li.appendChild(objectTree(elt)) + res.appendChild(li) } return res case 'Graph': @@ -199,20 +187,22 @@ export const dataContentPane = { ) } for (let i = 0; i < roots.length; i++) { - const tr = myDocument.createElement('tr') - tr.setAttribute('style', `background-color: ${i % 2 === 0 ? '#f0f0f0' : 'white'};`) - rep.appendChild(tr) - const subjectTD = myDocument.createElement('td') - tr.appendChild(subjectTD) - const TDTree = myDocument.createElement('td') - tr.appendChild(TDTree) + const subjBlock = myDocument.createElement('section') + subjBlock.classList.add('data-content__subject') + // Alternating background as visual separator (was previously row striping). + if (i % 2 === 0) subjBlock.classList.add('data-content__subject--even') + rep.appendChild(subjBlock) + + const subjLabel = myDocument.createElement('div') + subjLabel.classList.add('data-content__subject-label') const root = roots[i] if (root.termType === 'BlankNode') { - subjectTD.appendChild(myDocument.createTextNode(UI.utils.label(root))) // Don't recurse! + subjLabel.appendChild(myDocument.createTextNode(UI.utils.label(root))) // Don't recurse! } else { - subjectTD.appendChild(objectTree(root)) // won't have tree + subjLabel.appendChild(objectTree(root)) // won't have tree } - TDTree.appendChild(propertyTree(root)) + subjBlock.appendChild(subjLabel) + subjBlock.appendChild(propertyTree(root)) } for (const bNT in referencedBnodes) { // Add number to refer to diff --git a/src/outline/manager.css b/src/outline/manager.css index c1da1ff7..375240c0 100644 --- a/src/outline/manager.css +++ b/src/outline/manager.css @@ -1,14 +1,73 @@ /* Styles extracted from manager.js */ .obj { - margin: 0.2em; border: none; - padding: 0; vertical-align: top; } -.pred, .pred.internal { - /* Add any specific styles for predicate TDs here */ +/* Real data tables (dataContentPane's internal property tree). + These are legitimate s, so just give them consistent inter-cell spacing. */ +.tableFullWidth, +.collectionAsTables { + border-collapse: separate; + border-spacing: var(--spacing-sm, 0.5rem); +} + +/* rdf:List rendering: ordered list using the browser's native numbering. */ +.rdf-collection { + margin: 0; + padding-left: 2em; /* room for the native marker */ +} +.rdf-collection > li { + padding: 0.1em 0; +} +.rdf-collection > li::marker { + color: var(--color-text-muted, #6B7280); +} + +/* Description list of a subject's predicates and values, emitted by appendPropertyTRs. + The two-column visual layout is purely presentational — semantically it stays a
. */ +.property-list { + display: grid; + grid-template-columns: minmax(8rem, max-content) 1fr; + gap: 0.35em var(--spacing-sm, 0.5rem); + margin: 0; + align-items: start; +} +.property-list > dt { + grid-column: 1; + font-weight: 500; + color: var(--color-text-muted, #6B7280); +} +.property-list > dd { + grid-column: 2; + margin: 0; + min-width: 0; /* allow long URIs to wrap inside the cell */ +} +.property-list > dd.property-more { + grid-column: 2; +} +.property-list > dd.property-more > details { + display: contents; +} +.property-list > dd.property-more > details > summary { + cursor: pointer; + color: var(--color-text-muted, #6B7280); + font-size: 0.9em; +} + +/* dataContentPane: per-subject blocks (was a striped
) */ +.data-content { + display: block; +} +.data-content__subject { + padding: 0.5em 0.75em; +} +.data-content__subject--even { + background-color: var(--color-row-alt, #f0f0f0); +} +.data-content .property-list { + margin: 0; } .iconTD { @@ -21,6 +80,7 @@ .labelTD { width: 100%; + padding: 0; } .paneIconTray { @@ -63,6 +123,14 @@ background: var(--color-background, #F8F9FB) !important; } +/* Native disclosure for the object cell of a predicate row */ +.obj-disclosure { display: inline-block; } +.obj-disclosure > summary { cursor: pointer; } +.obj-disclosure > .obj-expanded { + margin-top: 0.25em; + padding-left: 1em; +} + .placeholderTable { width: 100%; } diff --git a/src/outline/manager.js b/src/outline/manager.js index b57ef366..d1f5b35e 100644 --- a/src/outline/manager.js +++ b/src/outline/manager.js @@ -193,23 +193,29 @@ export default function (context) { obj, view, deleteNode, - statement + statement, + elementName, + source ) { - const td = dom.createElement('td') + const td = dom.createElement(elementName || 'td') td.classList.add('obj') + if (source) td.dataset.outlineSource = source td.setAttribute('notSelectable', 'false') - td.style.margin = '0.2em' if (!obj) { td.textContent = 'No object available.' return td } - td.style.border = 'none' - td.style.padding = '0' - td.style.verticalAlign = 'top' - const theClass = 'obj' - // set about and put 'expand' icon - if ( + if (kb.whether(obj, UI.ns.rdf('type'), UI.ns.link('Request'))) { + td.classList.add('undetermined') + } // @@? why-timbl + + if (!view) { + // view should be a function pointer + view = viewAsBoringDefault + } + + const isExpandable = obj.termType === 'NamedNode' || obj.termType === 'BlankNode' || (obj.termType === 'Literal' && @@ -217,27 +223,26 @@ export default function (context) { (obj.value.slice(0, 6) === 'ftp://' || obj.value.slice(0, 8) === 'https://' || obj.value.slice(0, 7) === 'http://')) - ) { - td.setAttribute('about', obj.toNT()) - td.appendChild( - UI.utils.AJARImage( - UI.icons.originalIconBase + 'tbl-expand-trans.png', - 'expand', - undefined, - dom - ) - ).addEventListener('click', expandMouseDownListener) - } - td.setAttribute('class', theClass) // this is how you find an object - if (kb.whether(obj, UI.ns.rdf('type'), UI.ns.link('Request'))) { - td.className = 'undetermined' - } // @@? why-timbl - if (!view) { - // view should be a function pointer - view = viewAsBoringDefault + if (isExpandable) { + // Use native
/ for disclosure of the inlined sub-subject + td.setAttribute('about', obj.toNT()) + const details = dom.createElement('details') + details.classList.add('obj-disclosure') + const summary = details.appendChild(dom.createElement('summary')) + summary.appendChild(view(obj)) + const expanded = details.appendChild(dom.createElement('div')) + expanded.classList.add('obj-expanded') + details.addEventListener('toggle', () => { + if (details.open && !expanded.firstChild) { + // Lazy: fetch+render the sub-subject's property table on first open. + outlineExpand(expanded, obj, {}) + } + }) + td.appendChild(details) + } else { + td.appendChild(view(obj)) } - td.appendChild(view(obj)) if (deleteNode) { appendRemoveIcon(td, obj, deleteNode) } @@ -258,9 +263,10 @@ export default function (context) { predicate, newTr, inverse, - internal + internal, + elementName ) { - const predicateTD = dom.createElement('TD') + const predicateTD = dom.createElement(elementName || 'TD') predicateTD.setAttribute('about', predicate.toNT()) predicateTD.setAttribute('class', internal ? 'pred internal' : 'pred') @@ -278,7 +284,7 @@ export default function (context) { lab = lab ? lab.slice(0, 1).toUpperCase() + lab.slice(1) : '...' // if (kb.statementsMatching(predicate,rdf('type'), UI.ns.link('Request')).length) predicateTD.className='undetermined'; - const labelTD = dom.createElement('TD') + const labelTD = dom.createElement('span') labelTD.classList.add('labelTD') labelTD.setAttribute('notSelectable', 'true') labelTD.appendChild(dom.createTextNode(lab)) @@ -588,7 +594,7 @@ export default function (context) { } if (containerHost) { - const OutlineView = document.createElement('table') + const OutlineView = document.createElement('div') OutlineView.id = 'OutlineView' OutlineView.classList.add('outline-view') OutlineView.setAttribute('aria-label', 'Resource browser') @@ -654,12 +660,12 @@ export default function (context) { async function expandedHeaderTR (subject, requiredPane, options) { async function renderPaneIconTray (td, options = {}) { const paneShownStyle = - 'width: 24px; border-radius: 0.5em; border-top: solid #222 1px; border-left: solid #222 0.1em; border-bottom: solid #eee 0.1em; border-right: solid #eee 0.1em; margin-left: 1em; padding: 3px; background-color: #ffd;' + 'width: 24px; border-radius: 0.5em; border-top: solid #222 1px; border-left: solid #222 0.1em; border-bottom: solid #eee 0.1em; border-right: solid #eee 0.1em; padding: 3px; background-color: #ffd;' const paneHiddenStyle = - 'width: 24px; border-radius: 0.5em; margin-left: 1em; padding: 3px' + 'width: 24px; border-radius: 0.5em; padding: 3px' const paneIconTray = td.appendChild(dom.createElement('nav')) paneIconTray.style = - 'display:flex; justify-content: flex-start; align-items: center;' + 'display:flex; justify-content: flex-start; align-items: center; gap: 1em; flex-wrap: wrap;' const relevantPanes = options.hideList ? [] @@ -700,14 +706,20 @@ export default function (context) { ico.addEventListener( 'click', function (event) { - let containingTable - // Find the containing table for this subject - for (containingTable = td; containingTable.parentNode; containingTable = containingTable.parentNode) { - if (containingTable.nodeName === 'TABLE') break - } - if (containingTable.nodeName !== 'TABLE') { + // Find the per-subject scaffold that holds [header, panes...]. + // propertyTable() now builds this as
, so + // we look for it first; fall back to the outer view / a real
+ // for any legacy callers. + let containingTable = td.closest('.tableFullWidth, #OutlineView, .outline-view, table') + if (!containingTable) { throw new Error('outline: internal error.') } + // Find the subject's header row (direct child of containingTable) so + // panes get inserted immediately below their subject, not at the end. + let subjectRow = td + while (subjectRow && subjectRow.parentNode !== containingTable) { + subjectRow = subjectRow.parentNode + } const removePanes = function (specific) { for (let d = containingTable.firstChild; d; d = d.nextSibling) { if (typeof d.pane !== 'undefined') { @@ -740,7 +752,9 @@ export default function (context) { try { paneDiv = pane.render(subject, context, options) } catch (e) { - // Easier debugging for pane developers + // Easier debugging for pane developers — log the Error + // object so DevTools applies source maps to the stack. + console.error(e) paneDiv = dom.createElement('div') paneDiv.setAttribute('class', 'exceptionPane') const pre = dom.createElement('pre') @@ -756,15 +770,18 @@ export default function (context) { ) { dom.getElementById('queryButton').removeAttribute('style') } - const second = containingTable.firstChild.nextSibling - const row = dom.createElement('tr') - const cell = row.appendChild(dom.createElement('td')) - cell.setAttribute('colspan', '2') - cell.style.textAlign = 'left' - cell.style.width = '100%' - cell.appendChild(paneDiv) - if (second) containingTable.insertBefore(row, second) - else containingTable.appendChild(row) + const row = dom.createElement('div') + row.style.textAlign = 'left' + row.style.width = '100%' + row.appendChild(paneDiv) + // Insert directly after the subject's header row so panes stay grouped + // with their subject (was: insertBefore second-child of the whole view). + const insertAfter = subjectRow || containingTable.firstChild + if (insertAfter && insertAfter.nextSibling) { + containingTable.insertBefore(row, insertAfter.nextSibling) + } else { + containingTable.appendChild(row) + } row.pane = pane row.paneButton = ico } @@ -806,22 +823,23 @@ export default function (context) { return paneIconTray } // renderPaneIconTray - // Body of expandedHeaderTR - const tr = dom.createElement('tr') + // Body of expandedHeaderTR. + // Despite the legacy name, this now returns a
(the outer scaffold is + // no longer a
) holding the pane-icon tray. + const tr = dom.createElement('div') if (options.hover) { // By default no hide till hover as community deems it confusing tr.setAttribute('class', 'hoverControl') } - const td = tr.appendChild(dom.createElement('td')) + const td = tr.appendChild(dom.createElement('div')) td.setAttribute( 'style', - 'margin: 0.2em; border: none; padding-top: 0; padding-bottom: 0; vertical-align: top;' + + 'margin: 0.2em;' + 'display:flex; justify-content: space-between; flex-direction: row;' + 'background-color: var(--color-background, #F8F9FB);' ) td.setAttribute('notSelectable', 'true') td.setAttribute('about', subject.toNT()) - td.setAttribute('colspan', '2') // Stuff at the right about the subject const header = td.appendChild(dom.createElement('div')) @@ -911,8 +929,13 @@ export default function (context) { // if (!pane) pane = panes.defaultPane; if (!table) { - // Create a new property table - table = dom.createElement('table') + // Create a new property scaffold. + // The outer element is a
, not a
: this scaffold only ever + // holds a header (pane-icon tray) and a single full-width pane content + // row, so it does not need
semantics. Real tabular data + // (predicate/object rows) is rendered by individual panes in their own + // inner
. + table = dom.createElement('div') table.classList.add('tableFullWidth') expandedHeaderTR(subject, pane, options).then(tr1 => { table.appendChild(tr1) @@ -923,7 +946,9 @@ export default function (context) { UI.log.info('outline: Rendering pane (1): ' + tr1.firstPane.name) paneDiv = tr1.firstPane.render(subject, context, options) } catch (e) { - // Easier debugging for pane developers + // Easier debugging for pane developers — log the Error object so + // DevTools applies source maps to the stack. + console.error(e) paneDiv = dom.createElement('div') paneDiv.setAttribute('class', 'exceptionPane') const pre = dom.createElement('pre') @@ -931,13 +956,11 @@ export default function (context) { pre.appendChild(dom.createTextNode(UI.utils.stackString(e))) } - const row = dom.createElement('tr') - const cell = row.appendChild(dom.createElement('td')) - cell.setAttribute('colspan', '2') - cell.style.textAlign = 'left' - cell.style.width = '100%' - cell.style.backgroundColor = 'var(--color-background, #F8F9FB)' - cell.appendChild(paneDiv) + const row = dom.createElement('div') + row.style.textAlign = 'left' + row.style.width = '100%' + row.style.backgroundColor = 'var(--color-background, #F8F9FB)' + row.appendChild(paneDiv) if ( tr1.firstPane.requireQueryButton && dom.getElementById('queryButton') @@ -972,14 +995,19 @@ export default function (context) { this.propertyTR = propertyTR // / ////////// Property list + // + // Renders the subject's outgoing triples as a
+ // containing one
per predicate and one
per object value, then + // appends that
to `parent`. Earlier this function appended orphan + //
s directly to `parent` (a
), which produced invalid markup. function appendPropertyTRs (parent, plist, inverse, predicateFilter) { - // UI.log.info('@appendPropertyTRs, 'this' is %s, dom is %s, '+ // Gives 'can't access dead object' - // 'thisOutline.document is %s', this, dom.location, thisOutline.document.location); - // UI.log.info('@appendPropertyTRs, dom is now ' + this.document.location); - // UI.log.info('@appendPropertyTRs, dom is now ' + thisOutline.document.location); UI.log.debug('Property list length = ' + plist.length) if (plist.length === 0) return '' - let sel, j, k + const dl = dom.createElement('dl') + dl.classList.add('property-list') + if (inverse) dl.classList.add('property-list--inverse') + parent.appendChild(dl) + let sel if (inverse) { sel = function (x) { return x.subject @@ -992,204 +1020,83 @@ export default function (context) { plist = plist.sort(UI.utils.RDFComparePredicateObject) } - const max = plist.length - for (j = 0; j < max; j++) { - // squishing together equivalent properties I think - let s = plist[j] - // if (s.object == parentSubject) continue; // that we knew - - // Avoid predicates from other panes - if (predicateFilter && !predicateFilter(s.predicate, inverse)) continue - - const tr = propertyTR(dom, s, inverse) - parent.appendChild(tr) - const predicateTD = tr.firstChild // we need to kludge the rowspan later - - let defaultpropview = views.defaults[s.predicate.uri] - - // LANGUAGE PREFERENCES WAS AVAILABLE WITH FF EXTENSION - get from elsewhere? + const langPref = outline.labeller.LanguagePreference + const MAX_BEFORE_OVERFLOW = 10 - let dups = 0 // How many rows have the same predicate, -1? - let langTagged = 0 // how many objects have language tags? - let myLang = 0 // Is there one I like? + const max = plist.length + let j = 0 + while (j < max) { + const s = plist[j] + if (predicateFilter && !predicateFilter(s.predicate, inverse)) { + j++ + continue + } - for ( - k = 0; - k + j < max && plist[j + k].predicate.sameTerm(s.predicate); - k++ - ) { - if (k > 0 && sel(plist[j + k]).sameTerm(sel(plist[j + k - 1]))) dups++ - if (sel(plist[j + k]).lang && outline.labeller.LanguagePreference) { - langTagged += 1 - if ( - sel(plist[j + k]).lang.indexOf( - outline.labeller.LanguagePreference - ) >= 0 - ) { - myLang++ - } + // Find the run of consecutive statements with the same predicate. + let runEnd = j + while (runEnd < max && plist[runEnd].predicate.sameTerm(s.predicate)) runEnd++ + const run = plist.slice(j, runEnd) + + // Emit the
for this predicate. + const dt = thisOutline.outlinePredicateTD(s.predicate, null, inverse, false, 'dt') + dt.AJAR_statement = s + dt.AJAR_inverse = inverse + dl.appendChild(dt) + + // Decide which values to show. Language preference: when every value is + // lang-tagged and at least one matches, show only the matching one(s). + let langTagged = 0 + const matching = [] + for (const st of run) { + if (sel(st).lang && langPref) { + langTagged++ + if (sel(st).lang.indexOf(langPref) >= 0) matching.push(st) } } + let valuesToRender + let firstTag = 'predicate-row' + if (langPref && langTagged === run.length && matching.length > 0) { + valuesToRender = matching + firstTag = 'lang-preferred' + } else { + // De-duplicate exact same-term object values + const seen = new Set() + valuesToRender = run.filter(st => { + const key = sel(st).toNT() + if (seen.has(key)) return false + seen.add(key) + return true + }) + } - /* Display only the one in the preferred language - ONLY in the case (currently) when all the values are tagged. - Then we treat them as alternatives. */ + const defaultpropview = views.defaults[s.predicate.uri] + const overflow = [] - if (myLang > 0 && langTagged === dups + 1) { - for (let k = j; k <= j + dups; k++) { - if ( - outline.labeller.LanguagePreference && - sel(plist[k]).lang.indexOf(outline.labeller.LanguagePreference) >= 0 - ) { - tr.appendChild( - thisOutline.outlineObjectTD( - sel(plist[k]), - defaultpropview, - undefined, - s - ) - ) - break - } + valuesToRender.forEach((st, idx) => { + const tag = idx === 0 ? firstTag : 'duplicate-pred-row' + const dd = thisOutline.outlineObjectTD( + sel(st), defaultpropview, undefined, st, 'dd', tag + ) + if (idx < MAX_BEFORE_OVERFLOW) { + dl.appendChild(dd) + } else { + overflow.push(dd) } - j += dups // extra push - continue - } - - tr.appendChild( - thisOutline.outlineObjectTD(sel(s), defaultpropview, undefined, s) - ) + }) - /* Note: showNobj shows between n to 2n objects. - * This is to prevent the case where you have a long list of objects - * shown, and dangling at the end is '1 more' (which is easily ignored) - * Therefore more objects are shown than hidden. - */ - - tr.showNobj = function (n) { - const predDups = k - dups - const show = 2 * n < predDups ? n : predDups - const showLaterArray = [] - if (predDups !== 1) { - predicateTD.setAttribute( - 'rowspan', - show === predDups ? predDups : n + 1 - ) - let l - if (show < predDups && show === 1) { - // what case is this... - predicateTD.setAttribute('rowspan', 2) - } - let displayed = 0 // The number of cells generated-1, - // all duplicate thing removed - for (l = 1; l < k; l++) { - // This detects the same things - if ( - !kb - .canon(sel(plist[j + l])) - .sameTerm(kb.canon(sel(plist[j + l - 1]))) - ) { - displayed++ - s = plist[j + l] - defaultpropview = views.defaults[s.predicate.uri] - const trObj = dom.createElement('tr') - trObj.style.colspan = '1' - trObj.appendChild( - thisOutline.outlineObjectTD( - sel(plist[j + l]), - defaultpropview, - undefined, - s - ) - ) - trObj.AJAR_statement = s - trObj.AJAR_inverse = inverse - parent.appendChild(trObj) - if (displayed >= show) { - trObj.style.display = 'none' - showLaterArray.push(trObj) - } - } else { - // ToDo: show all the data sources of this statement - UI.log.info('there are duplicates here: %s', plist[j + l - 1]) - } - } - // @@a quick fix on the messing problem. - if (show === predDups) { - predicateTD.setAttribute('rowspan', displayed + 1) - } - } // end of if (predDups!==1) - - if (show < predDups) { - // Add the x more
here - const moreTR = dom.createElement('tr') - const moreTD = moreTR.appendChild(dom.createElement('td')) - moreTD.setAttribute( - 'style', - 'margin: 0.2em; border: none; padding: 0; vertical-align: top;' - ) - moreTD.setAttribute('notSelectable', 'false') - if (predDups > n) { - // what is this for?? - const small = dom.createElement('a') - moreTD.appendChild(small) - - const predToggle = (function (f) { - return f(predicateTD, k, dups, n) - })(function (predicateTD, k, dups, n) { - return function (display) { - small.innerHTML = '' - if (display === 'none') { - small.appendChild( - UI.utils.AJARImage( - UI.icons.originalIconBase + 'tbl-more-trans.png', - 'more', - 'See all', - dom - ) - ) - small.appendChild( - dom.createTextNode(predDups - n + ' more...') - ) - predicateTD.setAttribute('rowspan', n + 1) - } else { - small.appendChild( - UI.utils.AJARImage( - UI.icons.originalIconBase + 'tbl-shrink.png', - '(less)', - undefined, - dom - ) - ) - predicateTD.setAttribute('rowspan', predDups + 1) - } - for (let i = 0; i < showLaterArray.length; i++) { - const trObj = showLaterArray[i] - trObj.style.display = display - } - } - }) // ??? - let current = 'none' - const toggleObj = function (event) { - predToggle(current) - current = current === 'none' ? '' : 'none' - if (event) event.stopPropagation() - return false // what is this for? - } - toggleObj() - small.addEventListener('click', toggleObj, false) - } // if(predDups>n) - parent.appendChild(moreTR) - } // if - } // tr.showNobj - - tr.showAllobj = function () { - tr.showNobj(k - dups) + // If there's overflow, wrap the extras in a
/ "+ N more" + // disclosure so the user can opt to see them all. + if (overflow.length > 0) { + const moreDd = dom.createElement('dd') + moreDd.classList.add('property-more') + const details = moreDd.appendChild(dom.createElement('details')) + const summary = details.appendChild(dom.createElement('summary')) + summary.textContent = '+ ' + overflow.length + ' more' + for (const dd of overflow) details.appendChild(dd) + dl.appendChild(moreDd) } - tr.showNobj(10) - - j += k - 1 // extra push + j = runEnd } } // appendPropertyTRs @@ -1202,7 +1109,7 @@ export default function (context) { global.termWidget = termWidget termWidget.construct = function (dom) { dom = dom || document - const td = dom.createElement('TD') + const td = dom.createElement('span') td.setAttribute( 'style', 'margin: 0.2em; border: none; padding: 0; vertical-align: top;' @@ -2262,7 +2169,7 @@ export default function (context) { deleteNode = level.parentNode } thisOutline.replaceTD( - thisOutline.outlineObjectTD(subject, myview, deleteNode, statement), + thisOutline.outlineObjectTD(subject, myview, deleteNode, statement, undefined, 'collapse-replace'), level ) } // outlineCollapse @@ -2342,11 +2249,11 @@ export default function (context) { } function GotoSubjectDefault () { - const tr = dom.createElement('TR') - tr.style.verticalAlign = 'top' - table.appendChild(tr) - const td = thisOutline.outlineObjectTD(subject, undefined, tr) - tr.appendChild(td) + const row = dom.createElement('span') + row.classList.add('subject-row') + table.appendChild(row) + const td = thisOutline.outlineObjectTD(subject, undefined, row, undefined, 'div', 'subject-cell') + row.appendChild(td) return td } @@ -2456,31 +2363,34 @@ export default function (context) { rep.appendChild(dom.createTextNode(UI.utils.label(obj))) } } else if (obj.termType === 'Collection') { - // obj.elements is an array of the elements in the collection - rep = dom.createElement('table') - rep.classList.add('tableFullWidth') + // An rdf:List is a one-dimensional ordered sequence.
    /
  1. is the + // correct element: the browser provides automatic numbering, and + // assistive tech announces it as "list, N items" with positional cues. + rep = dom.createElement('ol') + rep.classList.add('rdf-collection') rep.setAttribute('about', obj.toNT()) - /* Not sure which looks best -- with or without. I think without - - var tr = rep.appendChild(document.createElement('tr')); - tr.appendChild(document.createTextNode( - obj.elements.length ? '(' + obj.elements.length+')' : '(none)')); - */ - for (let i = 0; i < obj.elements.length; i++) { - const elt = obj.elements[i] - const row = rep.appendChild(dom.createElement('tr')) - const numcell = row.appendChild(dom.createElement('td')) - numcell.classList.add('obj') - numcell.setAttribute('notSelectable', 'false') - numcell.setAttribute('about', obj.toNT()) - numcell.innerHTML = i + 1 + ')' - row.appendChild(thisOutline.outlineObjectTD(elt)) + for (const elt of obj.elements) { + const li = rep.appendChild(dom.createElement('li')) + li.setAttribute('about', obj.toNT()) + li.appendChild( + thisOutline.outlineObjectTD( + elt, undefined, undefined, undefined, 'div', 'collection-element' + ) + ) } } else if (obj.termType === 'Graph') { - rep = paneRegistry - .byName('dataContentPane') - .statementsAsTables(obj.statements, context) - rep.setAttribute('class', 'nestedFormula') + // The pane is registered as 'dataContents' (not 'dataContentPane'). + // Fall back to a plain text label if the lookup somehow fails so we + // don't crash the surrounding render. + const dataContents = paneRegistry.byName('dataContents') + if (dataContents && typeof dataContents.statementsAsTables === 'function') { + rep = dataContents.statementsAsTables(obj.statements, context) + rep.setAttribute('class', 'nestedFormula') + } else { + UI.log.warn('viewAsBoringDefault: dataContents pane not available for Graph value') + rep = dom.createElement('span') + rep.textContent = '[graph: ' + obj.statements.length + ' statement(s)]' + } } else { UI.log.error('Object ' + obj + ' has unknown term type: ' + obj.termType) rep = dom.createTextNode('[unknownTermType:' + obj.termType + ']') diff --git a/src/outline/userInput.js b/src/outline/userInput.js index e6a7af47..fa670ec2 100644 --- a/src/outline/userInput.js +++ b/src/outline/userInput.js @@ -900,6 +900,7 @@ export function UserInput (outline) { } }) } catch (e) { + console.error(e) UI.log.error( 'Exception trying to insert statement ' + insertTr.AJAR_statement + @@ -2327,6 +2328,7 @@ export function UserInput (outline) { }) } catch (e) { // outline.UserInput.deleteTriple(newTd,true); + console.error(e) UI.log.error( 'userinput.js (object): exception trying to insert statement ' + s + From 050a3a39ad9347ccc64d546f9c7a657dd10f37f4 Mon Sep 17 00:00:00 2001 From: m5x5 Date: Wed, 13 May 2026 23:27:09 +0200 Subject: [PATCH 2/2] Restore WIP: switch pane icons to lucide / inline SVG data URIs Ported the pre-merge stash onto the new TypeScript pane files brought in by origin/post-milestone3m. Lucide replaces the iconBase PNG/SVG URLs for the simple cases; dataContentPane and internalPane keep their custom inline SVG (RDF logo and gear) as data URIs. Resolved unmerged paths: - RDFXMLPane / dataContentPane / form/pane / imagePane / n3Pane: modify-vs-delete (upstream renamed .js -> .ts); ported icon swap to the new .ts files and removed the obsolete .js. - defaultPane.ts / internal/internalPane.ts / schedule/schedulePane.ts: content conflicts; kept upstream TS structure and grafted the lucide imports + icon strings on top. --- src/RDFXMLPane.ts | 3 +- src/attach/attachPane.js | 3 +- src/audio/audioPane.js | 3 +- src/classInstancePane.js | 3 +- src/dashboard/basicPreferences.ts | 5 +- src/dashboard/dashboardPane.ts | 5 +- src/dataContentPane.ts | 13 +- src/defaultPane.ts | 5 +- src/dokieli/dokieliPane.js | 3 +- src/form/pane.ts | 3 +- src/home/homePane.ts | 5 +- src/humanReadablePane.ts | 26 ++-- src/icons/dashboard.svg | 16 +-- src/icons/friends.svg | 15 +- src/icons/lucide.ts | 48 +++++++ src/icons/person.svg | 11 +- src/icons/signup.png | Bin 337 -> 0 bytes src/icons/signup.svg | 7 + src/imagePane.ts | 3 +- src/internal/internalPane.ts | 17 ++- src/mainPage/header.ts | 4 +- src/microblogPane/microblogPane.js | 3 +- src/n3Pane.ts | 3 +- src/outline/manager.js | 9 +- src/outline/outlineIcons.js | 131 +++++++----------- src/pad/padPane.ts | 6 +- src/playlist/playlistPane.js | 3 +- src/registerPanes.js | 18 +++ src/schedule/schedulePane.ts | 9 +- src/sharing/sharingPane.ts | 14 +- src/slideshow/slideshowPane.js | 3 +- src/social/socialPane.ts | 5 +- src/tabbed/tabbedPane.ts | 5 +- src/tableViewPane.js | 3 +- src/transaction/pane.js | 4 +- src/transaction/period.js | 3 +- src/trip/tripPane.js | 3 +- .../trustedApplications.view.ts | 5 +- src/ui/pane.js | 4 +- src/video/videoPane.js | 3 +- 40 files changed, 267 insertions(+), 165 deletions(-) create mode 100644 src/icons/lucide.ts delete mode 100644 src/icons/signup.png create mode 100644 src/icons/signup.svg diff --git a/src/RDFXMLPane.ts b/src/RDFXMLPane.ts index cee48c77..a9252c9e 100644 --- a/src/RDFXMLPane.ts +++ b/src/RDFXMLPane.ts @@ -10,6 +10,7 @@ import * as $rdf from 'rdflib' import type { DataBrowserContext, RenderEnvironment } from 'pane-registry' import type { NamedNode, Statement } from 'rdflib' import './RDFXMLPane.css' +import { lucideIcons } from './icons/lucide' const ns = UI.ns @@ -46,7 +47,7 @@ function trimLeadingIndent (line: string): string { } export const RDFXMLPane: RDFXMLPaneDefinition = { - icon: UI.icons.originalIconBase + '22-text-xml4.png', + icon: lucideIcons.code, name: 'RDFXML', diff --git a/src/attach/attachPane.js b/src/attach/attachPane.js index 53631b5a..88d37c9f 100644 --- a/src/attach/attachPane.js +++ b/src/attach/attachPane.js @@ -10,9 +10,10 @@ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from '../icons/lucide' export default { - icon: UI.icons.iconBase + 'noun_25830.svg', // noun_25830 + icon: lucideIcons.paperclip, name: 'attachments', diff --git a/src/audio/audioPane.js b/src/audio/audioPane.js index 43fd1ddc..82e2cc17 100644 --- a/src/audio/audioPane.js +++ b/src/audio/audioPane.js @@ -3,10 +3,11 @@ */ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns export default { - icon: UI.icons.iconBase + 'noun_534313.svg', + icon: lucideIcons.music2, name: 'audio', diff --git a/src/classInstancePane.js b/src/classInstancePane.js index 7bb15e07..46821c16 100644 --- a/src/classInstancePane.js +++ b/src/classInstancePane.js @@ -5,11 +5,12 @@ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from './icons/lucide' const ns = UI.ns export const classInstancePane = { - icon: UI.icons.originalIconBase + 'tango/22-folder-open.png', + icon: lucideIcons.folderOpen, name: 'classInstance', diff --git a/src/dashboard/basicPreferences.ts b/src/dashboard/basicPreferences.ts index 2b40e1b1..5ddd0d89 100644 --- a/src/dashboard/basicPreferences.ts +++ b/src/dashboard/basicPreferences.ts @@ -1,11 +1,12 @@ import { PaneDefinition } from 'pane-registry' import { IndexedFormula, NamedNode, parse, Store } from 'rdflib' -import { icons, login, ns, widgets } from 'solid-ui' +import { login, ns, widgets } from 'solid-ui' import ontologyData from './ontologyData.ttl' import preferencesFormText from './preferencesFormText.ttl' +import { lucideIcons } from '../icons/lucide' export const basicPreferencesPane: PaneDefinition = { - icon: icons.iconBase + 'noun_Sliders_341315_000000.svg', + icon: lucideIcons.slidersHorizontal, name: 'basicPreferences', label: _subject => { return null diff --git a/src/dashboard/dashboardPane.ts b/src/dashboard/dashboardPane.ts index 0e679f38..ac0e84fc 100644 --- a/src/dashboard/dashboardPane.ts +++ b/src/dashboard/dashboardPane.ts @@ -1,11 +1,12 @@ -import { icons } from 'solid-ui' +// icons import removed — using lucideIcons import { authn, authSession, store } from 'solid-logic' import { Fetcher, NamedNode } from 'rdflib' import { generateHomepage } from './homepage' import { DataBrowserContext, PaneDefinition } from 'pane-registry' +import { lucideIcons } from '../icons/lucide' export const dashboardPane: PaneDefinition = { - icon: icons.iconBase + 'noun_547570.svg', + icon: lucideIcons.layoutDashboard, name: 'dashboard', label: subject => { if (subject.termType === 'NamedNode' && subject.uri === subject.site().uri) { diff --git a/src/dataContentPane.ts b/src/dataContentPane.ts index 93269146..cdac1fc3 100644 --- a/src/dataContentPane.ts +++ b/src/dataContentPane.ts @@ -23,6 +23,17 @@ import './dataContentPane.css' const ns = UI.ns +// RDF icon — https://www.svgrepo.com/show/362270/rdf.svg (public domain via SVG Repo Mixer) +const RDF_ICON = + 'data:image/svg+xml;utf8,' + + encodeURIComponent( + // Tightened viewBox: original artwork bbox is x≈129 y≈227 w≈741 h≈800. + // Centring it in an 800×800 square gives a balanced icon with ~30px padding. + '' + + '' + + '' + ) + type SubjectTerm = NamedNode | BlankNode type ObjectTerm = Statement['object'] | Formula @@ -41,7 +52,7 @@ type DataContentPaneLike = { } export const dataContentPane = { - icon: UI.icons.originalIconBase + 'rdf_flyer.24.gif', + icon: RDF_ICON, name: 'dataContents', diff --git a/src/defaultPane.ts b/src/defaultPane.ts index 98a5a466..48c7cc60 100644 --- a/src/defaultPane.ts +++ b/src/defaultPane.ts @@ -10,6 +10,7 @@ import * as $rdf from 'rdflib' import type { DataBrowserContext, RenderEnvironment } from 'pane-registry' import type { BlankNode, Literal, NamedNode, Statement } from 'rdflib' import './defaultPane.css' +import { lucideIcons } from './icons/lucide' const ns = UI.ns /* Types were generated by Generative AI (GPT-5.4 in GitHub Copilot) based on the following prompt: @@ -37,7 +38,7 @@ type DefaultPaneOutliner = { } export const defaultPane: DefaultPaneDefinition = { - icon: UI.icons.originalIconBase + 'about.png', + icon: lucideIcons.info, name: 'default', @@ -120,7 +121,7 @@ export const defaultPane: DefaultPaneDefinition = { holdingTd.setAttribute('colspan', '2') holdingTd.setAttribute('notSelectable', 'true') const img = dom.createElement('img') - img.src = UI.icons.originalIconBase + 'tango/22-list-add-new.png' + img.src = lucideIcons.plus // was tango/22-list-add-new.png img.addEventListener('click', function addNewTripleIconMouseDownListener ( e ) { diff --git a/src/dokieli/dokieliPane.js b/src/dokieli/dokieliPane.js index 46cf0488..2a82a63c 100644 --- a/src/dokieli/dokieliPane.js +++ b/src/dokieli/dokieliPane.js @@ -12,9 +12,10 @@ import * as mime from 'mime-types' // const DOKIELI_TEMPLATE_URI = 'https://dokie.li/new' // Copy to make new dok import DOKIELI_TEMPLATE from './new.js' // Distributed with this library +import { lucideIcons } from '../icons/lucide' export default { - icon: UI.icons.iconBase + 'dokieli-logo.png', // @@ improve? more like doccument? + icon: lucideIcons.filePen, name: 'Dokieli', diff --git a/src/form/pane.ts b/src/form/pane.ts index d79d67a1..81fdca19 100644 --- a/src/form/pane.ts +++ b/src/form/pane.ts @@ -10,6 +10,7 @@ import type { DataBrowserContext, PaneDefinition } from 'pane-registry' import type { RenderEnvironment } from 'pane-registry' import type { NamedNode, Statement } from 'rdflib' import './formPane.css' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns type WorkspaceSelectionDetails = { @@ -80,7 +81,7 @@ function tagMobileTextareaRows (renderedForm: HTMLElement): void { } export const formPane: PaneDefinition = { - icon: UI.icons.iconBase + 'noun_122196.svg', + icon: lucideIcons.formInput, name: 'form', diff --git a/src/home/homePane.ts b/src/home/homePane.ts index afa4d72f..2ac32326 100644 --- a/src/home/homePane.ts +++ b/src/home/homePane.ts @@ -11,11 +11,12 @@ import { DataBrowserContext, PaneDefinition } from 'pane-registry' import { NamedNode } from 'rdflib' import { authn } from 'solid-logic' -import { create, icons, login, ns } from 'solid-ui' +import { create, login, ns } from 'solid-ui' import type { CreateContext } from 'solid-ui' +import { lucideIcons } from '../icons/lucide' const HomePaneSource: PaneDefinition = { - icon: icons.iconBase + 'noun_547570.svg', // noun_25830 + icon: lucideIcons.house, global: true, diff --git a/src/humanReadablePane.ts b/src/humanReadablePane.ts index 2e4bbd23..11d4c2b0 100644 --- a/src/humanReadablePane.ts +++ b/src/humanReadablePane.ts @@ -4,6 +4,7 @@ ** This is for peeking at a page, because the user might not want to leave the data browser. */ import { icons, ns } from 'solid-ui' +import { lucideIcons } from './icons/lucide' import { Util } from 'rdflib' import { marked } from 'marked' import DOMPurify from 'dompurify' @@ -47,7 +48,14 @@ const humanReadablePane: HumanReadablePaneDefinition = { icon: function (subject: NamedNode, context: DataBrowserContext): HumanReadableIcon { // Markdown files detected by extension if (subject && isMarkdownFile(subject.uri)) { - return icons.iconBase + 'markdown.svg' + // lucide file-text — https://lucide.dev/icons/file-text (ISC license) + return 'data:image/svg+xml;utf8,' + encodeURIComponent( + '' + + '' + + '' + + '' + + '' + ) } // Dokieli files detected by content check @@ -57,9 +65,9 @@ const humanReadablePane: HumanReadablePaneDefinition = { // Check cache from previous detection const cachedResult = dokieliCache.get(subject.uri) if (cachedResult === 'dokieli') { - return icons.iconBase + 'dokieli-logo.png' + return lucideIcons.filePen /* was dokieli-logo.png */ } else if (cachedResult === 'html') { - return icons.originalIconBase + 'tango/22-text-x-generic.png' + return lucideIcons.info /* was tango/22-text-x-generic.png — generic HTML doc */ } // Check if content already fetched (synchronous) @@ -70,8 +78,8 @@ const humanReadablePane: HumanReadablePaneDefinition = { text.includes('dokieli.css') dokieliCache.set(subject.uri, isDokieli ? 'dokieli' : 'html') return isDokieli - ? icons.iconBase + 'dokieli-logo.png' - : icons.originalIconBase + 'tango/22-text-x-generic.png' + ? lucideIcons.filePen /* was dokieli-logo.png */ + : lucideIcons.info /* was tango/22-text-x-generic.png — generic HTML doc */ } // Content not yet fetched - return a promise (async detection) @@ -86,18 +94,18 @@ const humanReadablePane: HumanReadablePaneDefinition = { text.includes('dokieli.css') dokieliCache.set(subject.uri, isDokieli ? 'dokieli' : 'html') return isDokieli - ? icons.iconBase + 'dokieli-logo.png' - : icons.originalIconBase + 'tango/22-text-x-generic.png' + ? lucideIcons.filePen /* was dokieli-logo.png */ + : lucideIcons.info /* was tango/22-text-x-generic.png — generic HTML doc */ }) .catch(() => { dokieliCache.set(subject.uri, 'html') - return icons.originalIconBase + 'tango/22-text-x-generic.png' + return lucideIcons.info /* was tango/22-text-x-generic.png — generic HTML doc */ }) } } // Default for all other human-readable content - return icons.originalIconBase + 'tango/22-text-x-generic.png' + return lucideIcons.info /* was tango/22-text-x-generic.png — generic HTML doc */ }, name: 'humanReadable', diff --git a/src/icons/dashboard.svg b/src/icons/dashboard.svg index 19c40ade..9078804a 100644 --- a/src/icons/dashboard.svg +++ b/src/icons/dashboard.svg @@ -1,9 +1,7 @@ - - - - - - \ No newline at end of file + + + + + + + diff --git a/src/icons/friends.svg b/src/icons/friends.svg index b6227721..e34a6714 100644 --- a/src/icons/friends.svg +++ b/src/icons/friends.svg @@ -1,9 +1,6 @@ - - - - - - \ No newline at end of file + + + + + + diff --git a/src/icons/lucide.ts b/src/icons/lucide.ts new file mode 100644 index 00000000..6a101886 --- /dev/null +++ b/src/icons/lucide.ts @@ -0,0 +1,48 @@ +// Lucide icon data URIs. Each SVG uses stroke="currentColor" so it inherits text colour. +// Generated from lucide-static (ISC license). + +export const lucideIcons = { + folderOpen: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-folder-open%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22m6%2014%201.5-2.9A2%202%200%200%201%209.24%2010H20a2%202%200%200%201%201.94%202.5l-1.54%206a2%202%200%200%201-1.95%201.5H4a2%202%200%200%201-2-2V5a2%202%200%200%201%202-2h3.9a2%202%200%200%201%201.69.9l.81%201.2a2%202%200%200%200%201.67.9H18a2%202%200%200%201%202%202v2%22%20%2F%3E%20%3C%2Fsvg%3E", + image: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-image%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Crect%20width%3D%2218%22%20height%3D%2218%22%20x%3D%223%22%20y%3D%223%22%20rx%3D%222%22%20ry%3D%222%22%20%2F%3E%20%3Ccircle%20cx%3D%229%22%20cy%3D%229%22%20r%3D%222%22%20%2F%3E%20%3Cpath%20d%3D%22m21%2015-3.086-3.086a2%202%200%200%200-2.828%200L6%2021%22%20%2F%3E%20%3C%2Fsvg%3E", + table: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-table%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M12%203v18%22%20%2F%3E%20%3Crect%20width%3D%2218%22%20height%3D%2218%22%20x%3D%223%22%20y%3D%223%22%20rx%3D%222%22%20%2F%3E%20%3Cpath%20d%3D%22M3%209h18%22%20%2F%3E%20%3Cpath%20d%3D%22M3%2015h18%22%20%2F%3E%20%3C%2Fsvg%3E", + video: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-video%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22m16%2013%205.223%203.482a.5.5%200%200%200%20.777-.416V7.87a.5.5%200%200%200-.752-.432L16%2010.5%22%20%2F%3E%20%3Crect%20x%3D%222%22%20y%3D%226%22%20width%3D%2214%22%20height%3D%2212%22%20rx%3D%222%22%20%2F%3E%20%3C%2Fsvg%3E", + code: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-code%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22m16%2018%206-6-6-6%22%20%2F%3E%20%3Cpath%20d%3D%22m8%206-6%206%206%206%22%20%2F%3E%20%3C%2Fsvg%3E", + braces: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-braces%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M8%203H7a2%202%200%200%200-2%202v5a2%202%200%200%201-2%202%202%202%200%200%201%202%202v5c0%201.1.9%202%202%202h1%22%20%2F%3E%20%3Cpath%20d%3D%22M16%2021h1a2%202%200%200%200%202-2v-5c0-1.1.9-2%202-2a2%202%200%200%201-2-2V5a2%202%200%200%200-2-2h-1%22%20%2F%3E%20%3C%2Fsvg%3E", + info: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-info%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%20%3Cpath%20d%3D%22M12%2016v-4%22%20%2F%3E%20%3Cpath%20d%3D%22M12%208h.01%22%20%2F%3E%20%3C%2Fsvg%3E", + house: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-house%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M15%2021v-8a1%201%200%200%200-1-1h-4a1%201%200%200%200-1%201v8%22%20%2F%3E%20%3Cpath%20d%3D%22M3%2010a2%202%200%200%201%20.709-1.528l7-6a2%202%200%200%201%202.582%200l7%206A2%202%200%200%201%2021%2010v9a2%202%200%200%201-2%202H5a2%202%200%200%201-2-2z%22%20%2F%3E%20%3C%2Fsvg%3E", + calendarRange: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-calendar-range%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Crect%20width%3D%2218%22%20height%3D%2218%22%20x%3D%223%22%20y%3D%224%22%20rx%3D%222%22%20%2F%3E%20%3Cpath%20d%3D%22M16%202v4%22%20%2F%3E%20%3Cpath%20d%3D%22M3%2010h18%22%20%2F%3E%20%3Cpath%20d%3D%22M8%202v4%22%20%2F%3E%20%3Cpath%20d%3D%22M17%2014h-6%22%20%2F%3E%20%3Cpath%20d%3D%22M13%2018H7%22%20%2F%3E%20%3Cpath%20d%3D%22M7%2014h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M17%2018h.01%22%20%2F%3E%20%3C%2Fsvg%3E", + wallet: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-wallet%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M19%207V4a1%201%200%200%200-1-1H5a2%202%200%200%200%200%204h15a1%201%200%200%201%201%201v4h-3a2%202%200%200%200%200%204h3a1%201%200%200%200%201-1v-2a1%201%200%200%200-1-1%22%20%2F%3E%20%3Cpath%20d%3D%22M3%205v14a2%202%200%200%200%202%202h15a1%201%200%200%200%201-1v-4%22%20%2F%3E%20%3C%2Fsvg%3E", + formInput: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-form-input%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Crect%20width%3D%2220%22%20height%3D%2212%22%20x%3D%222%22%20y%3D%226%22%20rx%3D%222%22%20%2F%3E%20%3Cpath%20d%3D%22M12%2012h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M17%2012h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M7%2012h.01%22%20%2F%3E%20%3C%2Fsvg%3E", + listMusic: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-list-music%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M16%205H3%22%20%2F%3E%20%3Cpath%20d%3D%22M11%2012H3%22%20%2F%3E%20%3Cpath%20d%3D%22M11%2019H3%22%20%2F%3E%20%3Cpath%20d%3D%22M21%2016V5%22%20%2F%3E%20%3Ccircle%20cx%3D%2218%22%20cy%3D%2216%22%20r%3D%223%22%20%2F%3E%20%3C%2Fsvg%3E", + wand: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-wand%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M15%204V2%22%20%2F%3E%20%3Cpath%20d%3D%22M15%2016v-2%22%20%2F%3E%20%3Cpath%20d%3D%22M8%209h2%22%20%2F%3E%20%3Cpath%20d%3D%22M20%209h2%22%20%2F%3E%20%3Cpath%20d%3D%22M17.8%2011.8%2019%2013%22%20%2F%3E%20%3Cpath%20d%3D%22M15%209h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M17.8%206.2%2019%205%22%20%2F%3E%20%3Cpath%20d%3D%22m3%2021%209-9%22%20%2F%3E%20%3Cpath%20d%3D%22M12.2%206.2%2011%205%22%20%2F%3E%20%3C%2Fsvg%3E", + messageSquareText: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-message-square-text%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M22%2017a2%202%200%200%201-2%202H6.828a2%202%200%200%200-1.414.586l-2.202%202.202A.71.71%200%200%201%202%2021.286V5a2%202%200%200%201%202-2h16a2%202%200%200%201%202%202z%22%20%2F%3E%20%3Cpath%20d%3D%22M7%2011h10%22%20%2F%3E%20%3Cpath%20d%3D%22M7%2015h6%22%20%2F%3E%20%3Cpath%20d%3D%22M7%207h8%22%20%2F%3E%20%3C%2Fsvg%3E", + calendarDays: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-calendar-days%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M8%202v4%22%20%2F%3E%20%3Cpath%20d%3D%22M16%202v4%22%20%2F%3E%20%3Crect%20width%3D%2218%22%20height%3D%2218%22%20x%3D%223%22%20y%3D%224%22%20rx%3D%222%22%20%2F%3E%20%3Cpath%20d%3D%22M3%2010h18%22%20%2F%3E%20%3Cpath%20d%3D%22M8%2014h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M12%2014h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M16%2014h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M8%2018h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M12%2018h.01%22%20%2F%3E%20%3Cpath%20d%3D%22M16%2018h.01%22%20%2F%3E%20%3C%2Fsvg%3E", + notebookPen: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-notebook-pen%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M13.4%202H6a2%202%200%200%200-2%202v16a2%202%200%200%200%202%202h12a2%202%200%200%200%202-2v-7.4%22%20%2F%3E%20%3Cpath%20d%3D%22M2%206h4%22%20%2F%3E%20%3Cpath%20d%3D%22M2%2010h4%22%20%2F%3E%20%3Cpath%20d%3D%22M2%2014h4%22%20%2F%3E%20%3Cpath%20d%3D%22M2%2018h4%22%20%2F%3E%20%3Cpath%20d%3D%22M21.378%205.626a1%201%200%201%200-3.004-3.004l-5.01%205.012a2%202%200%200%200-.506.854l-.837%202.87a.5.5%200%200%200%20.62.62l2.87-.837a2%202%200%200%200%20.854-.506z%22%20%2F%3E%20%3C%2Fsvg%3E", + layoutDashboard: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-layout-dashboard%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Crect%20width%3D%227%22%20height%3D%229%22%20x%3D%223%22%20y%3D%223%22%20rx%3D%221%22%20%2F%3E%20%3Crect%20width%3D%227%22%20height%3D%225%22%20x%3D%2214%22%20y%3D%223%22%20rx%3D%221%22%20%2F%3E%20%3Crect%20width%3D%227%22%20height%3D%229%22%20x%3D%2214%22%20y%3D%2212%22%20rx%3D%221%22%20%2F%3E%20%3Crect%20width%3D%227%22%20height%3D%225%22%20x%3D%223%22%20y%3D%2216%22%20rx%3D%221%22%20%2F%3E%20%3C%2Fsvg%3E", + slidersHorizontal: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-sliders-horizontal%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M10%205H3%22%20%2F%3E%20%3Cpath%20d%3D%22M12%2019H3%22%20%2F%3E%20%3Cpath%20d%3D%22M14%203v4%22%20%2F%3E%20%3Cpath%20d%3D%22M16%2017v4%22%20%2F%3E%20%3Cpath%20d%3D%22M21%2012h-9%22%20%2F%3E%20%3Cpath%20d%3D%22M21%2019h-5%22%20%2F%3E%20%3Cpath%20d%3D%22M21%205h-7%22%20%2F%3E%20%3Cpath%20d%3D%22M8%2010v4%22%20%2F%3E%20%3Cpath%20d%3D%22M8%2012H3%22%20%2F%3E%20%3C%2Fsvg%3E", + usersRound: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-users-round%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M18%2021a8%208%200%200%200-16%200%22%20%2F%3E%20%3Ccircle%20cx%3D%2210%22%20cy%3D%228%22%20r%3D%225%22%20%2F%3E%20%3Cpath%20d%3D%22M22%2020c0-3.37-2-6.5-4-8a5%205%200%200%200-.45-8.3%22%20%2F%3E%20%3C%2Fsvg%3E", + appWindow: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-app-window%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Crect%20x%3D%222%22%20y%3D%224%22%20width%3D%2220%22%20height%3D%2216%22%20rx%3D%222%22%20%2F%3E%20%3Cpath%20d%3D%22M10%204v4%22%20%2F%3E%20%3Cpath%20d%3D%22M2%208h20%22%20%2F%3E%20%3Cpath%20d%3D%22M6%204v4%22%20%2F%3E%20%3C%2Fsvg%3E", + music2: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-music-2%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Ccircle%20cx%3D%228%22%20cy%3D%2218%22%20r%3D%224%22%20%2F%3E%20%3Cpath%20d%3D%22M12%2018V2l7%204%22%20%2F%3E%20%3C%2Fsvg%3E", + map: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-map%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M14.106%205.553a2%202%200%200%200%201.788%200l3.659-1.83A1%201%200%200%201%2021%204.619v12.764a1%201%200%200%201-.553.894l-4.553%202.277a2%202%200%200%201-1.788%200l-4.212-2.106a2%202%200%200%200-1.788%200l-3.659%201.83A1%201%200%200%201%203%2019.381V6.618a1%201%200%200%201%20.553-.894l4.553-2.277a2%202%200%200%201%201.788%200z%22%20%2F%3E%20%3Cpath%20d%3D%22M15%205.764v15%22%20%2F%3E%20%3Cpath%20d%3D%22M9%203.236v15%22%20%2F%3E%20%3C%2Fsvg%3E", + presentation: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-presentation%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M2%203h20%22%20%2F%3E%20%3Cpath%20d%3D%22M21%203v11a2%202%200%200%201-2%202H5a2%202%200%200%201-2-2V3%22%20%2F%3E%20%3Cpath%20d%3D%22m7%2021%205-5%205%205%22%20%2F%3E%20%3C%2Fsvg%3E", + filePen: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-file-pen%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M12.659%2022H18a2%202%200%200%200%202-2V8a2.4%202.4%200%200%200-.706-1.706l-3.588-3.588A2.4%202.4%200%200%200%2014%202H6a2%202%200%200%200-2%202v9.34%22%20%2F%3E%20%3Cpath%20d%3D%22M14%202v5a1%201%200%200%200%201%201h5%22%20%2F%3E%20%3Cpath%20d%3D%22M10.378%2012.622a1%201%200%200%201%203%203.003L8.36%2020.637a2%202%200%200%201-.854.506l-2.867.837a.5.5%200%200%201-.62-.62l.836-2.869a2%202%200%200%201%20.506-.853z%22%20%2F%3E%20%3C%2Fsvg%3E", + paperclip: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-paperclip%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22m16%206-8.414%208.586a2%202%200%200%200%202.829%202.829l8.414-8.586a4%204%200%201%200-5.657-5.657l-8.379%208.551a6%206%200%201%200%208.485%208.485l8.379-8.551%22%20%2F%3E%20%3C%2Fsvg%3E", + shieldCheck: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-shield-check%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M20%2013c0%205-3.5%207.5-7.66%208.95a1%201%200%200%201-.67-.01C7.5%2020.5%204%2018%204%2013V6a1%201%200%200%201%201-1c2%200%204.5-1.2%206.24-2.72a1.17%201.17%200%200%201%201.52%200C14.51%203.81%2017%205%2019%205a1%201%200%200%201%201%201z%22%20%2F%3E%20%3Cpath%20d%3D%22m9%2012%202%202%204-4%22%20%2F%3E%20%3C%2Fsvg%3E", + bookUser: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-book-user%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M15%2013a3%203%200%201%200-6%200%22%20%2F%3E%20%3Cpath%20d%3D%22M4%2019.5v-15A2.5%202.5%200%200%201%206.5%202H19a1%201%200%200%201%201%201v18a1%201%200%200%201-1%201H6.5a1%201%200%200%201%200-5H20%22%20%2F%3E%20%3Ccircle%20cx%3D%2212%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%20%3C%2Fsvg%3E", + refreshCw: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-refresh-cw%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M3%2012a9%209%200%200%201%209-9%209.75%209.75%200%200%201%206.74%202.74L21%208%22%20%2F%3E%20%3Cpath%20d%3D%22M21%203v5h-5%22%20%2F%3E%20%3Cpath%20d%3D%22M21%2012a9%209%200%200%201-9%209%209.75%209.75%200%200%201-6.74-2.74L3%2016%22%20%2F%3E%20%3Cpath%20d%3D%22M8%2016H3v5%22%20%2F%3E%20%3C%2Fsvg%3E", + pencil: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-pencil%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M21.174%206.812a1%201%200%200%200-3.986-3.987L3.842%2016.174a2%202%200%200%200-.5.83l-1.321%204.352a.5.5%200%200%200%20.623.622l4.353-1.32a2%202%200%200%200%20.83-.497z%22%20%2F%3E%20%3Cpath%20d%3D%22m15%205%204%204%22%20%2F%3E%20%3C%2Fsvg%3E", + mail: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-mail%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22m22%207-8.991%205.727a2%202%200%200%201-2.009%200L2%207%22%20%2F%3E%20%3Crect%20x%3D%222%22%20y%3D%224%22%20width%3D%2220%22%20height%3D%2216%22%20rx%3D%222%22%20%2F%3E%20%3C%2Fsvg%3E", + chevronDown: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-chevron-down%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22m6%209%206%206%206-6%22%20%2F%3E%20%3C%2Fsvg%3E", + chevronRight: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-chevron-right%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22m9%2018%206-6-6-6%22%20%2F%3E%20%3C%2Fsvg%3E", + ellipsis: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-ellipsis%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%221%22%20%2F%3E%20%3Ccircle%20cx%3D%2219%22%20cy%3D%2212%22%20r%3D%221%22%20%2F%3E%20%3Ccircle%20cx%3D%225%22%20cy%3D%2212%22%20r%3D%221%22%20%2F%3E%20%3C%2Fsvg%3E", + chevronsUp: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-chevrons-up%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22m17%2011-5-5-5%205%22%20%2F%3E%20%3Cpath%20d%3D%22m17%2018-5-5-5%205%22%20%2F%3E%20%3C%2Fsvg%3E", + circle: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-circle%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%20%3C%2Fsvg%3E", + circleHelp: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-circle-help%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%20%2F%3E%20%3Cpath%20d%3D%22M9.09%209a3%203%200%200%201%205.83%201c0%202-3%203-3%203%22%20%2F%3E%20%3Cpath%20d%3D%22M12%2017h.01%22%20%2F%3E%20%3C%2Fsvg%3E", + plus: "data:image/svg+xml;utf8,%3Csvg%20class%3D%22lucide%20lucide-plus%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20%3E%20%3Cpath%20d%3D%22M5%2012h14%22%20%2F%3E%20%3Cpath%20d%3D%22M12%205v14%22%20%2F%3E%20%3C%2Fsvg%3E", + dotBlue: "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%226%22%20fill%3D%22%231e80ff%22%2F%3E%3C%2Fsvg%3E", + dotGreen: "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%226%22%20fill%3D%22%2322c55e%22%2F%3E%3C%2Fsvg%3E", + dotRed: "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%226%22%20fill%3D%22%23ef4444%22%2F%3E%3C%2Fsvg%3E", + dotYellow: "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%226%22%20fill%3D%22%23facc15%22%2F%3E%3C%2Fsvg%3E", +} + +export default lucideIcons diff --git a/src/icons/person.svg b/src/icons/person.svg index e822b509..c18d45cb 100644 --- a/src/icons/person.svg +++ b/src/icons/person.svg @@ -1,6 +1,5 @@ - - - \ No newline at end of file + + + + + diff --git a/src/icons/signup.png b/src/icons/signup.png deleted file mode 100644 index 2c029652f70ae95f23f7bf801bf081f536d1020e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBK3|DzL`iUdT1k0gQ7VI5W_oVoyp7Y6fih=2 zT^vIy7}o~p3LY}x@wH?LaZ*iK$mMWIYeU< + + + + + + diff --git a/src/imagePane.ts b/src/imagePane.ts index 6c672a05..9c0bf172 100644 --- a/src/imagePane.ts +++ b/src/imagePane.ts @@ -6,6 +6,7 @@ import * as UI from 'solid-ui' import type { DataBrowserContext } from 'pane-registry' import type { NamedNode } from 'rdflib' import './imagePane.css' +import { lucideIcons } from './icons/lucide' type FetcherLike = { getHeader: (subject: NamedNode, header: string) => string[] | undefined @@ -45,7 +46,7 @@ function contentTypeMatch ( } export const imagePane: ImagePane = { - icon: UI.icons.originalIconBase + 'tango/22-image-x-generic.png', + icon: lucideIcons.image, name: 'image', diff --git a/src/internal/internalPane.ts b/src/internal/internalPane.ts index 3242d19d..f885ed95 100644 --- a/src/internal/internalPane.ts +++ b/src/internal/internalPane.ts @@ -5,13 +5,24 @@ */ /* global alert confirm */ -import { icons, ns, widgets } from 'solid-ui' +import { ns, widgets } from 'solid-ui' import { BlankNode, IndexedFormula, literal, NamedNode, st, sym, Variable, Store } from 'rdflib' import { PaneDefinition } from 'pane-registry' import './internalPane.css' +import { lucideIcons } from '../icons/lucide' + +// lucide settings — https://lucide.dev/icons/settings (ISC license) +const SETTINGS_ICON = + 'data:image/svg+xml;utf8,' + + encodeURIComponent( + '' + + '' + + '' + + '' + ) const pane: PaneDefinition = { - icon: icons.originalIconBase + 'tango/22-emblem-system.png', + icon: SETTINGS_ICON, name: 'internal', @@ -133,7 +144,7 @@ const pane: PaneDefinition = { const refreshCell = controlRow.appendChild(dom.createElement('td')) const refreshButton = widgets.button( dom, - icons.iconBase + 'noun_479395.svg', + lucideIcons.refreshCw, // was noun_479395.svg 'refresh' ) refreshCell.appendChild(refreshButton) diff --git a/src/mainPage/header.ts b/src/mainPage/header.ts index 2cc79fd4..303f9549 100644 --- a/src/mainPage/header.ts +++ b/src/mainPage/header.ts @@ -10,7 +10,7 @@ import loginIconSvg from '../icons/person.svg?raw' import signOutIconSvg from '../icons/signOut.svg?raw' import defaultAvatarIconSvg from '../icons/personInCircle.svg?raw' import downArrowIconSvg from '../icons/downArrow.svg?raw' -import signupIconPng from '../icons/signup.png' +import signupIconSvg from '../icons/signup.svg?raw' import { createUiIcon } from '../icons/iconHelper' import { setActiveMenuPane } from './menu' /** @@ -18,7 +18,7 @@ import { setActiveMenuPane } from './menu' */ const HELP_MENU_ICON = createUiIcon(helpIconSvg, 'Help Icon', '#ffffff') const LOGIN_ICON = createUiIcon(loginIconSvg, 'LogIn Icon', '#ffffff') -const SIGNUP_ICON = signupIconPng +const SIGNUP_ICON = createUiIcon(signupIconSvg, 'Sign Up Icon', '#ffffff') const LOGOUT_ICON = createUiIcon(signOutIconSvg, 'LogOut Icon', '#000000') const DEFAULT_AVATAR_ICON = createUiIcon(defaultAvatarIconSvg, 'Default Avatar Icon', '#6A7282') const DOWN_ARROW_ICON = createUiIcon(downArrowIconSvg, 'Down Arrow Icon', '#ffffff') diff --git a/src/microblogPane/microblogPane.js b/src/microblogPane/microblogPane.js index cd69ff23..6b7edb85 100644 --- a/src/microblogPane/microblogPane.js +++ b/src/microblogPane/microblogPane.js @@ -6,9 +6,10 @@ import { authn, store } from 'solid-logic' import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from '../icons/lucide' export default { - icon: UI.icons.originalIconBase + 'microblog/microblog.png', + icon: lucideIcons.messageSquareText, name: 'microblogPane', label: function (subject) { if (store.whether(subject, UI.ns.rdf('type'), UI.ns.foaf('Person'))) { diff --git a/src/n3Pane.ts b/src/n3Pane.ts index 258b1724..6743ef3a 100644 --- a/src/n3Pane.ts +++ b/src/n3Pane.ts @@ -9,6 +9,7 @@ import * as $rdf from 'rdflib' import type { DataBrowserContext, RenderEnvironment } from 'pane-registry' import type { NamedNode, Statement } from 'rdflib' import './n3Pane.css' +import { lucideIcons } from './icons/lucide' const ns = UI.ns @@ -43,7 +44,7 @@ function leadingIndentWidth (line: string): number { } export const n3Pane: N3PaneLike = { - icon: UI.icons.originalIconBase + 'w3c/n3_smaller.png', + icon: lucideIcons.braces, name: 'n3', diff --git a/src/outline/manager.js b/src/outline/manager.js index 9409436c..3151f656 100644 --- a/src/outline/manager.js +++ b/src/outline/manager.js @@ -16,6 +16,7 @@ import personIcon from '../icons/person.svg' import friendsIcon from '../icons/friends.svg' import folderIcon from '../icons/folder.svg' import dashboardIcon from '../icons/dashboard.svg' +import { lucideIcons } from '../icons/lucide' const PERSON_ICON = personIcon const FRIENDS_ICON = friendsIcon @@ -397,12 +398,12 @@ export default function (context) { { paneName: 'home', label: 'Your dashboard', - icon: DASHBOARD_ICON + icon: lucideIcons.layoutDashboard }, { paneName: 'basicPreferences', label: 'Your preferences', - icon: UI.icons.iconBase + 'noun_Sliders_341315_000000.svg' + icon: lucideIcons.slidersHorizontal } ) @@ -493,7 +494,7 @@ export default function (context) { tabName: `contact-${index}`, label: 'Contacts', subject: book, - icon: UI.icons.iconBase + 'noun_15695.svg' + icon: lucideIcons.bookUser })) } catch (err) { console.error('oops in globalAppTabs AddressBook') @@ -851,7 +852,7 @@ export default function (context) { if (!options.solo && !showHeader) { const icon = header.appendChild( UI.utils.AJARImage( - UI.icons.originalIconBase + 'tbl-collapse.png', + lucideIcons.chevronDown, // was tbl-collapse.png 'collapse', undefined, dom diff --git a/src/outline/outlineIcons.js b/src/outline/outlineIcons.js index ea0445be..ff9a0c9a 100644 --- a/src/outline/outlineIcons.js +++ b/src/outline/outlineIcons.js @@ -1,6 +1,7 @@ // This is a system of tracking tooltip phrases used by the tabulator outliner import * as UI from 'solid-ui' +import { lucideIcons } from '../icons/lucide' export const outlineIcons = {} @@ -9,92 +10,67 @@ outlineIcons.tooltips = [] // look up tool tips from URL /// /////////////////////// Common icons -outlineIcons.src.icon_expand = - UI.icons.originalIconBase + 'tbl-expand-trans.png' -outlineIcons.src.icon_more = UI.icons.originalIconBase + 'tbl-more-trans.png' // looks just like expand, diff semantics -// Icon.src.icon_expand = UI.icons.originalIconBase + 'clean/Icon.src.Icon.src.icon_expand.png'; -outlineIcons.src.icon_collapse = UI.icons.originalIconBase + 'tbl-collapse.png' -outlineIcons.src.icon_internals = - UI.icons.originalIconBase + 'tango/22-emblem-system.png' -outlineIcons.src.icon_instances = - UI.icons.originalIconBase + 'tango/22-folder-open.png' -outlineIcons.src.icon_foaf = UI.icons.originalIconBase + 'foaf/foafTiny.gif' -outlineIcons.src.icon_social = UI.icons.originalIconBase + 'social/social.gif' -outlineIcons.src.icon_mb = UI.icons.originalIconBase + 'microblog/microblog.png' -outlineIcons.src.icon_shrink = UI.icons.originalIconBase + 'tbl-shrink.png' // shrink list back up -outlineIcons.src.icon_rows = UI.icons.originalIconBase + 'tbl-rows.png' -// Icon.src.Icon.src.icon_columns = 'icons/tbl-columns.png'; +outlineIcons.src.icon_expand = lucideIcons.chevronRight +outlineIcons.src.icon_more = lucideIcons.ellipsis +outlineIcons.src.icon_collapse = lucideIcons.chevronDown +outlineIcons.src.icon_internals = lucideIcons.slidersHorizontal +outlineIcons.src.icon_instances = lucideIcons.folderOpen +outlineIcons.src.icon_foaf = lucideIcons.usersRound +outlineIcons.src.icon_social = lucideIcons.usersRound +outlineIcons.src.icon_mb = lucideIcons.messageSquareText +outlineIcons.src.icon_shrink = lucideIcons.chevronsUp +outlineIcons.src.icon_rows = lucideIcons.table // Status balls: -outlineIcons.src.icon_unrequested = UI.icons.originalIconBase + '16dot-blue.gif' -// outlineIcons.src.Icon.src.icon_parse = UI.icons.originalIconBase + '18x18-white.gif'; -outlineIcons.src.icon_fetched = UI.icons.originalIconBase + '16dot-green.gif' -outlineIcons.src.icon_failed = UI.icons.originalIconBase + '16dot-red.gif' -outlineIcons.src.icon_requested = UI.icons.originalIconBase + '16dot-yellow.gif' -// Icon.src.icon_maximize = UI.icons.originalIconBase + 'clean/Icon.src.Icon.src.icon_con_max.png'; +outlineIcons.src.icon_unrequested = lucideIcons.dotBlue +outlineIcons.src.icon_fetched = lucideIcons.dotGreen +outlineIcons.src.icon_failed = lucideIcons.dotRed +outlineIcons.src.icon_requested = lucideIcons.dotYellow // Panes: -outlineIcons.src.icon_CVPane = UI.icons.originalIconBase + 'CV.png' -outlineIcons.src.icon_defaultPane = UI.icons.originalIconBase + 'about.png' -outlineIcons.src.icon_visit = - UI.icons.originalIconBase + 'tango/22-text-x-generic.png' -outlineIcons.src.icon_dataContents = - UI.icons.originalIconBase + 'rdf_flyer.24.gif' // @@ Bad .. find better -outlineIcons.src.icon_n3Pane = UI.icons.originalIconBase + 'w3c/n3_smaller.png' // @@ Bad .. find better -outlineIcons.src.icon_RDFXMLPane = - UI.icons.originalIconBase + '22-text-xml4.png' // @@ Bad .. find better -outlineIcons.src.icon_imageContents = - UI.icons.originalIconBase + 'tango/22-image-x-generic.png' -outlineIcons.src.icon_airPane = UI.icons.originalIconBase + '1pt5a.gif' -outlineIcons.src.icon_LawPane = UI.icons.originalIconBase + 'law.jpg' -outlineIcons.src.icon_pushbackPane = UI.icons.originalIconBase + 'pb-logo.png' +outlineIcons.src.icon_CVPane = UI.icons.originalIconBase + 'CV.png' // legacy +outlineIcons.src.icon_defaultPane = lucideIcons.info +outlineIcons.src.icon_visit = lucideIcons.info +outlineIcons.src.icon_dataContents = lucideIcons.code +outlineIcons.src.icon_n3Pane = lucideIcons.braces +outlineIcons.src.icon_RDFXMLPane = lucideIcons.code +outlineIcons.src.icon_imageContents = lucideIcons.image +outlineIcons.src.icon_airPane = UI.icons.originalIconBase + '1pt5a.gif' // legacy +outlineIcons.src.icon_LawPane = UI.icons.originalIconBase + 'law.jpg' // legacy +outlineIcons.src.icon_pushbackPane = UI.icons.originalIconBase + 'pb-logo.png' // legacy // For photo albums (By albert08@csail.mit.edu) -outlineIcons.src.icon_photoPane = UI.icons.originalIconBase + 'photo_small.png' -outlineIcons.src.icon_tagPane = UI.icons.originalIconBase + 'tag_small.png' -outlineIcons.src.icon_TinyTag = UI.icons.originalIconBase + 'tag_tiny.png' -outlineIcons.src.icon_photoBegin = UI.icons.originalIconBase + 'photo_begin.png' -outlineIcons.src.icon_photoNext = UI.icons.originalIconBase + 'photo_next.png' -outlineIcons.src.icon_photoBack = UI.icons.originalIconBase + 'photo_back.png' -outlineIcons.src.icon_photoEnd = UI.icons.originalIconBase + 'photo_end.png' -outlineIcons.src.icon_photoImportPane = - UI.icons.originalIconBase + 'flickr_small.png' -// Icon.src.icon_CloseButton = UI.icons.originalIconBase + 'close_tiny.png'; -// Icon.src.icon_AddButton = UI.icons.originalIconBase + 'addphoto_tiny.png'; - -// For that one we need a document with grid lines. Make data-x-generix maybe +outlineIcons.src.icon_photoPane = lucideIcons.image +outlineIcons.src.icon_tagPane = UI.icons.originalIconBase + 'tag_small.png' // legacy +outlineIcons.src.icon_TinyTag = UI.icons.originalIconBase + 'tag_tiny.png' // legacy +outlineIcons.src.icon_photoBegin = UI.icons.originalIconBase + 'photo_begin.png' // legacy +outlineIcons.src.icon_photoNext = UI.icons.originalIconBase + 'photo_next.png' // legacy +outlineIcons.src.icon_photoBack = UI.icons.originalIconBase + 'photo_back.png' // legacy +outlineIcons.src.icon_photoEnd = UI.icons.originalIconBase + 'photo_end.png' // legacy +outlineIcons.src.icon_photoImportPane = UI.icons.originalIconBase + 'flickr_small.png' // legacy // actions for sources; -outlineIcons.src.icon_retract = UI.icons.originalIconBase + 'retract.gif' -outlineIcons.src.icon_refresh = UI.icons.originalIconBase + 'refresh.gif' -outlineIcons.src.icon_optoff = UI.icons.originalIconBase + 'optional_off.PNG' -outlineIcons.src.icon_opton = UI.icons.originalIconBase + 'optional_on.PNG' -outlineIcons.src.icon_map = UI.icons.originalIconBase + 'compassrose.png' -outlineIcons.src.icon_retracted = outlineIcons.src.icon_unrequested +outlineIcons.src.icon_retract = UI.icons.originalIconBase + 'retract.gif' // legacy +outlineIcons.src.icon_refresh = lucideIcons.refreshCw +outlineIcons.src.icon_optoff = UI.icons.originalIconBase + 'optional_off.PNG' // legacy +outlineIcons.src.icon_opton = UI.icons.originalIconBase + 'optional_on.PNG' // legacy +outlineIcons.src.icon_map = lucideIcons.map outlineIcons.src.icon_retracted = outlineIcons.src.icon_unrequested -outlineIcons.src.icon_time = UI.icons.originalIconBase + 'icons/Wclocksmall.png' +outlineIcons.src.icon_time = UI.icons.originalIconBase + 'Wclocksmall.png' // legacy // Within outline mode: -outlineIcons.src.icon_telephone = - UI.icons.originalIconBase + 'silk/telephone.png' -outlineIcons.src.icon_time = UI.icons.originalIconBase + 'Wclocksmall.png' -outlineIcons.src.icon_remove_node = - UI.icons.originalIconBase + 'tbl-x-small.png' -outlineIcons.src.icon_add_triple = - UI.icons.originalIconBase + 'tango/22-list-add.png' -outlineIcons.src.icon_add_new_triple = - UI.icons.originalIconBase + 'tango/22-list-add-new.png' -outlineIcons.src.icon_show_choices = - UI.icons.originalIconBase + 'userinput_show_choices_temp.png' // looks just like collapse, diff smmantics +outlineIcons.src.icon_telephone = UI.icons.originalIconBase + 'silk/telephone.png' // legacy +outlineIcons.src.icon_remove_node = UI.icons.originalIconBase + 'tbl-x-small.png' // legacy +outlineIcons.src.icon_add_triple = UI.icons.originalIconBase + 'tango/22-list-add.png' // legacy +outlineIcons.src.icon_add_new_triple = UI.icons.originalIconBase + 'tango/22-list-add-new.png' // legacy +outlineIcons.src.icon_show_choices = UI.icons.originalIconBase + 'userinput_show_choices_temp.png' // legacy // Inline Justification -outlineIcons.src.icon_display_reasons = - UI.icons.originalIconBase + 'tango/22-help-browser.png' -outlineIcons.tooltips[outlineIcons.src.icon_display_reasons] = - 'Display explanations' +outlineIcons.src.icon_display_reasons = lucideIcons.circleHelp || UI.icons.originalIconBase + 'tango/22-help-browser.png' // help-circle if available +outlineIcons.tooltips[outlineIcons.src.icon_display_reasons] = 'Display explanations' // Other tooltips outlineIcons.tooltips[outlineIcons.src.icon_add_triple] = 'Add more' @@ -106,21 +82,16 @@ outlineIcons.tooltips[outlineIcons.src.icon_shrink] = 'Shrink list.' outlineIcons.tooltips[outlineIcons.src.icon_internals] = 'Under the hood' outlineIcons.tooltips[outlineIcons.src.icon_instances] = 'List' outlineIcons.tooltips[outlineIcons.src.icon_foaf] = 'Friends' -outlineIcons.tooltips[outlineIcons.src.icon_rows] = - 'Make a table of data like this' +outlineIcons.tooltips[outlineIcons.src.icon_rows] = 'Make a table of data like this' // Note the string '[Tt]his resource' can be replaced with an actual URI by the code outlineIcons.tooltips[outlineIcons.src.icon_unrequested] = 'Fetch this.' outlineIcons.tooltips[outlineIcons.src.icon_fetched] = 'Fetched successfully.' -outlineIcons.tooltips[outlineIcons.src.icon_failed] = - 'Failed to load. Click to retry.' -outlineIcons.tooltips[outlineIcons.src.icon_requested] = - 'This is being fetched. Please wait...' +outlineIcons.tooltips[outlineIcons.src.icon_failed] = 'Failed to load. Click to retry.' +outlineIcons.tooltips[outlineIcons.src.icon_requested] = 'This is being fetched. Please wait...' outlineIcons.tooltips[outlineIcons.src.icon_visit] = 'View document' -outlineIcons.tooltips[outlineIcons.src.icon_retract] = - 'Remove this source and all its data from tabulator.' -outlineIcons.tooltips[outlineIcons.src.icon_refresh] = - 'Refresh this source and reload its triples.' +outlineIcons.tooltips[outlineIcons.src.icon_retract] = 'Remove this source and all its data from tabulator.' +outlineIcons.tooltips[outlineIcons.src.icon_refresh] = 'Refresh this source and reload its triples.' /// ////////////////////////////// End comon area diff --git a/src/pad/padPane.ts b/src/pad/padPane.ts index 62bed32d..e2621b4b 100644 --- a/src/pad/padPane.ts +++ b/src/pad/padPane.ts @@ -1,15 +1,15 @@ -import { icons, ns, pad, widgets, login } from 'solid-ui' +import { ns, pad, widgets, login } from 'solid-ui' import { authn, AppDetails } from 'solid-logic' import { graph, log, NamedNode, Namespace, sym, serialize, Store } from 'rdflib' import { PaneDefinition } from 'pane-registry' +import { lucideIcons } from '../icons/lucide' import './padPane.css' /* pad Pane ** */ const paneDef: PaneDefinition = { - // icon: (module.__dirname || __dirname) + 'images/ColourOn.png', - icon: icons.iconBase + 'noun_79217.svg', + icon: lucideIcons.notebookPen, name: 'pad', diff --git a/src/playlist/playlistPane.js b/src/playlist/playlistPane.js index 003d1cff..f9f4af34 100644 --- a/src/playlist/playlistPane.js +++ b/src/playlist/playlistPane.js @@ -5,10 +5,11 @@ */ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns export default { - icon: UI.icons.iconBase + 'noun_1619.svg', + icon: lucideIcons.listMusic, name: 'playlistSlot', diff --git a/src/registerPanes.js b/src/registerPanes.js index 4d82e2a5..039a450c 100644 --- a/src/registerPanes.js +++ b/src/registerPanes.js @@ -90,6 +90,14 @@ export function registerPanes (register) { register(audioPane) // Audio clip player register(dokieliPane) // Should be above dataContentPane + // lucide folder — https://lucide.dev/icons/folder (ISC license) + folderPane.icon = + 'data:image/svg+xml;utf8,' + + encodeURIComponent( + '' + + '' + + '' + ) register(folderPane) // Should be above dataContentPane register(classInstancePane) // Should be above dataContentPane // register(require('./dynamic/dynamicPanes.js')) // warp etc warp broken 2017/8 @@ -101,6 +109,16 @@ export function registerPanes (register) { // register(require('markdown-pane').Pane) // replaced by markdown in humanReadablePane register(dataContentPane) // Preferred for a data file + // lucide code-xml — https://lucide.dev/icons/code-xml (ISC license) + sourcePane.icon = + 'data:image/svg+xml;utf8,' + + encodeURIComponent( + '' + + '' + + '' + + '' + + '' + ) register(sourcePane) // edit source register(n3Pane) register(RDFXMLPane) diff --git a/src/schedule/schedulePane.ts b/src/schedule/schedulePane.ts index 7865f06b..acfe6a6a 100644 --- a/src/schedule/schedulePane.ts +++ b/src/schedule/schedulePane.ts @@ -12,6 +12,7 @@ import type { NamedNode, Node as RdflibNode, Statement, Variable } from 'rdflib' import formText from './formsForSchedule.ttl' import './schedulePane.css' import '../styles/utilities.css' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns @@ -64,7 +65,7 @@ function runNextAgendaItem (agenda: AgendaTask[]): void { } export const schedulePane = { - icon: UI.icons.iconBase + 'noun_346777.svg', // @@ better? + icon: lucideIcons.calendarDays, name: 'schedule', @@ -921,7 +922,7 @@ export const schedulePane = { const emailButton = dom.createElement('button') emailButton.classList.add('schedule-pane__button') const emailIcon = emailButton.appendChild(dom.createElement('img')) - emailIcon.setAttribute('src', UI.icons.iconBase + 'noun_480183.svg') // noun_480183.svg + emailIcon.setAttribute('src', lucideIcons.mail) // was noun_480183.svg emailIcon.classList.add('schedule-pane__button-icon') // emailButton.textContent = 'email invitations' emailButton.addEventListener( @@ -1219,7 +1220,7 @@ export const schedulePane = { refreshButton.classList.add('schedule-pane__button') // refreshButton.textContent = 'refresh' // noun_479395.svg const refreshIcon = dom.createElement('img') - refreshIcon.setAttribute('src', UI.icons.iconBase + 'noun_479395.svg') + refreshIcon.setAttribute('src', lucideIcons.refreshCw) // was noun_479395.svg refreshIcon.classList.add('schedule-pane__button-icon') refreshButton.appendChild(refreshIcon) refreshButton.addEventListener( @@ -1361,7 +1362,7 @@ export const schedulePane = { editButton.classList.add('schedule-pane__button') // editButton.textContent = '(Modify the poll)' // noun_344563.svg const editIcon = dom.createElement('img') - editIcon.setAttribute('src', UI.icons.iconBase + 'noun_344563.svg') + editIcon.setAttribute('src', lucideIcons.pencil) // was noun_344563.svg editIcon.classList.add('schedule-pane__button-icon') editButton.appendChild(editIcon) editButton.addEventListener( diff --git a/src/sharing/sharingPane.ts b/src/sharing/sharingPane.ts index e3b8a4b3..6c3e45cd 100644 --- a/src/sharing/sharingPane.ts +++ b/src/sharing/sharingPane.ts @@ -8,10 +8,20 @@ ** like "this" where the string is seen by the user and so I18n is an issue. */ -import { aclControl, icons, ns } from 'solid-ui' +import { aclControl, ns } from 'solid-ui' + +// lucide lock — https://lucide.dev/icons/lock (ISC license) +const LOCK_ICON = + 'data:image/svg+xml;utf8,' + + encodeURIComponent( + '' + + '' + + '' + + '' + ) const sharingPane = { - icon: icons.iconBase + 'padlock-timbl.svg', + icon: LOCK_ICON, name: 'sharing', diff --git a/src/slideshow/slideshowPane.js b/src/slideshow/slideshowPane.js index 087ad4f6..4057239d 100644 --- a/src/slideshow/slideshowPane.js +++ b/src/slideshow/slideshowPane.js @@ -4,10 +4,11 @@ import * as UI from 'solid-ui' import makeBSS from '@solid/better-simple-slideshow' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns export const slideshowPane = { - icon: UI.icons.iconBase + 'noun_138712.svg', + icon: lucideIcons.presentation, name: 'slideshow', diff --git a/src/social/socialPane.ts b/src/social/socialPane.ts index df0a8cda..2f3334dd 100644 --- a/src/social/socialPane.ts +++ b/src/social/socialPane.ts @@ -9,11 +9,12 @@ */ import './socialPane.css' -import { icons, utils, ns, log, widgets } from 'solid-ui' +import { utils, ns, log, widgets } from 'solid-ui' import { authn } from 'solid-logic' import { LiveStore, NamedNode, Statement } from 'rdflib' import { DataBrowserContext } from 'pane-registry' import { locationIcon } from './icons' +import { lucideIcons } from '../icons/lucide' import { createAllFriendsSection, createHeaderSection, @@ -41,7 +42,7 @@ function applyEnvironmentAttributes ( } export const socialPane = { - icon: icons.originalIconBase + 'foaf/foafTiny.gif', + icon: lucideIcons.usersRound, name: 'social', diff --git a/src/tabbed/tabbedPane.ts b/src/tabbed/tabbedPane.ts index c9b035d1..ce6ea2ff 100644 --- a/src/tabbed/tabbedPane.ts +++ b/src/tabbed/tabbedPane.ts @@ -5,10 +5,11 @@ */ import { Store } from 'rdflib' import { PaneDefinition } from 'pane-registry' -import { icons, ns, tabs, widgets } from 'solid-ui' +import { ns, tabs, widgets } from 'solid-ui' +import { lucideIcons } from '../icons/lucide' const TabbedPane: PaneDefinition = { - icon: icons.iconBase + 'noun_688606.svg', + icon: lucideIcons.appWindow, name: 'tabbed', diff --git a/src/tableViewPane.js b/src/tableViewPane.js index 2e0c9cb9..34b4c786 100644 --- a/src/tableViewPane.js +++ b/src/tableViewPane.js @@ -16,9 +16,10 @@ // Table view pane -- view of a class as a table of properties of class members import * as UI from 'solid-ui' +import { lucideIcons } from './icons/lucide' export const tableViewPane = { - icon: UI.icons.originalIconBase + 'table.png', + icon: lucideIcons.table, name: 'tableOfClass', diff --git a/src/transaction/pane.js b/src/transaction/pane.js index bd6f3b1c..42dfa909 100644 --- a/src/transaction/pane.js +++ b/src/transaction/pane.js @@ -7,11 +7,11 @@ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns export default { - // icon: (module.__dirname || __dirname) + '22-pixel-068010-3d-transparent-glass-icon-alphanumeric-dollar-sign.png', - icon: UI.icons.iconBase + 'noun_106746.svg', + icon: lucideIcons.wallet, name: 'transaction', diff --git a/src/transaction/period.js b/src/transaction/period.js index c93962dc..bed376d0 100644 --- a/src/transaction/period.js +++ b/src/transaction/period.js @@ -6,10 +6,11 @@ */ import * as UI from 'solid-ui' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns export default { - icon: UI.icons.iconBase + 'noun_142708.svg', + icon: lucideIcons.calendarRange, name: 'period', diff --git a/src/trip/tripPane.js b/src/trip/tripPane.js index 5b827cf3..901f91f7 100644 --- a/src/trip/tripPane.js +++ b/src/trip/tripPane.js @@ -10,10 +10,11 @@ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns export default { - icon: UI.icons.iconBase + 'noun_62007.svg', + icon: lucideIcons.map, name: 'travel expenses', diff --git a/src/trustedApplications/trustedApplications.view.ts b/src/trustedApplications/trustedApplications.view.ts index 9cfe21bd..b3f4b870 100644 --- a/src/trustedApplications/trustedApplications.view.ts +++ b/src/trustedApplications/trustedApplications.view.ts @@ -1,14 +1,15 @@ import { PaneDefinition } from 'pane-registry' import { NamedNode } from 'rdflib' -import { icons, login, widgets } from 'solid-ui' +import { login, widgets } from 'solid-ui' import { store } from 'solid-logic' import { createApplicationTable, createContainer, createText } from './trustedApplications.dom' +import { lucideIcons } from '../icons/lucide' const thisColor = '#418d99' const trustedApplicationView: PaneDefinition = { global: true, - icon: `${icons.iconBase}noun_15177.svg`, + icon: lucideIcons.shieldCheck, name: 'trustedApplications', label: () => null, render: (subject, context) => { diff --git a/src/ui/pane.js b/src/ui/pane.js index 9a0a4381..a954fc56 100644 --- a/src/ui/pane.js +++ b/src/ui/pane.js @@ -4,11 +4,11 @@ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from '../icons/lucide' const ns = UI.ns export default { - // noun_170702.svg' builder noun_122196.svg form - icon: UI.icons.iconBase + 'noun_170702.svg', + icon: lucideIcons.wand, name: 'ui', diff --git a/src/video/videoPane.js b/src/video/videoPane.js index 47a36e27..8fe394ba 100644 --- a/src/video/videoPane.js +++ b/src/video/videoPane.js @@ -3,9 +3,10 @@ */ import * as UI from 'solid-ui' import * as $rdf from 'rdflib' +import { lucideIcons } from '../icons/lucide' export default { - icon: UI.icons.iconBase + 'noun_1619.svg', + icon: lucideIcons.video, name: 'video',