Remove demo status line, document context menu, release 0.2.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 18:59:24 -07:00
co-authored by Claude Opus 5.5
parent d8b8012e40
commit 0c10f4b79d
9 changed files with 41 additions and 41 deletions
+25
View File
@@ -58,12 +58,36 @@ Directories are sorted before files. Names use natural, case-insensitive sorting
| --- | --- | | --- | --- |
| `browser.entries` | Gets or replaces the current directory's entries. | | `browser.entries` | Gets or replaces the current directory's entries. |
| `browser.path` | Gets or sets the current directory path, for example `['home', 'ubuntu']`. `..` is shown when it is not empty. | | `browser.path` | Gets or sets the current directory path, for example `['home', 'ubuntu']`. `..` is shown when it is not empty. |
| `browser.contextMenu` | Gets or sets the context menu items. Empty by default. |
| `empty-label` | Attribute controlling the empty-directory message. | | `empty-label` | Attribute controlling the empty-directory message. |
| `octopus:open` | Event with `{ entry, path }` when a file is selected. | | `octopus:open` | Event with `{ entry, path }` when a file is selected. |
| `octopus:navigate` | Event with `{ path }` when a directory or `..` is selected. The browser does not change until your app sets `path` and `entries`. | | `octopus:navigate` | Event with `{ path }` when a directory or `..` is selected. The browser does not change until your app sets `path` and `entries`. |
The package also exports `OctopusFileBrowser`, `createOctopusFileBrowser`, `sortEntries`, `formatFileSize`, `formatRelativeDate`, `isRecent`, and `fileKindLabel`. Type declarations are included. The package also exports `OctopusFileBrowser`, `createOctopusFileBrowser`, `sortEntries`, `formatFileSize`, `formatRelativeDate`, `isRecent`, and `fileKindLabel`. Type declarations are included.
## Context menu
The context menu is off until you set items. Right-click, the Menu key, or a touch long press on a file or directory opens it.
```js
browser.contextMenu = [
{ label: 'Copy path', action: (entry, path) => navigator.clipboard.writeText('/' + path.join('/')) },
{ label: 'New folder…', when: (entry) => entry.type === 'directory', action: (entry, path) => createFolder(path) },
{ label: 'Download', when: (entry) => entry.type === 'file', action: (entry, path) => download(path) },
{ label: 'Validate JSON', when: (entry) => entry.name.endsWith('.json'), action: (entry, path) => validate(path) },
{ label: 'Delete', danger: true, action: (entry, path) => remove(path) },
];
```
| Field | Type | Description |
| --- | --- | --- |
| `label` | `string` | Menu item text. |
| `action` | `(entry, path) => void` | Called when the item is selected. |
| `when` | `(entry, path) => boolean` | Optional. Shows the item only for entries it returns `true` for. Items without `when` show for every entry. |
| `danger` | `boolean` | Optional. Styles the item as destructive. |
Items appear in the order given. When no item matches an entry, the native context menu is shown instead.
## Theme ## Theme
Set CSS custom properties on the element: Set CSS custom properties on the element:
@@ -77,6 +101,7 @@ octopus-file-browser {
--ofb-hover: #f6f8fa; --ofb-hover: #f6f8fa;
--ofb-focus: #0969da; --ofb-focus: #0969da;
--ofb-recent: #d15700; --ofb-recent: #d15700;
--ofb-danger: #cf222e;
} }
``` ```
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
:root{color:#181c21;font-synthesis:none;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif}body{min-width:280px;margin:0}main{max-width:1220px;margin:0 auto;padding:20px}@media (width<=640px){main{padding:8px}}.demo-status{color:#57606a;min-height:20px;margin:8px 2px 0;font-size:13px} :root{color:#181c21;font-synthesis:none;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif}body{min-width:280px;margin:0}main{max-width:1220px;margin:0 auto;padding:20px}@media (width<=640px){main{padding:8px}}
+2 -3
View File
@@ -6,13 +6,12 @@
<meta name="color-scheme" content="light"> <meta name="color-scheme" content="light">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<title>octopus-file-browser</title> <title>octopus-file-browser</title>
<script type="module" crossorigin src="/assets/index-BJSID0yX.js"></script> <script type="module" crossorigin src="/assets/index-DfcMjrKO.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Bx92WdqS.css"> <link rel="stylesheet" crossorigin href="/assets/index-rQJBLWWV.css">
</head> </head>
<body> <body>
<main> <main>
<octopus-file-browser></octopus-file-browser> <octopus-file-browser></octopus-file-browser>
<p class="demo-status" role="status">Right-click or long-press a file or folder.</p>
</main> </main>
</body> </body>
</html> </html>
-1
View File
@@ -10,7 +10,6 @@
<body> <body>
<main> <main>
<octopus-file-browser></octopus-file-browser> <octopus-file-browser></octopus-file-browser>
<p class="demo-status" role="status">Right-click or long-press a file or folder.</p>
</main> </main>
<script type="module" src="/src/demo.js"></script> <script type="module" src="/src/demo.js"></script>
</body> </body>
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "octopus-file-browser", "name": "octopus-file-browser",
"version": "0.1.0", "version": "0.2.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "octopus-file-browser", "name": "octopus-file-browser",
"version": "0.1.0", "version": "0.2.0",
"license": "SEE LICENSE IN LICENSE", "license": "SEE LICENSE IN LICENSE",
"devDependencies": { "devDependencies": {
"material-icon-theme": "5.38.1", "material-icon-theme": "5.38.1",
+4 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "octopus-file-browser", "name": "octopus-file-browser",
"version": "0.1.0", "version": "0.2.0",
"description": "A compact, framework-free file browser inspired by Gitea's repository view.", "description": "A compact, framework-free file browser inspired by Gitea's repository view.",
"type": "module", "type": "module",
"main": "./dist/octopus-file-browser.umd.cjs", "main": "./dist/octopus-file-browser.umd.cjs",
@@ -42,7 +42,9 @@
"homepage": "https://github.com/multipleof4/octopus-file-browser#readme", "homepage": "https://github.com/multipleof4/octopus-file-browser#readme",
"bugs": "https://github.com/multipleof4/octopus-file-browser/issues", "bugs": "https://github.com/multipleof4/octopus-file-browser/issues",
"funding": "https://github.com/sponsors/multipleof4", "funding": "https://github.com/sponsors/multipleof4",
"publishConfig": { "access": "public" }, "publishConfig": {
"access": "public"
},
"devDependencies": { "devDependencies": {
"material-icon-theme": "5.38.1", "material-icon-theme": "5.38.1",
"vite": "8.3.0" "vite": "8.3.0"
-7
View File
@@ -15,10 +15,3 @@ main {
@media (max-width: 640px) { @media (max-width: 640px) {
main { padding: 8px; } main { padding: 8px; }
} }
.demo-status {
color: #57606a;
font-size: 13px;
margin: 8px 2px 0;
min-height: 20px;
}
+6 -24
View File
@@ -1,10 +1,8 @@
import './demo.css'; import './demo.css';
import { formatFileSize } from './octopus-file-browser.js'; import './octopus-file-browser.js';
import tree from 'virtual:octopus-repo-tree'; import tree from 'virtual:octopus-repo-tree';
const browser = document.querySelector('octopus-file-browser'); const browser = document.querySelector('octopus-file-browser');
const status = document.querySelector('.demo-status');
const say = (text) => { status.textContent = text; };
const pathText = (path) => `/${path.join('/')}`; const pathText = (path) => `/${path.join('/')}`;
const isDirectory = (entry) => entry.type === 'directory'; const isDirectory = (entry) => entry.type === 'directory';
@@ -34,41 +32,27 @@ browser.contextMenu = [
when: isDirectory, when: isDirectory,
action: (entry, path) => { action: (entry, path) => {
const name = prompt(`New folder in ${pathText(path)}`)?.trim(); const name = prompt(`New folder in ${pathText(path)}`)?.trim();
if (!name) return; if (name) directory(path).push({ name, type: 'directory', modified: new Date().toISOString(), children: [] });
directory(path).push({ name, type: 'directory', modified: new Date().toISOString(), children: [] });
say(`Created ${pathText([...path, name])}`);
}, },
}, },
{ {
label: 'Download', label: 'Download',
when: (entry) => entry.type === 'file', when: (entry) => entry.type === 'file',
action: (entry, path) => say(`"Download" on ${pathText(path)} (${formatFileSize(entry.size)})`), action: (entry, path) => console.log('Download', pathText(path)),
}, },
{ {
label: 'Validate JSON', label: 'Validate JSON',
when: (entry) => entry.type === 'file' && entry.name.toLowerCase().endsWith('.json'), when: (entry) => entry.type === 'file' && entry.name.toLowerCase().endsWith('.json'),
action: (entry, path) => say(`"Validate JSON" on ${pathText(path)}`), action: (entry, path) => console.log('Validate JSON', pathText(path)),
},
{
label: 'Copy path',
action: async (entry, path) => {
try {
await navigator.clipboard.writeText(pathText(path));
say(`Copied ${pathText(path)}`);
} catch {
say(`Could not copy ${pathText(path)}`);
}
},
}, },
{ label: 'Copy path', action: (entry, path) => navigator.clipboard?.writeText(pathText(path)).catch(() => {}) },
{ {
label: 'Rename…', label: 'Rename…',
action: (entry, path) => { action: (entry, path) => {
const name = prompt(`Rename ${entry.name}`, entry.name)?.trim(); const name = prompt(`Rename ${entry.name}`, entry.name)?.trim();
if (!name || name === entry.name) return; if (!name || name === entry.name || list(browser.path).some((sibling) => sibling.name === name)) return;
if (list(browser.path).some((sibling) => sibling.name === name)) return say(`${name} already exists`);
stored(path).name = name; stored(path).name = name;
browser.entries = list(browser.path); browser.entries = list(browser.path);
say(`Renamed ${pathText(path)} to ${name}`);
}, },
}, },
{ {
@@ -79,12 +63,10 @@ browser.contextMenu = [
const siblings = directory(path.slice(0, -1)); const siblings = directory(path.slice(0, -1));
siblings.splice(siblings.indexOf(stored(path)), 1); siblings.splice(siblings.indexOf(stored(path)), 1);
browser.entries = list(browser.path); browser.entries = list(browser.path);
say(`Deleted ${pathText(path)}`);
}, },
}, },
]; ];
browser.addEventListener('octopus:navigate', ({ detail }) => show(detail.path)); browser.addEventListener('octopus:navigate', ({ detail }) => show(detail.path));
browser.addEventListener('octopus:open', ({ detail }) => say(`Opened ${pathText(detail.path)}`));
show([]); show([]);