diff --git a/README.md b/README.md index 7c67eff0..bec774c2 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ Code examples that accompany various MDN DOM and Web API documentation pages. - The "edit-context" directory contains examples demonstrating the [EditContext API](https://developer.mozilla.org/docs/Web/API/EditContext_API). See the [list of examples](https://github.com/mdn/dom-examples/tree/main/edit-context/). +- The "filesystemobserver" directory contains an example demonstrating usage of the [FileSystemObserver](https://developer.mozilla.org/docs/Web/API/FileSystemObserver) API ([run the example live](https://mdn.github.io/dom-examples/filesystemobserver/)). This example was originally published on Glitch as [File System Observer Demo](https://file-system-observer.glitch.me/) by [Thomas Steiner](https://front-end.social/@tomayac@toot.cafe). + - The "fullscreen-api" directory is for examples and demos of the [Fullscreen API](https://wiki.developer.mozilla.org/docs/Web/API/Fullscreen_API). Run the [example live](https://mdn.github.io/dom-examples/fullscreen-api/). - The "history-api" directory contains an example that demonstrates the [History API](https://developer.mozilla.org/docs/Web/API/History_API). [View the demo live](https://mdn.github.io/dom-examples/history-api/). diff --git a/filesystemobserver/index.html b/filesystemobserver/index.html new file mode 100644 index 00000000..6b078252 --- /dev/null +++ b/filesystemobserver/index.html @@ -0,0 +1,27 @@ + + +
+ + + + + + + +Log of randomly created, deleted, or modified files.
+ +Log of observed file system changes.
+ + + + diff --git a/filesystemobserver/observer.js b/filesystemobserver/observer.js new file mode 100644 index 00000000..6a230388 --- /dev/null +++ b/filesystemobserver/observer.js @@ -0,0 +1,45 @@ +function logMessage(message) { + const logContainer = document.getElementById("observer"); + const logEntry = document.createElement("div"); + logEntry.className = "log-entry"; + logEntry.textContent = message; + + logContainer.appendChild(logEntry); + + // Ensure we only keep the last 10 log entries + while (logContainer.children.length > 10) { + logContainer.removeChild(logContainer.firstChild); + } + + // Scroll to the bottom of the log + logContainer.scrollTop = logContainer.scrollHeight; +} + +const callback = async (records, observer) => { + const icons = { + created: "✅", + appeared: "✅", + modified: "📝", + deleted: "🗑️", + disappeared: "🗑️", + }; + for (const record of records) { + console.log(record); + if (record.changedHandle && record.changedHandle.name.endsWith(".crswap")) { + continue; + } + if (!record.changedHandle) { + continue; + } + logMessage( + `The ${record.changedHandle.kind} "${record.changedHandle.name}" ${ + ["modified", "deleted", "created"].includes(record.type) ? "was " : "" + }${icons[record.type]} ${record.type}` + ); + } +}; + +(async () => { + const observer = new self.FileSystemObserver(callback); + await observer.observe(await navigator.storage.getDirectory()); +})(); diff --git a/filesystemobserver/random.js b/filesystemobserver/random.js new file mode 100644 index 00000000..12f36c4c --- /dev/null +++ b/filesystemobserver/random.js @@ -0,0 +1,98 @@ +(async () => { + const rootHandle = await navigator.storage.getDirectory(); + await rootHandle.remove({ recursive: true }); + await startObserver(); + + async function startObserver() { + if ("FileSystemObserver" in self) { + await import("./observer.js"); + demoOPFS(); + } else { + document.querySelector("pre").textContent = + "😕 Your browser does not support the File System Observer API"; + } + } + + async function demoOPFS() { + const loremIpsum = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + + function logMessage(message) { + const logContainer = document.getElementById("log"); + const logEntry = document.createElement("div"); + logEntry.className = "log-entry"; + logEntry.textContent = message; + + logContainer.appendChild(logEntry); + + // Ensure we only keep the last 10 log entries + while (logContainer.children.length > 10) { + logContainer.removeChild(logContainer.firstChild); + } + + // Scroll to the bottom of the log + logContainer.scrollTop = logContainer.scrollHeight; + } + + async function getRandomFileHandle() { + const files = []; + for await (const entry of rootHandle.values()) { + if (entry.kind === "file") { + files.push(entry); + } + } + if (files.length === 0) { + return null; + } + const randomIndex = Math.floor(Math.random() * files.length); + return files[randomIndex]; + } + + async function createFile() { + const fileName = `file_${Date.now()}.txt`; + const fileHandle = await rootHandle.getFileHandle(fileName, { + create: true, + }); + const writable = await fileHandle.createWritable(); + const text = loremIpsum.repeat(Math.floor(Math.random() * 20) + 1); + await writable.write(text); + await writable.close(); + logMessage(`✅ Created file "${fileName}"`); + } + + async function deleteFile() { + const fileHandle = await getRandomFileHandle(); + if (!fileHandle) { + return; + } + await rootHandle.removeEntry(fileHandle.name); + logMessage(`🗑️ Deleted file "${fileHandle.name}"`); + } + + async function modifyFile() { + const fileHandle = await getRandomFileHandle(); + if (!fileHandle) { + return; + } + const writable = await fileHandle.createWritable(); + const text = loremIpsum.repeat(Math.floor(Math.random() * 20) + 1); + await writable.write(text); + await writable.close(); + logMessage(`📝 Modified file "${fileHandle.name}"`); + } + + async function performRandomOperation() { + const operations = [createFile, deleteFile, modifyFile]; + const randomOperation = + operations[Math.floor(Math.random() * operations.length)]; + try { + await randomOperation(); + } catch { + // No op + } + } + + // Perform random operations in an interval + setInterval(performRandomOperation, 2000); // every 2 seconds + } +})(); diff --git a/filesystemobserver/style.css b/filesystemobserver/style.css new file mode 100644 index 00000000..7c388008 --- /dev/null +++ b/filesystemobserver/style.css @@ -0,0 +1,22 @@ +:root { + color-scheme: dark light; +} + +html { + box-sizing: border-box; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +body { + margin: 1rem; + font-family: system-ui, sans-serif; +} + +pre { + color: red; +}