diff --git a/package-lock.json b/package-lock.json index 5e600704..8261d962 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "rdflib": "2.4.0", "solid-logic": "6.0.0-0", "solid-namespace": "^0.5.4", - "solid-ui": "5.0.0-0", + "solid-ui": "5.0.0-1", "source-pane": "4.0.0-0" }, "devDependencies": { @@ -2187,9 +2187,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.0.tgz", - "integrity": "sha512-5+5LEmFuY1AjXdYhmgjTJogtQnP1evJ1zrBZGUNZ0thkpwnnmKxcHdAMn/OtFjAb25zA+jKDVYVRl+5G7rjv1A==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.1.tgz", + "integrity": "sha512-YpAJZhaHplYQkG8ib+/Fx5Y0eF2lVWi3tIvMJA6i39TLyUNp2439cifzW8VMjhlqrBjHzK5hVGugRRm2zTKI/A==", "devOptional": true, "funding": [ { @@ -2238,9 +2238,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.8.tgz", - "integrity": "sha512-CpMLjAvwQg3BL5S0IeqsZNMH7EQrEWi0kLKOC13ZBF0ZwERiLWlibNPJr8G1kdU3Ms/r2KiNrF81pUh2HwAHdg==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.9.tgz", + "integrity": "sha512-iGGw4OsAYsS6pD29MdJ2bX/nJx65a04ZZiw6x+VwWlP2DdXf6f++Zmuv/OzALpdyfVhjbduIIF2cXM7HWBIe9A==", "devOptional": true, "funding": [ { @@ -3559,17 +3559,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", - "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.68.0.tgz", + "integrity": "sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/type-utils": "8.67.0", - "@typescript-eslint/utils": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/type-utils": "8.68.0", + "@typescript-eslint/utils": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -3582,7 +3582,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.67.0", + "@typescript-eslint/parser": "^8.68.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -3598,15 +3598,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", - "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.68.0.tgz", + "integrity": "sha512-fHq2VC1kpyYfvEcbiMjOpySY4WS7voEp89yAThrHRX5sm9j2lzYppCb2umFMEed4fWcyeLjHxrz0mpjNBaBxMQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", "debug": "^4.4.3" }, "engines": { @@ -3622,13 +3622,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", - "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.68.0.tgz", + "integrity": "sha512-5GQtWZCXFcFYux955pvoS02WLc49pXNlvIxocKjS0clvwo3in1RdlzVKyiqQH9vE5AKWFLTaUgeQkOrTS+0Qxw==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.67.0", - "@typescript-eslint/types": "^8.67.0", + "@typescript-eslint/tsconfig-utils": "^8.68.0", + "@typescript-eslint/types": "^8.68.0", "debug": "^4.4.3" }, "engines": { @@ -3643,13 +3643,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", - "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.68.0.tgz", + "integrity": "sha512-T5eXpcaJNg8bhjHJ8Rjp68Vq/QBteYtTKY8TZqVNPaUbuz0f6jI9t6aDkylwvalpAB9XTTFeFOjrjXAZ3YvmVA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0" + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3660,9 +3660,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", - "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.68.0.tgz", + "integrity": "sha512-F7zrGQfiJHojPwi8vhxZQC1tWtJzvL74cK/nqri2lk8YUXvYaYwl263xOJ69jDWPUk1hmcdoayFwk9lX09npVw==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3676,15 +3676,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", - "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.68.0.tgz", + "integrity": "sha512-X77zqoY1EjeWGs/0JNxeaMfp5C5lIz4Tw8y66F1Ne8Faq6g424sBNYM6xBAqElfGZPLpWS+CZAp0DXyKDzWiHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0", + "@typescript-eslint/utils": "8.68.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -3701,9 +3701,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", - "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.68.0.tgz", + "integrity": "sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3714,15 +3714,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", - "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.68.0.tgz", + "integrity": "sha512-OKKsD0tYmoNiU5PW2zehO1yO56jYOm1ShYlxon/Z0SJNidAkdVg86eg9ruRuoXf8xfnuWZGbwDsStkoXbZtIIA==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.67.0", - "@typescript-eslint/tsconfig-utils": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", + "@typescript-eslint/project-service": "8.68.0", + "@typescript-eslint/tsconfig-utils": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/visitor-keys": "8.68.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3741,16 +3741,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", - "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.68.0.tgz", + "integrity": "sha512-PB5gJMMOg0Q5P1tsgWtEAqQacJXq0qEqRHDX/YJ4FaTMLfZPpHB3gjl2EJuiZyPABxmj4ZQYiY9m1bdAJ5y7tQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0" + "@typescript-eslint/scope-manager": "8.68.0", + "@typescript-eslint/types": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3765,12 +3765,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", - "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.68.0.tgz", + "integrity": "sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/types": "8.68.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -4061,6 +4061,42 @@ "timeago.js": "^4.0.2" } }, + "node_modules/activitystreams-pane/node_modules/solid-ui": { + "version": "5.0.0-0", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-0.tgz", + "integrity": "sha512-AHHhqeUcggQmudCHKi90ugJMdEHtFsT46MSl7oNbM/TGW2WpkpQv1d17Z4NU1ezq3YFhJPEK7j9iea5Nx5lHMw==", + "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.0-0", + "rdflib": "2.4.0", + "solid-logic": "6.0.0-0", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -4409,9 +4445,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.18", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.18.tgz", - "integrity": "sha512-1iEmLEYSiE1SeBoAfPo/Mnx3PzfzHUkDK61ASkCpuk3YXugYLH5DYK1SzqV55F8FMI6s0F+/tCP7Polz1QRjxw==", + "version": "2.11.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz", + "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4579,9 +4615,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001809", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", - "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -4660,6 +4696,42 @@ "vitest-axe": "^0.1.0" } }, + "node_modules/chat-pane/node_modules/solid-ui": { + "version": "5.0.0-0", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-0.tgz", + "integrity": "sha512-AHHhqeUcggQmudCHKi90ugJMdEHtFsT46MSl7oNbM/TGW2WpkpQv1d17Z4NU1ezq3YFhJPEK7j9iea5Nx5lHMw==", + "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.0-0", + "rdflib": "2.4.0", + "solid-logic": "6.0.0-0", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -4747,6 +4819,42 @@ "solid-ui": "5.0.0-0" } }, + "node_modules/contacts-pane/node_modules/solid-ui": { + "version": "5.0.0-0", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-0.tgz", + "integrity": "sha512-AHHhqeUcggQmudCHKi90ugJMdEHtFsT46MSl7oNbM/TGW2WpkpQv1d17Z4NU1ezq3YFhJPEK7j9iea5Nx5lHMw==", + "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.0-0", + "rdflib": "2.4.0", + "solid-logic": "6.0.0-0", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5162,9 +5270,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.412", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz", - "integrity": "sha512-z4rMe3esBzlzovKHj4gxJnsCGZRK5l4baUvm+gCGJBPE+gsyUMKsuU9tnEUtI1dOebXz1ytAPGjvXhmQ7rIPwA==", + "version": "1.5.415", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz", + "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==", "dev": true, "license": "ISC" }, @@ -7213,6 +7321,42 @@ "node": ">=20.19.2" } }, + "node_modules/issue-pane/node_modules/solid-ui": { + "version": "5.0.0-0", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-0.tgz", + "integrity": "sha512-AHHhqeUcggQmudCHKi90ugJMdEHtFsT46MSl7oNbM/TGW2WpkpQv1d17Z4NU1ezq3YFhJPEK7j9iea5Nx5lHMw==", + "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.0-0", + "rdflib": "2.4.0", + "solid-logic": "6.0.0-0", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -8123,9 +8267,9 @@ } }, "node_modules/marked": { - "version": "18.0.10", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.10.tgz", - "integrity": "sha512-FJeH4bRpYoXiggcgriCGItKCSv3xkngJc4QCZ/rkQCogU3VYaLxYJoZl8Nw/b4+x7iij/pd+09mZ6A1dXzpL0A==", + "version": "18.0.11", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.11.tgz", + "integrity": "sha512-HnslJfsZkRPBDJRHvVtAaWlZHEpSu7u8LgQuJCELjRKuWR+hpq4A7sLq3p8HaI9ypVoXDXxV34CsQJEe1+J5Aw==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -8162,6 +8306,42 @@ "solid-ui": "5.0.0-0" } }, + "node_modules/meeting-pane/node_modules/solid-ui": { + "version": "5.0.0-0", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-0.tgz", + "integrity": "sha512-AHHhqeUcggQmudCHKi90ugJMdEHtFsT46MSl7oNbM/TGW2WpkpQv1d17Z4NU1ezq3YFhJPEK7j9iea5Nx5lHMw==", + "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.0-0", + "rdflib": "2.4.0", + "solid-logic": "6.0.0-0", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -8284,9 +8464,9 @@ "license": "MIT" }, "node_modules/n3": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/n3/-/n3-2.2.5.tgz", - "integrity": "sha512-lR/0N7zVayC6N0uWqrMkPpJn8Up4+qBsjWiBnoOWWEi/ldTcygs2Dw1iAgozzDXLW/fcxXFfbMzpGHlc/1MBLQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/n3/-/n3-2.6.0.tgz", + "integrity": "sha512-iG8yKHcAZNxI+ModlltpLXncdzdgNZWFqA7Hux/p9VSPqyiK2ET5c7Q9+aka/6nWuuamapFw8L4xN4ZFx9KBqQ==", "license": "MIT", "dependencies": { "buffer": "^6.0.3", @@ -9035,9 +9215,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "license": "MIT", "engines": { "node": ">=12" @@ -9255,6 +9435,42 @@ "vitest-axe": "^0.1.0" } }, + "node_modules/profile-pane/node_modules/solid-ui": { + "version": "5.0.0-0", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-0.tgz", + "integrity": "sha512-AHHhqeUcggQmudCHKi90ugJMdEHtFsT46MSl7oNbM/TGW2WpkpQv1d17Z4NU1ezq3YFhJPEK7j9iea5Nx5lHMw==", + "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.0-0", + "rdflib": "2.4.0", + "solid-logic": "6.0.0-0", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -10076,9 +10292,9 @@ "license": "MIT" }, "node_modules/solid-ui": { - "version": "5.0.0-0", - "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-0.tgz", - "integrity": "sha512-AHHhqeUcggQmudCHKi90ugJMdEHtFsT46MSl7oNbM/TGW2WpkpQv1d17Z4NU1ezq3YFhJPEK7j9iea5Nx5lHMw==", + "version": "5.0.0-1", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-1.tgz", + "integrity": "sha512-0wDAw3vjj79yXv9qcmPnqHML3t7ZyvoczZPyuHfPoUND3SePkC8wPt+7gAkGfGZCLTiIe+/pY6kzEFs2trUabg==", "license": "MIT", "dependencies": { "@awesome.me/webawesome": "^3.9.0", @@ -10188,6 +10404,42 @@ "vitest-axe": "^0.1.0" } }, + "node_modules/source-pane/node_modules/solid-ui": { + "version": "5.0.0-0", + "resolved": "https://registry.npmjs.org/solid-ui/-/solid-ui-5.0.0-0.tgz", + "integrity": "sha512-AHHhqeUcggQmudCHKi90ugJMdEHtFsT46MSl7oNbM/TGW2WpkpQv1d17Z4NU1ezq3YFhJPEK7j9iea5Nx5lHMw==", + "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.0-0", + "rdflib": "2.4.0", + "solid-logic": "6.0.0-0", + "solid-namespace": "^0.5.4", + "uuid": "^14.0.0" + }, + "optionalDependencies": { + "fsevents": "*" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -10543,22 +10795,22 @@ } }, "node_modules/tldts": { - "version": "7.4.10", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.10.tgz", - "integrity": "sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==", + "version": "7.4.11", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.11.tgz", + "integrity": "sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==", "devOptional": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.4.10" + "tldts-core": "^7.4.11" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.4.10", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.10.tgz", - "integrity": "sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==", + "version": "7.4.11", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.11.tgz", + "integrity": "sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==", "devOptional": true, "license": "MIT" }, @@ -10748,16 +11000,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", - "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.68.0.tgz", + "integrity": "sha512-MHy0Y0ynqeEbx/S45+i/bBssdy3X6KNBfmJAP35GrgtNxu2TQ5K5xsFDhAnmsq1jvpdoZOPG1LGtJo0HWqYCrQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.67.0", - "@typescript-eslint/parser": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/utils": "8.67.0" + "@typescript-eslint/eslint-plugin": "8.68.0", + "@typescript-eslint/parser": "8.68.0", + "@typescript-eslint/typescript-estree": "8.68.0", + "@typescript-eslint/utils": "8.68.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11189,9 +11441,9 @@ } }, "node_modules/vite-plugin-lit-css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vite-plugin-lit-css/-/vite-plugin-lit-css-3.0.0.tgz", - "integrity": "sha512-3cp46w6OuPbYSICwQBqLqqV62KojqopeqF7nYqxlZlJhhLO8phB8kku7LR986uEVQibfCuEhuRz9V6jtBQENwg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-lit-css/-/vite-plugin-lit-css-3.1.0.tgz", + "integrity": "sha512-hrOvv1ZHOBlc+ETAGCneg2gPqTIwojyZ127+L4P6Cc5adsu1yffbjgE+c608JYNFdyLxKDe0yzVTFpOFZMwL2w==", "dev": true, "engines": { "node": ">=18.2.0" @@ -11320,9 +11572,9 @@ } }, "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 4b199aec..0b6f18f2 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "rdflib": "2.4.0", "solid-logic": "6.0.0-0", "solid-namespace": "^0.5.4", - "solid-ui": "5.0.0-0", + "solid-ui": "5.0.0-1", "source-pane": "4.0.0-0" }, "overrides": { diff --git a/src/components/file-explorer-header/FileExplorerHeader.styles.css b/src/components/file-explorer-header/FileExplorerHeader.styles.css index d75d59f4..866bd8cb 100644 --- a/src/components/file-explorer-header/FileExplorerHeader.styles.css +++ b/src/components/file-explorer-header/FileExplorerHeader.styles.css @@ -7,6 +7,7 @@ flex-wrap: nowrap; width: 100%; text-align: right; - padding: 15px; + padding: 10px; + border-bottom: 1px solid var(--solid-ui-color-gray-200, #cbd5e1); } } diff --git a/src/components/file-explorer-header/FileExplorerHeader.ts b/src/components/file-explorer-header/FileExplorerHeader.ts index 136cdc3f..b51c4d82 100644 --- a/src/components/file-explorer-header/FileExplorerHeader.ts +++ b/src/components/file-explorer-header/FileExplorerHeader.ts @@ -10,8 +10,8 @@ import styles from './FileExplorerHeader.styles.css' import './FileExplorerHeaderSummary' import './FileExplorerHeaderControls' import { PaneIcon } from './types' -import { fetchContentAndMetadata, type FileExplorerResourceMetadata } from './helper' - +import { fetchResourceMetadata } from '../../utils/podUtils' +import { type FileExplorerResourceMetadata } from './types' @customElement('file-explorer-header') export default class FileExplorerHeader extends WebComponent { static styles = styles @@ -55,7 +55,7 @@ export default class FileExplorerHeader extends WebComponent { if (!this.fileExplorerContext?.store || !this.fileExplorerContext.subjectUri) return try { - const { metadata } = await fetchContentAndMetadata(this.fileExplorerContext.store, sym(this.fileExplorerContext.subjectUri)) + const metadata = await fetchResourceMetadata(this.fileExplorerContext.store, sym(this.fileExplorerContext.subjectUri)) this.responseMetadata = { modified: metadata.modified, isPublic: metadata.isPublic, diff --git a/src/components/file-explorer-header/FileExplorerHeaderControls.styles.css b/src/components/file-explorer-header/FileExplorerHeaderControls.styles.css index d962f83b..626163df 100644 --- a/src/components/file-explorer-header/FileExplorerHeaderControls.styles.css +++ b/src/components/file-explorer-header/FileExplorerHeaderControls.styles.css @@ -31,6 +31,12 @@ white-space: nowrap; } + icon-lucide-share-2, + icon-lucide-pencil { + width: 16px; + height: 16px; + } + @media (max-width: 900px) { div { padding-right: 3px; @@ -42,5 +48,17 @@ justify-content: flex-start; gap: 5px; } + + .file-explorer-header-access-button, + .file-explorer-header-edit-button { + display: none; + } + + icon-lucide-ellipsis-vertical { + width: 15.36px; + height: 15.36px; + flex-shrink: 0; + aspect-ratio: 1 / 1; + } } } diff --git a/src/components/file-explorer-header/FileExplorerHeaderControls.ts b/src/components/file-explorer-header/FileExplorerHeaderControls.ts index b6668ce4..5d5364dc 100644 --- a/src/components/file-explorer-header/FileExplorerHeaderControls.ts +++ b/src/components/file-explorer-header/FileExplorerHeaderControls.ts @@ -1,5 +1,5 @@ import { WebComponent } from 'solid-ui' -import { customElement, property } from 'lit/decorators.js' +import { customElement, property, state } from 'lit/decorators.js' import { consume } from '@lit/context' import { html, nothing } from 'lit' import 'solid-ui/components/button' @@ -8,20 +8,50 @@ import '~icons/lucide/pencil' import styles from './FileExplorerHeaderControls.styles.css' import '../resource-actions-menu/ResourceActionsMenu' import { fileExplorerContext, type FileExplorerContext } from 'solid-ui' +import { isContainerSubject } from '../../utils/podUtils' @customElement('file-explorer-header-controls') export default class FileExplorerHeaderControls extends WebComponent { static styles = styles + private mobileMediaQuery: MediaQueryList | undefined + private readonly mobileQuery = '(max-width: 600px)' + private readonly handleMobileMediaChange = (event: MediaQueryListEvent) => { + this.isMobile = event.matches + } + @consume({ context: fileExplorerContext, subscribe: true }) accessor fileExplorerContext: FileExplorerContext = undefined as unknown as FileExplorerContext @property({ attribute: false }) - accessor menuItems: Array<{ label: string, action: (event: Event) => void }> = [] + accessor menuItems: Array<{ label: string, action: (event: Event) => void, icon?: HTMLElement }> = [] @property({ type: Boolean }) accessor canEdit: boolean = false + @state() + accessor isMobile = typeof window !== 'undefined' && typeof window.matchMedia === 'function' + ? window.matchMedia('(max-width: 600px)').matches + : false + + connectedCallback () { + super.connectedCallback() + + if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { + return + } + + this.mobileMediaQuery = window.matchMedia(this.mobileQuery) + this.isMobile = this.mobileMediaQuery.matches + this.mobileMediaQuery.addEventListener('change', this.handleMobileMediaChange) + } + + disconnectedCallback () { + this.mobileMediaQuery?.removeEventListener('change', this.handleMobileMediaChange) + this.mobileMediaQuery = undefined + super.disconnectedCallback() + } + // TODO: Add broken then use this function to set tooltip and disable edit button /* private setEditable() { const sourcePaneState = this.sourceContext?.sourcePaneState @@ -32,10 +62,6 @@ export default class FileExplorerHeaderControls extends WebComponent { this.sourceContext?.setEditing?.() } */ - private handleEditingClick () { - this.fileExplorerContext.edit?.onEdit?.() - } - private getEditTooltip () { if (!this.fileExplorerContext.paneSupportsEditing) return 'Not Supported' if (!this.canEdit) return 'No Access' @@ -49,24 +75,36 @@ export default class FileExplorerHeaderControls extends WebComponent { } render () { + const isContainerResource = isContainerSubject(this.fileExplorerContext.store, this.fileExplorerContext.subjectUri) + return html`
${this.renderDirtyIndicator()} - - - - - - + ${!isContainerResource && !this.isMobile + ? html` + + + + + + + ` + : nothing}
` diff --git a/src/components/file-explorer-header/FileExplorerHeaderSummary.styles.css b/src/components/file-explorer-header/FileExplorerHeaderSummary.styles.css index 0fce991a..db9952b7 100644 --- a/src/components/file-explorer-header/FileExplorerHeaderSummary.styles.css +++ b/src/components/file-explorer-header/FileExplorerHeaderSummary.styles.css @@ -12,8 +12,25 @@ h1 { color: var(--solid-ui-color-gray-700, #364153); - font-size: var(--solid-ui-font-size-2xl, 1.5rem); + font-size: var(--solid-ui-font-size-xl, 1.25rem); /* while in expand; will go to 2xl when new design is complete */ font-weight: 500; + display: flex; + align-items: center; + gap: 8px; + margin: 0; + } + + .resource-info { + display: flex; + flex-direction: column; + min-width: 0; + } + + .container-info { + display: flex; + flex-direction: row; + gap: 10px; + min-width: 0; } .pane-icon { @@ -82,8 +99,50 @@ height: 0.875rem; } + .resource-date { + font-size: inherit; + } + icon-lucide-arrow-left { width: 1.125rem; height: 1.125rem; } + + @media (max-width: 600px) { + .file-explorer-header-summary { + gap: 7px; + } + + h1 { + font-size: 14px; + } + + .pane-icon { + width: 18.2px; + height: 18.2px; + flex-shrink: 0; + aspect-ratio: 1 / 1; + padding: 0; + } + + p { + font-size: 14px; + } + + .resource-date { + font-size: 10px; + } + + .public, + .private { + font-size: 9px; + } + + icon-lucide-arrow-left { + width: 15px; + height: 18px; + flex-shrink: 0; + aspect-ratio: 5 / 6; + } + } } diff --git a/src/components/file-explorer-header/FileExplorerHeaderSummary.ts b/src/components/file-explorer-header/FileExplorerHeaderSummary.ts index a02b6947..2ca785d5 100644 --- a/src/components/file-explorer-header/FileExplorerHeaderSummary.ts +++ b/src/components/file-explorer-header/FileExplorerHeaderSummary.ts @@ -8,8 +8,10 @@ import { PaneIcon } from './types' import '~icons/lucide/globe' import '~icons/lucide/lock-keyhole' import '~icons/lucide/arrow-left' +import '~icons/lucide/folder' import styles from './FileExplorerHeaderSummary.styles.css' -import { type FileExplorerResourceMetadata } from './helper' +import { getContainerItemCount, isContainerSubject } from '../../utils/podUtils' +import type { FileExplorerResourceMetadata } from './types' @customElement('file-explorer-header-summary') export default class FileExplorerHeaderSummary extends WebComponent { @@ -93,11 +95,41 @@ export default class FileExplorerHeaderSummary extends WebComponent { } } + private renderContainerResourceHeader (label: string, isPublic: boolean) { + const itemCount = getContainerItemCount(this.fileExplorerContext?.store, this.fileExplorerContext?.subjectUri) ?? 0 + return html` +
+

+ ${label} +

+

+ ${itemCount} items + ${isPublic + ? html`` + : html``} +

+
+ ` + } + + private renderResourceHeader (label: string, isPublic: boolean) { + const modified = this.formatModifiedDate(this.responseMetadata.modified) + + return html` +
+

+ ${label} +

+

${modified} ${isPublic ? html` Public` : html` Private`}

+
+ ` + } + render () { const subject = this.fileExplorerContext?.subjectUri ? sym(this.fileExplorerContext.subjectUri) : undefined const label = subject ? utils.label(subject) : '' - const modified = this.formatModifiedDate(this.responseMetadata.modified) const isPublic = this.responseMetadata.isPublic + const isContainerResource = isContainerSubject(this.fileExplorerContext?.store, this.fileExplorerContext?.subjectUri) return html`
@@ -111,10 +143,8 @@ export default class FileExplorerHeaderSummary extends WebComponent { ${this.resolvedPaneIcon ? html`` : ''} -
-

${label}

-

${modified} ${isPublic ? html` Public` : html` Private`}

-
+ ${isContainerResource ? this.renderContainerResourceHeader(label, isPublic) : this.renderResourceHeader(label, isPublic)} +
` } diff --git a/src/components/file-explorer-header/FileExplorerProvider.ts b/src/components/file-explorer-header/FileExplorerProvider.ts index 07ea4c24..3696b3e5 100644 --- a/src/components/file-explorer-header/FileExplorerProvider.ts +++ b/src/components/file-explorer-header/FileExplorerProvider.ts @@ -9,6 +9,7 @@ import './FileExplorerHeader' import styles from './FileExplorerProvider.styles.css' import personIcon from '../../icons/person.svg' import friendsIcon from '../../icons/friends.svg' +import '~icons/lucide/share-2' const PERSON_ICON = personIcon const FRIENDS_ICON = friendsIcon @@ -20,7 +21,7 @@ function createFileExplorerContextValue (value: { soloPane?: boolean onBack?: () => void openPane?: (subject: NamedNode, paneName: string) => void - handleSharingClick?: () => void + handleAccessClick?: () => void paneSupportsEditing?: boolean edit?: { onEdit?: () => void @@ -35,7 +36,7 @@ function createFileExplorerContextValue (value: { soloPane: value.soloPane, onBack: value.onBack, openPane: value.openPane, - handleSharingClick: value.handleSharingClick, + handleAccessClick: value.handleAccessClick, paneSupportsEditing: value.paneSupportsEditing, edit: value.edit } @@ -66,7 +67,7 @@ export default class FileExplorerProvider extends WebComponent { accessor showHeader: boolean = true @property({ attribute: false }) - accessor handleSharingClick: (() => void) | undefined = undefined + accessor handleAccessClick: (() => void) | undefined = undefined // TODO: Need to research this more, brought it over from manager. @property({ attribute: false }) @@ -125,7 +126,7 @@ export default class FileExplorerProvider extends WebComponent { soloPane: this.soloPane, onBack: this.onBack, openPane: this.openPane, - handleSharingClick: this.handleSharingClick, + handleAccessClick: this.handleAccessClick, paneSupportsEditing: false, edit: this.edit }) @@ -181,7 +182,7 @@ export default class FileExplorerProvider extends WebComponent { soloPane: this.soloPane, onBack: this.onBack, openPane: this.openPane, - handleSharingClick: this.handleSharingClick, + handleAccessClick: this.handleAccessClick, paneSupportsEditing: this.paneSupportsEditing, edit: this.edit }) @@ -228,7 +229,7 @@ export default class FileExplorerProvider extends WebComponent { changedProperties.has('soloPane') || changedProperties.has('onBack') || changedProperties.has('openPane') || - changedProperties.has('handleSharingClick') || + changedProperties.has('handleAccessClick') || changedProperties.has('pane') || changedProperties.has('isDirty') ) { diff --git a/src/components/file-explorer-header/helper.ts b/src/components/file-explorer-header/helper.ts deleted file mode 100644 index 129c30bb..00000000 --- a/src/components/file-explorer-header/helper.ts +++ /dev/null @@ -1,105 +0,0 @@ -// TODO: this code will need to be moved later -import { ACL_LINK } from 'solid-logic' -import { ns } from 'solid-ui' -import type { LiveStore, NamedNode } from 'rdflib' - -export type FileExplorerResourceMetadata = { - contentType: string | undefined - canEdit: boolean - isPublic: boolean - aclUri: string | undefined - eTag: string | undefined - modified: string | undefined -} - -function parseWacAllowHeader (headerValue: string | null | undefined) { - const permissions = new Map>() - if (!headerValue) return permissions - - for (const entry of headerValue.split(',')) { - const match = entry.trim().match(/^([A-Za-z]+)\s*=\s*"([^"]*)"$/) - if (match) { - const [, permissionGroup, accessModes] = match - const modes = accessModes.trim().split(/\s+/).filter(Boolean) - permissions.set(permissionGroup.toLowerCase(), new Set(modes.map(mode => mode.toLowerCase()))) - } - } - - return permissions -} - -function deriveAccessFlags (wacAllow: string | null | undefined) { - if (!wacAllow) { - return { canEdit: false, isPublic: false } - } - - const permissions = parseWacAllowHeader(wacAllow) - const userModes = permissions.get('user') ?? new Set() - const publicModes = permissions.get('public') ?? new Set() - - return { - canEdit: userModes.has('write'), - isPublic: publicModes.has('read') || publicModes.has('write') - } -} - -export function getResponseMetadata (store: LiveStore, subject: NamedNode, response: Response): FileExplorerResourceMetadata { - let contentType: string | undefined - let canEdit = false - let isPublic = false - let eTag: string | undefined - let modified: string | undefined - - if (response.headers && response.headers.get('content-type')) { - contentType = response.headers.get('content-type')?.split(';')[0] ?? undefined - const accessFlags = deriveAccessFlags(response.headers.get('wac-allow')) - - canEdit = accessFlags.canEdit - isPublic = accessFlags.isPublic - eTag = response.headers.get('etag') ?? undefined - modified = store.anyValue(subject as any, ns.dct('modified')) || store.anyValue(subject as any, ns.dc('modified')) || undefined - } else { - const reqs = store.each( - null, - store.sym('http://www.w3.org/2007/ont/link#requestedURI'), - subject - ) - reqs.forEach((req: any) => { - const responseNode = store.any( - req as any, - store.sym('http://www.w3.org/2007/ont/link#response') - ) - if (responseNode && responseNode.termType === 'NamedNode') { - contentType = store.anyValue(responseNode as any, ns.httph('content-type')) || undefined - const wacAllow = (store.anyValue(responseNode as any, ns.httph('wac-allow')) as string | undefined) || - (store.anyValue(responseNode as any, ns.httph('WAC-Allow')) as string | undefined) - const accessFlags = deriveAccessFlags(wacAllow) - canEdit = accessFlags.canEdit - isPublic = accessFlags.isPublic - eTag = store.anyValue(responseNode as any, ns.httph('etag')) || undefined - modified = store.anyValue(subject as any, ns.dct('modified')) || store.anyValue(subject as any, ns.dc('modified')) || undefined - } - }) - } - - const aclUri = store.any(subject, ACL_LINK)?.value || undefined - return { contentType, canEdit, isPublic, aclUri, eTag, modified } -} - -export async function fetchContentAndMetadata (store: LiveStore, subject: NamedNode): Promise<{ content: string, metadata: FileExplorerResourceMetadata }> { - const fetcher = store.fetcher - - const response = await fetcher.webOperation('GET', subject.uri) - const content = (response as Response & { responseText?: string }).responseText - - if (content === undefined) { - throw new Error('No text in response object!!') - } - - const metadata = getResponseMetadata(store, subject, response) - if (!metadata.contentType) { - throw new Error('No content-type available!') - } - - return { content, metadata } -} diff --git a/src/components/file-explorer-header/index.ts b/src/components/file-explorer-header/index.ts new file mode 100644 index 00000000..2140b416 --- /dev/null +++ b/src/components/file-explorer-header/index.ts @@ -0,0 +1,2 @@ +export { default as FileExplorerHeader } from './FileExplorerHeader' +export { default as FileExplorerProvider } from './FileExplorerProvider' diff --git a/src/components/file-explorer-header/types.ts b/src/components/file-explorer-header/types.ts index b3b3ecbd..826d13a8 100644 --- a/src/components/file-explorer-header/types.ts +++ b/src/components/file-explorer-header/types.ts @@ -1 +1,10 @@ export type PaneIcon = string | Promise | null | undefined + +export type FileExplorerResourceMetadata = { + contentType: string | undefined + canEdit: boolean + isPublic: boolean + aclUri: string | undefined + eTag: string | undefined + modified: string | undefined +} diff --git a/src/components/resource-actions-menu/ResourceActionsMenu.styles.css b/src/components/resource-actions-menu/ResourceActionsMenu.styles.css new file mode 100644 index 00000000..33a5392f --- /dev/null +++ b/src/components/resource-actions-menu/ResourceActionsMenu.styles.css @@ -0,0 +1,6 @@ +:host { + .ellipsisIcon { + width: 16px; + height: 16px; + } +} diff --git a/src/components/resource-actions-menu/ResourceActionsMenu.ts b/src/components/resource-actions-menu/ResourceActionsMenu.ts index 884b3004..e441e70d 100644 --- a/src/components/resource-actions-menu/ResourceActionsMenu.ts +++ b/src/components/resource-actions-menu/ResourceActionsMenu.ts @@ -1,17 +1,19 @@ -import { showDialog, utils, WebComponent } from 'solid-ui' +import { WebComponent } from 'solid-ui' import { customElement, property } from 'lit/decorators.js' -import { html } from 'lit' +import { html, nothing } from 'lit' import 'solid-ui/components/button' import 'solid-ui/components/menu' import 'solid-ui/components/menu-item' -import DeleteResourceDialog from './DeleteResourceDialog' import '~icons/lucide/ellipsis-vertical' -import '~icons/lucide/trash-2' +import '~icons/lucide/share-2' +import '~icons/lucide/pencil' import { LiveStore } from 'rdflib' -// TODO: Add Error Status section - +import styles from './ResourceActionsMenu.styles.css' +import { isContainerSubject } from '../../utils/podUtils' @customElement('resource-actions-menu') export default class ResourceActionsMenu extends WebComponent { + static styles = styles + @property({ attribute: false }) accessor store: LiveStore | undefined @@ -19,74 +21,31 @@ export default class ResourceActionsMenu extends WebComponent { accessor subjectUri: string | undefined @property({ attribute: false }) - accessor menuItems: Array<{ label: string, icon?: HTMLElement, action: (event: Event) => void }> = [] - - private confirmDelete (resourceName: string) { - return new Promise(resolve => { - showDialog(DeleteResourceDialog, { - props: { resourceName }, - onClose: (result) => resolve(result === true) - }) - }) - } - - /* private gotoParentFolder(resourceUri: string) { - const sourceContext = this.sourceContext - const outliner = sourceContext?.context?.getOutliner?.(sourceContext.context.dom) - - if (!outliner) return - - const parentFolderUri = this.getParentFolderUri(resourceUri) - ;(outliner as any).GotoSubject(sourceContext.context.session.store.sym(parentFolderUri), true, undefined, true, undefined) - } */ + accessor handleAccessClick: (() => void) | undefined = undefined - private async deleteResourceIfPresent (store: LiveStore, uri: string) { - try { - await store.fetcher.webOperation('DELETE', uri) - } catch (err: any) { - const status = err?.response?.status ?? err?.status - if (status === 404) return - throw err - } - } - - // TODO: Below is for a file only. I need to move this to a function - // that function needs to check if it's a container and if so we can do the - // recursive delete function. - // dont' forget public and private type indexes. - private async handleDelete (event: Event) { - event.preventDefault() - - if (!this.store?.fetcher || !this.subjectUri) return - - const store = this.store - const resourceNode = store.sym(this.subjectUri) + @property({ attribute: false }) + accessor handleEditingClick: (() => void) | undefined = undefined - const confirmation = await this.confirmDelete(utils.label(resourceNode)) - if (!confirmation) return + @property({ type: Boolean }) + accessor paneSupportsEditing = false - try { - await this.deleteResourceIfPresent(store, resourceNode.value) + @property({ type: Boolean }) + accessor canEdit = false - const aclUri = this.subjectUri + '.acl' - if (aclUri) { - await this.deleteResourceIfPresent(store, aclUri) - store.removeDocument(store.sym(aclUri)) - } + @property({ type: Boolean }) + accessor isMobile = false - store.removeDocument(resourceNode) - // this.gotoParentFolder(resourceNode.value) - } catch (err) { - // error('Error deleting resource:', err) - // getStatusSection()?.showError('Failed to delete resource. Check console for details.') - } - } + @property({ attribute: false }) + accessor menuItems: Array<{ label: string, icon?: unknown, action: (event: Event) => void }> = [] render () { + const isContainerResource = isContainerSubject(this.store, this.subjectUri) + const canEdit = !isContainerResource && this.isMobile && this.paneSupportsEditing && this.canEdit && !!this.handleEditingClick + const canManageAccess = !!this.handleAccessClick return html` - + ${this.menuItems.map(item => html` item.action(event)}> @@ -94,10 +53,30 @@ export default class ResourceActionsMenu extends WebComponent { ${item.label} `)} - this.handleDelete(event)}> - - Delete - + ${canEdit + ? html` + + + Edit + + ` + : nothing} + ${!isContainerResource && this.isMobile && canManageAccess + ? html` + + + Manage Access + + ` + : nothing} + ${isContainerResource && canManageAccess + ? html` + + + Manage Access + + ` + : nothing} ` } diff --git a/src/outline/manager.css b/src/outline/manager.css index 4915eed5..8e7901e2 100644 --- a/src/outline/manager.css +++ b/src/outline/manager.css @@ -80,3 +80,12 @@ width: 12px; height: 12px; } + +@media (max-width: 600px) { + .obj, + .iconTD { + font-size: 100%; + margin: 0 !important; + vertical-align: top; + } +} diff --git a/src/outline/manager.js b/src/outline/manager.js index 1207ad30..afb06da2 100644 --- a/src/outline/manager.js +++ b/src/outline/manager.js @@ -13,7 +13,7 @@ import { UserInput } from './userInput.js' import * as queryByExample from './queryByExample.js' import { loadContainerRepresentation } from '../utils/podUtils' import { isWebIdUri } from '../utils/webIdUtils' -import '../components/file-explorer-header/FileExplorerProvider' +import '../components/file-explorer-header' export default function (context) { const dom = context.dom @@ -711,10 +711,7 @@ export default function (context) { // 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.classList.add('obj') moreTD.setAttribute('notSelectable', 'false') if (predDups > n) { // what is this for?? @@ -790,23 +787,14 @@ export default function (context) { termWidget.construct = function (dom) { dom = dom || document const td = dom.createElement('TD') - td.setAttribute( - 'style', - 'margin: 0.2em; border: none; padding: 0; vertical-align: top;' - ) td.setAttribute('class', 'iconTD') td.setAttribute('notSelectable', 'true') - td.style.width = '0px' return td } termWidget.addIcon = function (td, icon, listener) { const iconTD = td.childNodes[1] if (!iconTD) return - let width = iconTD.style.width const img = UI.utils.AJARImage(icon.src, icon.alt, icon.tooltip, dom) - width = parseInt(width) - width = width + icon.width - iconTD.style.width = width + 'px' iconTD.appendChild(img) if (listener) { img.addEventListener('click', listener) @@ -816,10 +804,6 @@ export default function (context) { const iconTD = td.childNodes[1] let baseURI if (!iconTD) return - let width = iconTD.style.width - width = parseInt(width) - width = width - icon.width - iconTD.style.width = width + 'px' for (let x = 0; x < iconTD.childNodes.length; x++) { const elt = iconTD.childNodes[x] const eltSrc = elt.src diff --git a/src/utils/podUtils.ts b/src/utils/podUtils.ts index aa7b4aaa..27abd7dd 100644 --- a/src/utils/podUtils.ts +++ b/src/utils/podUtils.ts @@ -1,7 +1,40 @@ import { store } from 'solid-logic' import { ns } from 'solid-ui' -import { NamedNode, parse } from 'rdflib' +import { LiveStore, NamedNode, parse } from 'rdflib' import { isWebIdUri } from './webIdUtils' +import { ACL_LINK } from 'solid-logic' +import { FileExplorerResourceMetadata } from 'src/components/file-explorer-header/types' + +function parseWacAllowHeader (headerValue: string | null | undefined) { + const permissions = new Map>() + if (!headerValue) return permissions + + for (const entry of headerValue.split(',')) { + const match = entry.trim().match(/^([A-Za-z]+)\s*=\s*"([^"]*)"$/) + if (match) { + const [, permissionGroup, accessModes] = match + const modes = accessModes.trim().split(/\s+/).filter(Boolean) + permissions.set(permissionGroup.toLowerCase(), new Set(modes.map(mode => mode.toLowerCase()))) + } + } + + return permissions +} + +function deriveAccessFlags (wacAllow: string | null | undefined) { + if (!wacAllow) { + return { canEdit: false, isPublic: false } + } + + const permissions = parseWacAllowHeader(wacAllow) + const userModes = permissions.get('user') ?? new Set() + const publicModes = permissions.get('public') ?? new Set() + + return { + canEdit: userModes.has('write'), + isPublic: publicModes.has('read') || publicModes.has('write') + } +} export async function getPodStorages (url: NamedNode): Promise { if (isWebIdUri(url)) { @@ -56,3 +89,102 @@ export async function loadContainerRepresentation (subject) { } } } + +export function isContainerSubject (store: LiveStore | undefined, subjectUri: string | undefined): boolean { + if (!store || !subjectUri) return false + + const subject = store.sym(subjectUri) + const typeUris = store.findTypeURIs(subject) + return Boolean( + typeUris[ns.ldp('Container').uri] || + typeUris[ns.ldp('BasicContainer').uri] || + subject.uri.endsWith('/') + ) +} + +// The shared rdflib store can hold duplicate ldp:contains statements for the +// same child resource when the container and companion metadata are both loaded. +// Count unique visible children here so the header summary matches the list UI. +export function getContainerItemCount (store: LiveStore | undefined, subjectUri: string | undefined): number { + if (!store || !subjectUri) return 0 + + const subject = store.sym(subjectUri) + const seen = new Set() + + for (const item of store.each(subject, ns.ldp('contains'))) { + if (item.termType === 'NamedNode') { + const resource = item as NamedNode + const parent = resource.dir() + + if (parent) { + const pathEnd = resource.uri.slice(parent.uri.length) + if ( + !pathEnd.startsWith('.') && + !pathEnd.endsWith('.acl') && + !pathEnd.endsWith('~') + ) { + seen.add(resource.uri) + } + } + } + } + return seen.size +} + +export function getResponseMetadata (store: LiveStore, subject: NamedNode, response: Response): FileExplorerResourceMetadata { + let contentType: string | undefined + let canEdit = false + let isPublic = false + let eTag: string | undefined + let modified: string | undefined + + if (response.headers && response.headers.get('content-type')) { + contentType = response.headers.get('content-type')?.split(';')[0] ?? undefined + const accessFlags = deriveAccessFlags(response.headers.get('wac-allow')) + + canEdit = accessFlags.canEdit + isPublic = accessFlags.isPublic + eTag = response.headers.get('etag') ?? undefined + modified = store.anyValue(subject as any, ns.dct('modified')) || store.anyValue(subject as any, ns.dc('modified')) || undefined + } else { + const reqs = store.each( + null, + store.sym('http://www.w3.org/2007/ont/link#requestedURI'), + subject + ) + reqs.forEach((req: any) => { + const responseNode = store.any( + req as any, + store.sym('http://www.w3.org/2007/ont/link#response') + ) + if (responseNode && responseNode.termType === 'NamedNode') { + contentType = store.anyValue(responseNode as any, ns.httph('content-type')) || undefined + const wacAllow = (store.anyValue(responseNode as any, ns.httph('wac-allow')) as string | undefined) || + (store.anyValue(responseNode as any, ns.httph('WAC-Allow')) as string | undefined) + const accessFlags = deriveAccessFlags(wacAllow) + canEdit = accessFlags.canEdit + isPublic = accessFlags.isPublic + eTag = store.anyValue(responseNode as any, ns.httph('etag')) || undefined + modified = store.anyValue(subject as any, ns.dct('modified')) || store.anyValue(subject as any, ns.dc('modified')) || undefined + } + }) + } + + const aclUri = store.any(subject, ACL_LINK)?.value || undefined + return { contentType, canEdit, isPublic, aclUri, eTag, modified } +} + +export async function fetchResourceMetadata (store: LiveStore, subject: NamedNode): Promise { + const response = await store.fetcher.webOperation('HEAD', subject.uri) + + if (!response.ok) { + throw new Error(`HEAD request failed with status ${response.status}`) + } + + const metadata = getResponseMetadata(store, subject, response) + if (!metadata.contentType) { + throw new Error('No content-type available!') + } + + return metadata +}