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 @@ + + + + + + + + + + + File System Observer Demo + + +

File System Observer Demo

+

🎲 Random file operations log

+

Log of randomly created, deleted, or modified files.

+
+

👀 File system observer log

+

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;
+}