diff --git a/package-lock.json b/package-lock.json index 7bca6460..5bcc9011 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,9 +25,9 @@ "pane-registry": "5.0.2-1", "profile-pane": "4.1.0-0", "rdflib": "2.4.1", - "solid-logic": "6.0.1-4", + "solid-logic": "6.0.1-5", "solid-namespace": "^0.5.4", - "solid-ui": "5.0.2-3", + "solid-ui": "5.0.2-4", "source-pane": "4.0.1-0" }, "devDependencies": { @@ -6159,6 +6159,58 @@ "vitest-axe": "^0.1.0" } }, + "node_modules/folder-pane/node_modules/solid-logic": { + "version": "6.0.1-4", + "resolved": "https://registry.npmjs.org/solid-logic/-/solid-logic-6.0.1-4.tgz", + "integrity": "sha512-EVvS+5g91aUoONqGtM1vPnMimtwH6c/72td5b4qBRcudDjeRU/hycPg8p5abmx1YWZseLA1EFK76+E/t5bcPFw==", + "license": "MIT", + "dependencies": { + "@dokieli/web-access-control": "^1.1.0", + "@uvdsl/solid-oidc-client-browser": "^0.2.3", + "solid-namespace": "^0.5.4", + "vite": "^8.0.16", + "vitest": "^4.1.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/folder-pane/node_modules/solid-ui": { + "version": "5.0.2-3", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.2-3.tgz", + "integrity": "sha512-ERESxhk6dYjoh/PcsKAwQ5UuxJbO2CTfIG2dccQrEn6/vSNpvVzsVkXsU2QCKemrSirzKiBbbB/eZTYdxLczkA==", + "license": "MIT", + "dependencies": { + "@awesome.me/webawesome": "^3.9.0", + "@codemirror/commands": "^6.10.0", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.3.1", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.12.3", + "@codemirror/legacy-modes": "^6.5.3", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.43.0", + "@lit/context": "^1.1.6", + "@lit/task": "^1.0.3", + "@noble/curves": "^2.2.0", + "@noble/hashes": "^2.2.0", + "@uiw/codemirror-theme-vscode": "^4.25.10", + "escape-html": "^1.0.3", + "lit": "^3.3.3", + "mime-types": "^3.0.2", + "pane-registry": "5.0.2-1", + "rdflib": "2.4.1", + "solid-logic": "6.0.1-4", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -8820,6 +8872,22 @@ "solid-logic": "6.0.1-4" } }, + "node_modules/pane-registry/node_modules/solid-logic": { + "version": "6.0.1-4", + "resolved": "https://registry.npmjs.org/solid-logic/-/solid-logic-6.0.1-4.tgz", + "integrity": "sha512-EVvS+5g91aUoONqGtM1vPnMimtwH6c/72td5b4qBRcudDjeRU/hycPg8p5abmx1YWZseLA1EFK76+E/t5bcPFw==", + "license": "MIT", + "dependencies": { + "@dokieli/web-access-control": "^1.1.0", + "@uvdsl/solid-oidc-client-browser": "^0.2.3", + "solid-namespace": "^0.5.4", + "vite": "^8.0.16", + "vitest": "^4.1.9" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -9889,9 +9957,9 @@ } }, "node_modules/solid-logic": { - "version": "6.0.1-4", - "resolved": "https://registry.npmjs.org/solid-logic/-/solid-logic-6.0.1-4.tgz", - "integrity": "sha512-EVvS+5g91aUoONqGtM1vPnMimtwH6c/72td5b4qBRcudDjeRU/hycPg8p5abmx1YWZseLA1EFK76+E/t5bcPFw==", + "version": "6.0.1-5", + "resolved": "https://registry.npmjs.org/solid-logic/-/solid-logic-6.0.1-5.tgz", + "integrity": "sha512-U6IBElhpCpNkNrJq4TujBT7VMfl8xyY0U2PiuyltI5SiORhMAtLMHOqO6liSH5PO50y7UjAOMP1G8VbLjnYIOw==", "license": "MIT", "dependencies": { "@dokieli/web-access-control": "^1.1.0", @@ -9911,9 +9979,9 @@ "license": "MIT" }, "node_modules/solid-ui": { - "version": "5.0.2-3", - "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.2-3.tgz", - "integrity": "sha512-ERESxhk6dYjoh/PcsKAwQ5UuxJbO2CTfIG2dccQrEn6/vSNpvVzsVkXsU2QCKemrSirzKiBbbB/eZTYdxLczkA==", + "version": "5.0.2-4", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.2-4.tgz", + "integrity": "sha512-lT6Grklrtd9eDn6Rt9HMRDstcMNmbb/7Vy0oOUOLVpA5njmq/+P1A3eNvkLMRqR9cCsXCrwCkAYqdbql+uazRQ==", "license": "MIT", "dependencies": { "@awesome.me/webawesome": "^3.9.0", @@ -9946,6 +10014,22 @@ "fsevents": "*" } }, + "node_modules/solid-ui/node_modules/solid-logic": { + "version": "6.0.1-4", + "resolved": "https://registry.npmjs.org/solid-logic/-/solid-logic-6.0.1-4.tgz", + "integrity": "sha512-EVvS+5g91aUoONqGtM1vPnMimtwH6c/72td5b4qBRcudDjeRU/hycPg8p5abmx1YWZseLA1EFK76+E/t5bcPFw==", + "license": "MIT", + "dependencies": { + "@dokieli/web-access-control": "^1.1.0", + "@uvdsl/solid-oidc-client-browser": "^0.2.3", + "solid-namespace": "^0.5.4", + "vite": "^8.0.16", + "vitest": "^4.1.9" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/solidos-toolkit": { "version": "1.0.0-dev.2fb6be508170f3b28ca79d738aae71cd48f6d87e", "resolved": "https://registry.npmjs.org/solidos-toolkit/-/solidos-toolkit-1.0.0-dev.2fb6be508170f3b28ca79d738aae71cd48f6d87e.tgz", @@ -10023,6 +10107,58 @@ "vitest-axe": "^0.1.0" } }, + "node_modules/source-pane/node_modules/solid-logic": { + "version": "6.0.1-4", + "resolved": "https://registry.npmjs.org/solid-logic/-/solid-logic-6.0.1-4.tgz", + "integrity": "sha512-EVvS+5g91aUoONqGtM1vPnMimtwH6c/72td5b4qBRcudDjeRU/hycPg8p5abmx1YWZseLA1EFK76+E/t5bcPFw==", + "license": "MIT", + "dependencies": { + "@dokieli/web-access-control": "^1.1.0", + "@uvdsl/solid-oidc-client-browser": "^0.2.3", + "solid-namespace": "^0.5.4", + "vite": "^8.0.16", + "vitest": "^4.1.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-pane/node_modules/solid-ui": { + "version": "5.0.2-3", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.2-3.tgz", + "integrity": "sha512-ERESxhk6dYjoh/PcsKAwQ5UuxJbO2CTfIG2dccQrEn6/vSNpvVzsVkXsU2QCKemrSirzKiBbbB/eZTYdxLczkA==", + "license": "MIT", + "dependencies": { + "@awesome.me/webawesome": "^3.9.0", + "@codemirror/commands": "^6.10.0", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.3.1", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.12.3", + "@codemirror/legacy-modes": "^6.5.3", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.43.0", + "@lit/context": "^1.1.6", + "@lit/task": "^1.0.3", + "@noble/curves": "^2.2.0", + "@noble/hashes": "^2.2.0", + "@uiw/codemirror-theme-vscode": "^4.25.10", + "escape-html": "^1.0.3", + "lit": "^3.3.3", + "mime-types": "^3.0.2", + "pane-registry": "5.0.2-1", + "rdflib": "2.4.1", + "solid-logic": "6.0.1-4", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", diff --git a/package.json b/package.json index 52712e2e..2bb5bcce 100644 --- a/package.json +++ b/package.json @@ -72,9 +72,9 @@ "pane-registry": "5.0.2-1", "profile-pane": "4.1.0-0", "rdflib": "2.4.1", - "solid-logic": "6.0.1-4", + "solid-logic": "6.0.1-5", "solid-namespace": "^0.5.4", - "solid-ui": "5.0.2-3", + "solid-ui": "5.0.2-4", "source-pane": "4.0.1-0" }, "overrides": { diff --git a/src/navigation/chrome.ts b/src/navigation/chrome.ts index 5a37a295..566e302b 100644 --- a/src/navigation/chrome.ts +++ b/src/navigation/chrome.ts @@ -168,7 +168,12 @@ export function deriveExplorerHeader (input: ExplorerHeaderInput): boolean { * The header is never shown over a person's profile or friends, whatever the resource, * nor over a storage view, which has its own header or the navbar. */ -export function explorerHeaderForPane (resourceHeader: boolean, paneName?: string, folderView?: FolderView): boolean { +export function explorerHeaderForPane ( + resourceHeader: boolean, + paneName?: string, + folderView?: FolderView, + solo = false +): boolean { if (paneName === 'folder' && (folderView === 'explorer' || folderView === 'storage')) return false return resourceHeader && !isPersonPane(paneName) } diff --git a/src/outline/manager.js b/src/outline/manager.js index c56f3b87..a6e83039 100644 --- a/src/outline/manager.js +++ b/src/outline/manager.js @@ -259,14 +259,6 @@ export default function (context) { }) } - function getPane (relevantPanes, subject) { - return ( - relevantPanes.find( - pane => pane.shouldGetFocus && pane.shouldGetFocus(subject) - ) || relevantPanes[0] - ) - } - function setQueryButtonVisibility (shouldShow) { const queryButton = dom.getElementById('queryButton') if (!queryButton) return @@ -313,7 +305,7 @@ export default function (context) { subjectUri: subject.uri, pane, relevantPanes: provider.relevantPanes || [], - header: state && explorerHeaderForPane(state.showHeader, pane?.name, state.options.folderView) + header: state && explorerHeaderForPane(state.showHeader, pane?.name, state.options.folderView, state.options.solo) ? { paneIcon: getPaneIcon(pane, subject, context), menuItems: state.menuItems } : undefined, paneElement @@ -403,7 +395,7 @@ export default function (context) { } provider.relevantPanes = relevantPanes - provider.pane = requiredPane || getPane(relevantPanes, subject) + provider.pane = requiredPane || UI.getPreferredPane(subject, context, relevantPanes) const isRootResource = !!(subject && subject.uri && subject.site && subject.site().uri === subject.uri) const viewerPodOnly = !!options.headerOnlyInViewerPod const showHeader = !subject.uri.endsWith('/index.ttl#this') && deriveExplorerHeader({ diff --git a/src/registerPanes.js b/src/registerPanes.js index 4d82e2a5..2fb03723 100644 --- a/src/registerPanes.js +++ b/src/registerPanes.js @@ -90,10 +90,10 @@ export function registerPanes (register) { register(audioPane) // Audio clip player register(dokieliPane) // Should be above dataContentPane + register(slideshowPane) // Image containers should open in their specialized view register(folderPane) // Should be above dataContentPane register(classInstancePane) // Should be above dataContentPane // register(require('./dynamic/dynamicPanes.js')) // warp etc warp broken 2017/8 - register(slideshowPane) register(socialPane) diff --git a/src/slideshow/slideshowPane.js b/src/slideshow/slideshowPane.js index 087ad4f6..99ff2b42 100644 --- a/src/slideshow/slideshowPane.js +++ b/src/slideshow/slideshowPane.js @@ -2,9 +2,11 @@ ** */ import * as UI from 'solid-ui' +import { solidLogicSingleton } from 'solid-logic' import makeBSS from '@solid/better-simple-slideshow' const ns = UI.ns +let slideshowId = 0 export const slideshowPane = { icon: UI.icons.iconBase + 'noun_138712.svg', @@ -16,13 +18,11 @@ export const slideshowPane = { // Does the subject deserve an slideshow pane? label: function (subject, context) { const store = context.session.store - const ns = UI.ns - const t = store.findTypeURIs(subject) - if (t[ns.ldp('Container').uri] || t[ns.ldp('BasicContainer').uri]) { + if (solidLogicSingleton.resource.isContainer(subject)) { const contents = store.each(subject, ns.ldp('contains')) let count = 0 contents.forEach(function (file) { - if (UI.widgets.isImage(file)) count++ + if (solidLogicSingleton.resource.isImage(file)) count++ }) return count > 0 ? 'Slideshow' : null } @@ -40,18 +40,21 @@ export const slideshowPane = { const store = context.session.store const ns = UI.ns const div = dom.createElement('div') + const id = `slideshow-${++slideshowId}` + div.id = id div.setAttribute('class', 'bss-slides') - const t = store.findTypeURIs(subject) let predicate - if (t[ns.ldp('BasicContainer').uri] || t[ns.ldp('Container').uri]) { + if (solidLogicSingleton.resource.isContainer(subject)) { predicate = ns.ldp('contains') } - const images = store.each(subject, predicate) // @@ random order? + const images = [...new Map( + store.each(subject, predicate).map(image => [image.uri, image]) + ).values()] // @@ Ideally: sort by embedded time of image - images.sort() // Sort for now by URI + images.sort((left, right) => left.uri.localeCompare(right.uri)) for (let i = 0; i < images.length; i++) { - if (!UI.widgets.isImage(images[i])) continue + if (!solidLogicSingleton.resource.isImage(images[i])) continue const figure = div.appendChild(dom.createElement('figure')) const img = figure.appendChild(dom.createElement('img')) @@ -69,9 +72,9 @@ export const slideshowPane = { } const options = { dom } - setTimeout(function () { - makeBSS('.bss-slides', options) - }, 1000) // Must run after the code which called this + dom.defaultView.requestAnimationFrame(function () { + makeBSS(`#${id}`, options) + }) return div } diff --git a/test/unit/navigation/chrome.test.ts b/test/unit/navigation/chrome.test.ts index 0c2e16fa..c06052b5 100644 --- a/test/unit/navigation/chrome.test.ts +++ b/test/unit/navigation/chrome.test.ts @@ -223,6 +223,11 @@ describe('file explorer header', () => { // A pane opened in place of the folder keeps its resource header expect(explorerHeaderForPane(true, 'source', 'explorer')).toBe(true) }) + + it('retains the header for slideshow in solo and regular views', () => { + expect(explorerHeaderForPane(true, 'slideshow', undefined, true)).toBe(true) + expect(explorerHeaderForPane(true, 'slideshow')).toBe(true) + }) }) describe('storage view', () => { diff --git a/test/unit/outline/manager.test.ts b/test/unit/outline/manager.test.ts index 2c08c86e..4be638ed 100644 --- a/test/unit/outline/manager.test.ts +++ b/test/unit/outline/manager.test.ts @@ -16,6 +16,7 @@ vi.mock('solid-ui', async () => { return { ...actual, getRelevantPanes: async (_subject, context) => context.session.paneRegistry.list, + getPreferredPane: (subject, _context, panes) => panes.find(pane => pane.shouldGetFocus?.(subject)) || panes[0], login: { ...actual.login, filterAvailablePanes: async panes => panes