From 001b54cc3e4b07af0faf6d8deb9fc13f74c69fc0 Mon Sep 17 00:00:00 2001 From: Reynaldi Chernando Date: Mon, 27 Oct 2025 16:31:44 +0700 Subject: [PATCH 1/3] Init refactor app.js --- build.js | 12 +- package-lock.json | 485 +++++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/assets/js/index.js | 1 + 4 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 src/assets/js/index.js diff --git a/build.js b/build.js index afc084a..39dfd44 100644 --- a/build.js +++ b/build.js @@ -7,6 +7,16 @@ const menuItems = require('./src/menu.js'); const { encode } = require('html-entities'); const { JSDOM } = require('jsdom'); const yaml = require('js-yaml'); +const esbuild = require('esbuild'); + +esbuild.build({ + entryPoints: ['src/assets/js/index.js'], + bundle: true, + outfile: 'dist/assets/js/bundle.js', + minify: true, + sourcemap: true, + allowOverwrite: true +}).catch((error) => console.error(error)); const site = "https://docs.puter.com"; @@ -447,7 +457,7 @@ function generateDocsHTML(filePath, rootDir, page, isIndex = false) { html += generateSearchUIHTML(); - html += ``; + html += ``; html += ``; const relativeDir = path.relative(rootDir, path.dirname(filePath)); const newDir = path.join(rootDir, '..', 'dist', relativeDir, path.basename(filePath, '.md')); diff --git a/package-lock.json b/package-lock.json index 2f9f691..b6fe97d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ }, "devDependencies": { "concurrently": "^8.2.2", + "esbuild": "0.25.11", "http-server": "^14.1.1", "nodemon": "^3.1.4" } @@ -154,6 +155,448 @@ "node": ">=18" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -566,6 +1009,48 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", diff --git a/package.json b/package.json index ce82d01..970a36d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "devDependencies": { "concurrently": "^8.2.2", + "esbuild": "0.25.11", "http-server": "^14.1.1", "nodemon": "^3.1.4" } diff --git a/src/assets/js/index.js b/src/assets/js/index.js new file mode 100644 index 0000000..8903310 --- /dev/null +++ b/src/assets/js/index.js @@ -0,0 +1 @@ +import "./app.js"; From 63632ce4033c6ee7e9d3a0d3905f8db8839ccb77 Mon Sep 17 00:00:00 2001 From: Reynaldi Chernando Date: Tue, 28 Oct 2025 14:33:03 +0700 Subject: [PATCH 2/3] Split search.js --- .gitignore | 3 +- build.js | 24 ++- src/assets/js/app.js | 408 ++++++---------------------------------- src/assets/js/index.js | 1 + src/assets/js/search.js | 334 ++++++++++++++++++++++++++++++++ 5 files changed, 404 insertions(+), 366 deletions(-) create mode 100644 src/assets/js/search.js diff --git a/.gitignore b/.gitignore index d3909af..91c4195 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store .env dist/ -node_modules/ \ No newline at end of file +node_modules/ +.vscode/ diff --git a/build.js b/build.js index 39dfd44..0e474f6 100644 --- a/build.js +++ b/build.js @@ -9,15 +9,6 @@ const { JSDOM } = require('jsdom'); const yaml = require('js-yaml'); const esbuild = require('esbuild'); -esbuild.build({ - entryPoints: ['src/assets/js/index.js'], - bundle: true, - outfile: 'dist/assets/js/bundle.js', - minify: true, - sourcemap: true, - allowOverwrite: true -}).catch((error) => console.error(error)); - const site = "https://docs.puter.com"; let usedPlaygroundExamples = new Set(); @@ -526,10 +517,23 @@ function findMdFiles(rootDir) { } // Updated main function to start the process -function generateDocumentation(rootDir) { +async function generateDocumentation(rootDir) { const distDir = path.join(rootDir, '..', 'dist'); removeDirectoryRecursively(distDir); // Remove the existing 'dist' directory findMdFiles(rootDir); // Process files based on sidebar + + try { + await esbuild.build({ + entryPoints: ['src/assets/js/index.js'], + bundle: true, + outfile: 'dist/assets/js/bundle.js', + minify: true, + sourcemap: true, + allowOverwrite: true + }) + } catch (error) { + console.error(error); + } } function generateRedirects() { diff --git a/src/assets/js/app.js b/src/assets/js/app.js index 2d42c68..39b3fb6 100755 --- a/src/assets/js/app.js +++ b/src/assets/js/app.js @@ -1,8 +1,3 @@ -// Global search index -let searchIndex = []; -let searchTimeout = null; -let selectedSearchResult = -1; - const icons = { ai_outline: ``, ai_active: ``, @@ -14,16 +9,15 @@ const icons = { hosting_active: ``, auth_outline: ``, auth_active: ``, - command: `` } -jQuery(document).ready(function() { +jQuery(document).ready(function () { //when doc is loaded scroll side nav to active section $('#sidebar').scrollTop($('#sidebar').scrollTop() + $('#sidebar a.active').position()?.top - - $('#sidebar').height()/2 + $('#sidebar a.active').height()/2); + - $('#sidebar').height() / 2 + $('#sidebar a.active').height() / 2); //History API if (window.history && window.history.pushState) { - $(window).on('popstate', function() { + $(window).on('popstate', function () { if (window.history.state.reload) { window.location.href = window.location.href; } @@ -31,16 +25,16 @@ jQuery(document).ready(function() { } // add icons to .icon elements - $('.example-group').each(function() { + $('.example-group').each(function () { $(this).find('.icon').html(icons[$(this).data('icon')]); }); - $('.example-group.active').each(function() { + $('.example-group.active').each(function () { $(this).find('.icon').html(icons[$(this).data('icon-active')]); }); // "Copy code" buttons - $(document).on('click', '.copy-code-button', function(e) { + $(document).on('click', '.copy-code-button', function (e) { const $codeWrapper = $(this).closest('.code-wrapper') const $codeBlock = $codeWrapper.find('code').first(); @@ -53,7 +47,7 @@ jQuery(document).ready(function() { }) // "Download code" buttons - $(document).on('click', '.download-code-button', function(e) { + $(document).on('click', '.download-code-button', function (e) { const $codeWrapper = $(this).closest('.code-wrapper') const $codeBlock = $codeWrapper.find('code').first(); const $filename = 'puter-example.html'; @@ -70,14 +64,14 @@ jQuery(document).ready(function() { }) // Dropdown toggle functionality - $(document).on('click', '.dropdown-button', function(e) { + $(document).on('click', '.dropdown-button', function (e) { e.preventDefault(); e.stopPropagation(); $('.menu-dropdown-items').toggle(); }); // Menu button click handlers - $(document).on('click', '#menu-copy-page', async function(e) { + $(document).on('click', '#menu-copy-page', async function (e) { const markdownUrl = new URL("index.md", window.location.href).toString(); try { /** @@ -86,12 +80,12 @@ jQuery(document).ready(function() { * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ const clipboardItem = new ClipboardItem({ - ["text/plain"]: fetch(markdownUrl) - .then((r) => r.text()) - .then((t) => new Blob([t], { type: "text/plain" })) - .catch((e) => { - throw new Error(`Received ${e.message} for ${markdownUrl}`); - }), + ["text/plain"]: fetch(markdownUrl) + .then((r) => r.text()) + .then((t) => new Blob([t], { type: "text/plain" })) + .catch((e) => { + throw new Error(`Received ${e.message} for ${markdownUrl}`); + }), }); await navigator.clipboard.write([clipboardItem]); @@ -108,119 +102,29 @@ jQuery(document).ready(function() { } }); - $(document).on('click', '#menu-view-markdown', function(e) { - window.open(new URL("index.md", window.location.href),"_blank"); + $(document).on('click', '#menu-view-markdown', function (e) { + window.open(new URL("index.md", window.location.href), "_blank"); }); - $(document).on('click', '#menu-open-chatgpt', function(e) { + $(document).on('click', '#menu-open-chatgpt', function (e) { const message = `Read from ${window.location.href} so I can ask questions about it.`; window.open(`https://chat.openai.com/?q=${message}`, "_blank"); }); - $(document).on('click', '#menu-open-claude', function(e) { + $(document).on('click', '#menu-open-claude', function (e) { const message = `Read from ${window.location.href} so I can ask questions about it.`; window.open(`https://claude.ai/new?q=${message}`, "_blank"); }); - const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; - const shortcut = isMac ? `${icons.command} K` : 'Ctrl K'; - - const $searchTrigger = $('.search-trigger'); - const $shortcutElement = $('
') - .addClass('search-trigger-shortcut') - .html(shortcut); - $searchTrigger.append($shortcutElement); - - // search handlers - function openSearchUI() { - $('.search-overlay').addClass('active'); - $('body').css('overflow', 'hidden'); - $('.search-input').val('').focus(); - updateSearchResults([]); - } - - function closeSearchUI() { - $('.search-overlay').removeClass('active'); - $('body').css('overflow', 'auto'); - } - - $(document).on('click', '.search-trigger', function(e) { - e.preventDefault(); - e.stopPropagation(); - openSearchUI(); - }); - - $(document).on('keydown', function(e) { - if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { - e.stopPropagation(); - e.preventDefault(); - openSearchUI(); - } - - if (e.key === 'Escape' && $('.search-overlay').hasClass('active')) { - e.stopPropagation(); - e.preventDefault(); - closeSearchUI(); - } - - // Arrow key navigation in search results - if ($('.search-overlay').hasClass('active')) { - if (e.key === 'ArrowDown') { - e.stopPropagation(); - e.preventDefault(); - navigateSearchResults('down'); - } else if (e.key === 'ArrowUp') { - e.stopPropagation(); - e.preventDefault(); - navigateSearchResults('up'); - } else if (e.key === 'Enter' && selectedSearchResult >= 0) { - e.stopPropagation(); - e.preventDefault(); - closeSearchUI(); - activateSelectedResult(); - } - } - }); - - $(document).on('click', '.search-overlay', function(e) { - if (e.target === this) { - closeSearchUI(); - } - }); - - $(document).on('click', '.search-result', function(e) { - closeSearchUI(); - }); - - $(document).on('input', '.search-input', function(e) { - const query = $(this).val().trim(); - - // Clear existing timeout - if (searchTimeout) { - clearTimeout(searchTimeout); - } - - // Set new timeout for debouncing - searchTimeout = setTimeout(async () => { - if (searchIndex.length == 0) { - await fetchSearchIndex(); - } - performSearch(query); - }, 300); - }); - // get github stars fetchGitHubData(); - - // fetch search index - fetchSearchIndex(); }); -$(document).on('click', '.example-group', function(e) { +$(document).on('click', '.example-group', function (e) { e.preventDefault(); $('.example-group').removeClass('active'); // change all icons to outline - $('.example-group').not(this).each(function() { + $('.example-group').not(this).each(function () { $(this).find('.icon').html(icons[$(this).data('icon')]); }); $(this).toggleClass('active'); @@ -238,16 +142,16 @@ $(document).on('click', '.example-group', function(e) { } }) -$(document).on('click', '.sidebar-toggle', function(e) { +$(document).on('click', '.sidebar-toggle', function (e) { e.preventDefault(); $('#sidebar-wrapper').toggleClass('active'); $('.sidebar-toggle-button').toggleClass('active'); }) // clicking anywhere on the page will close the sidebar and menu dropdown -$(document).on('click', function(e) { +$(document).on('click', function (e) { // print event target class - + if (!$(e.target).closest('#sidebar-wrapper').length && !$(e.target).closest('.sidebar-toggle-button').length && !$(e.target).hasClass('sidebar-toggle-button') && !$(e.target).hasClass('sidebar-toggle')) { $('#sidebar-wrapper').removeClass('active'); $('.sidebar-toggle-button').removeClass('active'); @@ -262,12 +166,12 @@ $(document).on('click', function(e) { } }) -$(document).on('click', '#sidebar a:not(.skip-insta-load), .next-prev-button', function(e) { - e.preventDefault(); - $('#sidebar a').removeClass('active'); - $(this).addClass('active'); +$(document).on('click', '#sidebar a:not(.skip-insta-load), .next-prev-button', function (e) { + e.preventDefault(); + $('#sidebar a').removeClass('active'); + $(this).addClass('active'); - if($(this).hasClass('next-prev-button')){ + if ($(this).hasClass('next-prev-button')) { // get the next or previous link var $nextPrevLink = $(this).attr('href'); // find the sidebar link that matches the next or previous link @@ -277,23 +181,23 @@ $(document).on('click', '#sidebar a:not(.skip-insta-load), .next-prev-button', f // add active class to the sidebar link that matches the next or previous link $sidebarLink.addClass('active'); } - + // reset progress bar $('#progress-bar').css('width', '0%'); $('#progress-bar').show(); // History API - try{ - window.history.pushState({reload: true}, document.title, $(this).attr('href')); - }catch(e){ + try { + window.history.pushState({ reload: true }, document.title, $(this).attr('href')); + } catch (e) { console.error('Error: Failed to push state.', e); } $.ajax({ url: $(this).attr('href'), - xhr: function() { + xhr: function () { var xhr = new window.XMLHttpRequest(); - xhr.onprogress = function(e) { + xhr.onprogress = function (e) { if (e.lengthComputable) { var percentComplete = e.loaded / e.total * 100; $('#progress-bar').css('width', percentComplete + '%'); @@ -301,34 +205,34 @@ $(document).on('click', '#sidebar a:not(.skip-insta-load), .next-prev-button', f }; return xhr; } - }).done(function(data) { - $('.docs-content').html($(data).find('.docs-content').html()); + }).done(function (data) { + $('.docs-content').html($(data).find('.docs-content').html()); $('#toc-wrapper').html($(data).find('#toc-wrapper').html()); // highlight code - $(`code[class^='language']`).each(function() { + $(`code[class^='language']`).each(function () { var $this = $(this); if ($this.attr('data-highlighted') === 'yes') { // Remove the attribute or set it to 'no' $this.removeAttr('data-highlighted'); } // Now you can re-highlight - else{ - try{ - hljs.configure({ignoreUnescapedHTML: true}); + else { + try { + hljs.configure({ ignoreUnescapedHTML: true }); hljs.highlightElement(this); - }catch(e){ + } catch (e) { console.error('Error: Failed to highlight.', e); } } }); - + // add icons to .icon elements - $('.example-group').each(function() { + $('.example-group').each(function () { $(this).find('.icon').html(icons[$(this).data('icon')]); }); - $('.example-group.active').each(function() { + $('.example-group.active').each(function () { $(this).find('.icon').html(icons[$(this).data('icon-active')]); }); @@ -340,18 +244,18 @@ $(document).on('click', '#sidebar a:not(.skip-insta-load), .next-prev-button', f // close sidebar $('#sidebar-wrapper').removeClass('active'); $('.sidebar-toggle-button').removeClass('active'); - - //set title of page + + //set title of page let title = $(data).filter('title').text(); - if(!title) + if (!title) title = $(data).find('title').text(); - document.title = title; + document.title = title; // update description meta tag let description = $(data).filter('meta[name="description"]').attr('content'); - if(!description) + if (!description) description = $(data).find('meta[name="description"]').attr('content'); - if(description) { + if (description) { let descriptionMeta = $('meta[name="description"]'); if (descriptionMeta.length === 0) { descriptionMeta = $('').appendTo('head'); @@ -369,7 +273,7 @@ $(document).on('click', '#sidebar a:not(.skip-insta-load), .next-prev-button', f setTimeout(() => { $('#progress-bar').fadeOut(100); }, 1000); - }).fail(function(e) { + }).fail(function (e) { // Handle the error here console.error('Error: Failed to load the content.', e); // Optionally, display an error message to the user @@ -383,212 +287,6 @@ $(document).on('click', '#sidebar a:not(.skip-insta-load), .next-prev-button', f return false; }); - -async function fetchSearchIndex() { - try { - const response = await fetch('/index.json'); - const data = await response.json(); - searchIndex = data; - console.log('Search index loaded:', searchIndex.length + ' items'); - } catch (error) { - console.error('Failed to load search index:', error); - searchIndex = []; - } -} - -function escapeHtml(text) { - return text - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -function generateTextFragment(matchedText, prefix = '', suffix = '') { - const encodedText = encodeURIComponent(matchedText); - const encodedPrefix = prefix ? encodeURIComponent(prefix) + '-,' : ''; - const encodedSuffix = suffix ? ',-' + encodeURIComponent(suffix) : ''; - - return `#:~:text=${encodedPrefix}${encodedText}${encodedSuffix}`; -} - -function performSearch(query) { - if (!query || query.length < 2) { - $('.search-results').html('
Start typing to search...
'); - return; - } - - const titleResults = []; - const textResults = []; - const queryLower = query.toLowerCase(); - - searchIndex.forEach(item => { - const titleMatch = item.title.toLowerCase().indexOf(queryLower); - if (titleMatch !== -1) { - const highlightedTitle = escapeHtml(item.title).replace( - new RegExp(`(${escapeHtml(query)})`, 'i'), - '$1' - ); - - titleResults.push({ - title: highlightedTitle, - path: item.path, - text: escapeHtml(item.text.substring(0, 60) + (item.text.length > 60 ? '...' : '')), - textFragment: '', - }); - } - - const textLower = item.text.toLowerCase(); - let searchOffset = 0; - - // Find all matches in the text - while (true) { - const textMatch = textLower.indexOf(queryLower, searchOffset); - if (textMatch === -1) break; - - // Extract 50 chars before and after the match - const contextStart = Math.max(0, textMatch - 50); - const contextEnd = Math.min(item.text.length, textMatch + query.length + 50); - const contextText = item.text.substring(contextStart, contextEnd); - - // Split into words - const words = contextText.split(/\s+/); - - // Find all words that intersect with the match range - const matchStart = textMatch; - const matchEnd = textMatch + query.length; - let matchStartWordIndex = -1; - let matchEndWordIndex = -1; - let currentPos = contextStart; - - for (let i = 0; i < words.length; i++) { - const wordStart = currentPos; - const wordEnd = wordStart + words[i].length; - - // Check if this word intersects with the match - if (wordStart < matchEnd && wordEnd > matchStart) { - if (matchStartWordIndex === -1) { - matchStartWordIndex = i; - } - matchEndWordIndex = i; - } - currentPos = wordEnd + 1; // +1 for space - } - - // Get the complete matched text (all words that contain the match) - const matchedWords = matchStartWordIndex !== -1 ? - words.slice(matchStartWordIndex, matchEndWordIndex + 1).join(' ') : - words[0] || ''; - - // Get prefix and suffix for text fragment (closest words) - const fragmentPrefix = matchStartWordIndex > 0 ? words[matchStartWordIndex - 1] : ''; - const fragmentSuffix = matchEndWordIndex < words.length - 1 ? words[matchEndWordIndex + 1] : ''; - - // Generate text fragment - const textFragment = generateTextFragment(matchedWords, fragmentPrefix, fragmentSuffix); - - // Create display text (max 4 words before/after) - const startWord = Math.max(0, matchStartWordIndex - 4); - const endWord = Math.min(words.length, matchEndWordIndex + 5); - const displayWords = words.slice(startWord, endWord); - - let displayText = displayWords.join(' '); - if (startWord > 0) displayText = '...' + displayText; - if (endWord < words.length) displayText = displayText + '...'; - - // Highlight the matched text in display - const highlightedChunk = escapeHtml(displayText).replace( - new RegExp(`(${escapeHtml(query)})`, 'i'), - '$1' - ); - - textResults.push({ - title: item.title, - path: item.path, - text: highlightedChunk, - textFragment: textFragment, - }); - - searchOffset = textMatch + 1; - } - }); - - updateSearchResults([...titleResults, ...textResults]); -} - -function updateSearchResults(results) { - if (results.length === 0) { - $('.search-results').html('
No results found
'); - selectedSearchResult = -1; - return; - } - - let html = ''; - results.slice(0, 15).forEach((result, index) => { - const url = result.path + '/' + (result.textFragment || ''); - html += ` - - `; - }); - - $('.search-results').html(html); - selectedSearchResult = -1; // Reset selection - updateSelectedResult(); -} - -function updateSelectedResult() { - $('.search-result').removeClass('selected'); - if (selectedSearchResult >= 0) { - const $selected = $(`.search-result[data-index="${selectedSearchResult}"]`); - $selected.addClass('selected'); - - // Scroll the container to keep the selected result visible - const $container = $('.search-results'); - const containerHeight = $container.height(); - const containerScrollTop = $container.scrollTop(); - const selectedOffset = $selected.offset().top; - const containerOffset = $container.offset().top; - const selectedRelativeTop = selectedOffset - containerOffset + containerScrollTop; - const selectedHeight = $selected.outerHeight(); - - if (selectedRelativeTop < containerScrollTop) { - // Selected result is above the visible area - $container.scrollTop(selectedRelativeTop); - } else if (selectedRelativeTop + selectedHeight > containerScrollTop + containerHeight) { - // Selected result is below the visible area - $container.scrollTop(selectedRelativeTop + selectedHeight - containerHeight); - } - } -} - -function navigateSearchResults(direction) { - const $results = $('.search-result'); - if ($results.length === 0) return; - - if (direction === 'down') { - selectedSearchResult = selectedSearchResult < $results.length - 1 ? selectedSearchResult + 1 : selectedSearchResult; - } else if (direction === 'up') { - selectedSearchResult = selectedSearchResult >= 0 ? selectedSearchResult - 1 : selectedSearchResult; - } - - updateSelectedResult(); -} - -function activateSelectedResult() { - if (selectedSearchResult >= 0) { - const $selected = $(`.search-result[data-index="${selectedSearchResult}"] .search-result-link`); - if ($selected.length) { - window.location.href = $selected.attr('href'); - } - } -} - function fetchGitHubData() { // GitHub API fetching and handling @@ -613,6 +311,6 @@ function fetchGitHubData() { }); } -$(document).on('change', '.dark-mode-toggle-checkbox', function() { +$(document).on('change', '.dark-mode-toggle-checkbox', function () { $('body').toggleClass('dark', $(this).is(':checked')); }); \ No newline at end of file diff --git a/src/assets/js/index.js b/src/assets/js/index.js index 8903310..f2ce61e 100644 --- a/src/assets/js/index.js +++ b/src/assets/js/index.js @@ -1 +1,2 @@ import "./app.js"; +import "./search.js"; diff --git a/src/assets/js/search.js b/src/assets/js/search.js new file mode 100644 index 0000000..475f912 --- /dev/null +++ b/src/assets/js/search.js @@ -0,0 +1,334 @@ +// Global search index +let searchIndex = []; +let searchTimeout = null; +let selectedSearchResult = -1; + +const commandIcon = ``; + +jQuery(document).ready(function () { + const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0; + const shortcut = isMac ? `${commandIcon} K` : "Ctrl K"; + + const $searchTrigger = $(".search-trigger"); + const $shortcutElement = $("
") + .addClass("search-trigger-shortcut") + .html(shortcut); + $searchTrigger.append($shortcutElement); + + // search handlers + function openSearchUI() { + $(".search-overlay").addClass("active"); + $("body").css("overflow", "hidden"); + $(".search-input").val("").focus(); + updateSearchResults([]); + } + + function closeSearchUI() { + $(".search-overlay").removeClass("active"); + $("body").css("overflow", "auto"); + } + + $(document).on("click", ".search-trigger", function (e) { + e.preventDefault(); + e.stopPropagation(); + openSearchUI(); + }); + + $(document).on("keydown", function (e) { + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + e.stopPropagation(); + e.preventDefault(); + openSearchUI(); + } + + if (e.key === "Escape" && $(".search-overlay").hasClass("active")) { + e.stopPropagation(); + e.preventDefault(); + closeSearchUI(); + } + + // Arrow key navigation in search results + if ($(".search-overlay").hasClass("active")) { + if (e.key === "ArrowDown") { + e.stopPropagation(); + e.preventDefault(); + navigateSearchResults("down"); + } else if (e.key === "ArrowUp") { + e.stopPropagation(); + e.preventDefault(); + navigateSearchResults("up"); + } else if (e.key === "Enter" && selectedSearchResult >= 0) { + e.stopPropagation(); + e.preventDefault(); + closeSearchUI(); + activateSelectedResult(); + } + } + }); + + $(document).on("click", ".search-overlay", function (e) { + if (e.target === this) { + closeSearchUI(); + } + }); + + $(document).on("click", ".search-result", function (e) { + closeSearchUI(); + }); + + $(document).on("input", ".search-input", function (e) { + const query = $(this).val().trim(); + + // Clear existing timeout + if (searchTimeout) { + clearTimeout(searchTimeout); + } + + // Set new timeout for debouncing + searchTimeout = setTimeout(async () => { + if (searchIndex.length == 0) { + await fetchSearchIndex(); + } + performSearch(query); + }, 300); + }); + + // fetch search index + fetchSearchIndex(); +}); + +async function fetchSearchIndex() { + try { + const response = await fetch("/index.json"); + const data = await response.json(); + searchIndex = data; + console.log("Search index loaded:", searchIndex.length + " items"); + } catch (error) { + console.error("Failed to load search index:", error); + searchIndex = []; + } +} +function escapeHtml(text) { + return text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +function generateTextFragment(matchedText, prefix = "", suffix = "") { + const encodedText = encodeURIComponent(matchedText); + const encodedPrefix = prefix ? encodeURIComponent(prefix) + "-," : ""; + const encodedSuffix = suffix ? ",-" + encodeURIComponent(suffix) : ""; + + return `#:~:text=${encodedPrefix}${encodedText}${encodedSuffix}`; +} + +function performSearch(query) { + if (!query || query.length < 2) { + $(".search-results").html( + '
Start typing to search...
' + ); + return; + } + + const titleResults = []; + const textResults = []; + const queryLower = query.toLowerCase(); + + searchIndex.forEach((item) => { + const titleMatch = item.title.toLowerCase().indexOf(queryLower); + if (titleMatch !== -1) { + const highlightedTitle = escapeHtml(item.title).replace( + new RegExp(`(${escapeHtml(query)})`, "i"), + "$1" + ); + + titleResults.push({ + title: highlightedTitle, + path: item.path, + text: escapeHtml( + item.text.substring(0, 60) + (item.text.length > 60 ? "..." : "") + ), + textFragment: "", + }); + } + + const textLower = item.text.toLowerCase(); + let searchOffset = 0; + + // Find all matches in the text + while (true) { + const textMatch = textLower.indexOf(queryLower, searchOffset); + if (textMatch === -1) break; + + // Extract 50 chars before and after the match + const contextStart = Math.max(0, textMatch - 50); + const contextEnd = Math.min( + item.text.length, + textMatch + query.length + 50 + ); + const contextText = item.text.substring(contextStart, contextEnd); + + // Split into words + const words = contextText.split(/\s+/); + + // Find all words that intersect with the match range + const matchStart = textMatch; + const matchEnd = textMatch + query.length; + let matchStartWordIndex = -1; + let matchEndWordIndex = -1; + let currentPos = contextStart; + + for (let i = 0; i < words.length; i++) { + const wordStart = currentPos; + const wordEnd = wordStart + words[i].length; + + // Check if this word intersects with the match + if (wordStart < matchEnd && wordEnd > matchStart) { + if (matchStartWordIndex === -1) { + matchStartWordIndex = i; + } + matchEndWordIndex = i; + } + currentPos = wordEnd + 1; // +1 for space + } + + // Get the complete matched text (all words that contain the match) + const matchedWords = + matchStartWordIndex !== -1 + ? words.slice(matchStartWordIndex, matchEndWordIndex + 1).join(" ") + : words[0] || ""; + + // Get prefix and suffix for text fragment (closest words) + const fragmentPrefix = + matchStartWordIndex > 0 ? words[matchStartWordIndex - 1] : ""; + const fragmentSuffix = + matchEndWordIndex < words.length - 1 + ? words[matchEndWordIndex + 1] + : ""; + + // Generate text fragment + const textFragment = generateTextFragment( + matchedWords, + fragmentPrefix, + fragmentSuffix + ); + + // Create display text (max 4 words before/after) + const startWord = Math.max(0, matchStartWordIndex - 4); + const endWord = Math.min(words.length, matchEndWordIndex + 5); + const displayWords = words.slice(startWord, endWord); + + let displayText = displayWords.join(" "); + if (startWord > 0) displayText = "..." + displayText; + if (endWord < words.length) displayText = displayText + "..."; + + // Highlight the matched text in display + const highlightedChunk = escapeHtml(displayText).replace( + new RegExp(`(${escapeHtml(query)})`, "i"), + "$1" + ); + + textResults.push({ + title: item.title, + path: item.path, + text: highlightedChunk, + textFragment: textFragment, + }); + + searchOffset = textMatch + 1; + } + }); + + updateSearchResults([...titleResults, ...textResults]); +} + +function updateSearchResults(results) { + if (results.length === 0) { + $(".search-results").html( + '
No results found
' + ); + selectedSearchResult = -1; + return; + } + + let html = ""; + results.slice(0, 15).forEach((result, index) => { + const url = result.path + "/" + (result.textFragment || ""); + html += ` + + `; + }); + + $(".search-results").html(html); + selectedSearchResult = -1; // Reset selection + updateSelectedResult(); +} + +function updateSelectedResult() { + $(".search-result").removeClass("selected"); + if (selectedSearchResult >= 0) { + const $selected = $(`.search-result[data-index="${selectedSearchResult}"]`); + $selected.addClass("selected"); + + // Scroll the container to keep the selected result visible + const $container = $(".search-results"); + const containerHeight = $container.height(); + const containerScrollTop = $container.scrollTop(); + const selectedOffset = $selected.offset().top; + const containerOffset = $container.offset().top; + const selectedRelativeTop = + selectedOffset - containerOffset + containerScrollTop; + const selectedHeight = $selected.outerHeight(); + + if (selectedRelativeTop < containerScrollTop) { + // Selected result is above the visible area + $container.scrollTop(selectedRelativeTop); + } else if ( + selectedRelativeTop + selectedHeight > + containerScrollTop + containerHeight + ) { + // Selected result is below the visible area + $container.scrollTop( + selectedRelativeTop + selectedHeight - containerHeight + ); + } + } +} + +function navigateSearchResults(direction) { + const $results = $(".search-result"); + if ($results.length === 0) return; + + if (direction === "down") { + selectedSearchResult = + selectedSearchResult < $results.length - 1 + ? selectedSearchResult + 1 + : selectedSearchResult; + } else if (direction === "up") { + selectedSearchResult = + selectedSearchResult >= 0 + ? selectedSearchResult - 1 + : selectedSearchResult; + } + + updateSelectedResult(); +} + +function activateSelectedResult() { + if (selectedSearchResult >= 0) { + const $selected = $( + `.search-result[data-index="${selectedSearchResult}"] .search-result-link` + ); + if ($selected.length) { + window.location.href = $selected.attr("href"); + } + } +} From 6a233a2e4dae728417418bcc4b869b9cd42c0bb0 Mon Sep 17 00:00:00 2001 From: Reynaldi Chernando Date: Tue, 28 Oct 2025 15:00:56 +0700 Subject: [PATCH 3/3] Split context-menu and example event handler --- build.js | 3 +- src/assets/js/app.js | 137 +--------------------------------- src/assets/js/context-menu.js | 94 +++++++++++++++++++++++ src/assets/js/example.js | 45 +++++++++++ src/assets/js/index.js | 2 + 5 files changed, 143 insertions(+), 138 deletions(-) create mode 100644 src/assets/js/context-menu.js create mode 100644 src/assets/js/example.js diff --git a/build.js b/build.js index 0e474f6..68f6246 100644 --- a/build.js +++ b/build.js @@ -520,8 +520,6 @@ function findMdFiles(rootDir) { async function generateDocumentation(rootDir) { const distDir = path.join(rootDir, '..', 'dist'); removeDirectoryRecursively(distDir); // Remove the existing 'dist' directory - findMdFiles(rootDir); // Process files based on sidebar - try { await esbuild.build({ entryPoints: ['src/assets/js/index.js'], @@ -534,6 +532,7 @@ async function generateDocumentation(rootDir) { } catch (error) { console.error(error); } + findMdFiles(rootDir); // Process files based on sidebar } function generateRedirects() { diff --git a/src/assets/js/app.js b/src/assets/js/app.js index 39b3fb6..2b7f789 100755 --- a/src/assets/js/app.js +++ b/src/assets/js/app.js @@ -1,16 +1,3 @@ -const icons = { - ai_outline: ``, - ai_active: ``, - fs_outline: ``, - fs_active: ``, - kv_outline: ``, - kv_active: ``, - hosting_outline: ``, - hosting_active: ``, - auth_outline: ``, - auth_active: ``, -} - jQuery(document).ready(function () { //when doc is loaded scroll side nav to active section $('#sidebar').scrollTop($('#sidebar').scrollTop() + $('#sidebar a.active').position()?.top @@ -24,131 +11,17 @@ jQuery(document).ready(function () { }); } - // add icons to .icon elements - $('.example-group').each(function () { - $(this).find('.icon').html(icons[$(this).data('icon')]); - }); - - $('.example-group.active').each(function () { - $(this).find('.icon').html(icons[$(this).data('icon-active')]); - }); - - // "Copy code" buttons - $(document).on('click', '.copy-code-button', function (e) { - const $codeWrapper = $(this).closest('.code-wrapper') - const $codeBlock = $codeWrapper.find('code').first(); - - navigator.clipboard.writeText($codeBlock.text()); - // show check mark for 1 second after copying - $(this).find('.copy').css('background-image', 'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 24 24\' fill=\'none\' stroke=\'%23012238\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-linejoin=\'round\'%3E%3Cpolyline points=\'20 6 9 17 4 12\'/%3E%3C/svg%3E")'); - setTimeout(() => { - $(this).find('.copy').css('background-image', ''); - }, 1000); - }) - - // "Download code" buttons - $(document).on('click', '.download-code-button', function (e) { - const $codeWrapper = $(this).closest('.code-wrapper') - const $codeBlock = $codeWrapper.find('code').first(); - const $filename = 'puter-example.html'; - const $code = $codeBlock.text(); - - const blob = new Blob([$code], { type: 'text/plain' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = $filename; - document.body.appendChild(a); - a.click(); - window.URL.revokeObjectURL(url); - }) - - // Dropdown toggle functionality - $(document).on('click', '.dropdown-button', function (e) { - e.preventDefault(); - e.stopPropagation(); - $('.menu-dropdown-items').toggle(); - }); - - // Menu button click handlers - $(document).on('click', '#menu-copy-page', async function (e) { - const markdownUrl = new URL("index.md", window.location.href).toString(); - try { - /** - * The MIT License (MIT) Copyright (c) 2021 Cloudflare, Inc. - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - */ - const clipboardItem = new ClipboardItem({ - ["text/plain"]: fetch(markdownUrl) - .then((r) => r.text()) - .then((t) => new Blob([t], { type: "text/plain" })) - .catch((e) => { - throw new Error(`Received ${e.message} for ${markdownUrl}`); - }), - }); - - await navigator.clipboard.write([clipboardItem]); - - const buttonElement = document.querySelector("#menu-copy-page span"); - const originalContent = buttonElement.innerHTML; - buttonElement.textContent = "Copied!"; - - setTimeout(() => { - buttonElement.innerHTML = originalContent; - }, 2000); - } catch (error) { - console.error("Failed to copy Markdown:", error); - } - }); - - $(document).on('click', '#menu-view-markdown', function (e) { - window.open(new URL("index.md", window.location.href), "_blank"); - }); - - $(document).on('click', '#menu-open-chatgpt', function (e) { - const message = `Read from ${window.location.href} so I can ask questions about it.`; - window.open(`https://chat.openai.com/?q=${message}`, "_blank"); - }); - - $(document).on('click', '#menu-open-claude', function (e) { - const message = `Read from ${window.location.href} so I can ask questions about it.`; - window.open(`https://claude.ai/new?q=${message}`, "_blank"); - }); - // get github stars fetchGitHubData(); }); -$(document).on('click', '.example-group', function (e) { - e.preventDefault(); - $('.example-group').removeClass('active'); - // change all icons to outline - $('.example-group').not(this).each(function () { - $(this).find('.icon').html(icons[$(this).data('icon')]); - }); - $(this).toggleClass('active'); - // change icon - if ($(this).hasClass('active')) { - $(this).find('.icon').html(icons[$(this).data('icon-active')]); - } else { - $(this).find('.icon').html(icons[$(this).data('icon')]); - } - // show content - $('.example-content').hide(); - let section = $(this).data('section'); - if ($(this).hasClass('active')) { - $(`.example-content[data-section="${section}"]`).show(); - } -}) - $(document).on('click', '.sidebar-toggle', function (e) { e.preventDefault(); $('#sidebar-wrapper').toggleClass('active'); $('.sidebar-toggle-button').toggleClass('active'); }) -// clicking anywhere on the page will close the sidebar and menu dropdown +// clicking anywhere on the page will close the sidebar $(document).on('click', function (e) { // print event target class @@ -156,14 +29,6 @@ $(document).on('click', function (e) { $('#sidebar-wrapper').removeClass('active'); $('.sidebar-toggle-button').removeClass('active'); } - - // Close menu dropdown if clicking outside - if (!$(e.target).closest('.menu-item-main').length) { - $('.menu-dropdown-items').hide(); - } - if (!$(e.target).closest('.menu-item').length) { - $('.menu-dropdown-items').hide(); - } }) $(document).on('click', '#sidebar a:not(.skip-insta-load), .next-prev-button', function (e) { diff --git a/src/assets/js/context-menu.js b/src/assets/js/context-menu.js new file mode 100644 index 0000000..23b692f --- /dev/null +++ b/src/assets/js/context-menu.js @@ -0,0 +1,94 @@ +jQuery(document).ready(function () { + // "Copy code" buttons + $(document).on('click', '.copy-code-button', function (e) { + const $codeWrapper = $(this).closest('.code-wrapper') + const $codeBlock = $codeWrapper.find('code').first(); + + navigator.clipboard.writeText($codeBlock.text()); + // show check mark for 1 second after copying + $(this).find('.copy').css('background-image', 'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 24 24\' fill=\'none\' stroke=\'%23012238\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-linejoin=\'round\'%3E%3Cpolyline points=\'20 6 9 17 4 12\'/%3E%3C/svg%3E")'); + setTimeout(() => { + $(this).find('.copy').css('background-image', ''); + }, 1000); + }) + + // "Download code" buttons + $(document).on('click', '.download-code-button', function (e) { + const $codeWrapper = $(this).closest('.code-wrapper') + const $codeBlock = $codeWrapper.find('code').first(); + const $filename = 'puter-example.html'; + const $code = $codeBlock.text(); + + const blob = new Blob([$code], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = $filename; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + }) + + // Dropdown toggle functionality + $(document).on('click', '.dropdown-button', function (e) { + e.preventDefault(); + e.stopPropagation(); + $('.menu-dropdown-items').toggle(); + }); + + // Menu button click handlers + $(document).on('click', '#menu-copy-page', async function (e) { + const markdownUrl = new URL("index.md", window.location.href).toString(); + try { + /** + * The MIT License (MIT) Copyright (c) 2021 Cloudflare, Inc. + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + */ + const clipboardItem = new ClipboardItem({ + ["text/plain"]: fetch(markdownUrl) + .then((r) => r.text()) + .then((t) => new Blob([t], { type: "text/plain" })) + .catch((e) => { + throw new Error(`Received ${e.message} for ${markdownUrl}`); + }), + }); + + await navigator.clipboard.write([clipboardItem]); + + const buttonElement = document.querySelector("#menu-copy-page span"); + const originalContent = buttonElement.innerHTML; + buttonElement.textContent = "Copied!"; + + setTimeout(() => { + buttonElement.innerHTML = originalContent; + }, 2000); + } catch (error) { + console.error("Failed to copy Markdown:", error); + } + }); + + $(document).on('click', '#menu-view-markdown', function (e) { + window.open(new URL("index.md", window.location.href), "_blank"); + }); + + $(document).on('click', '#menu-open-chatgpt', function (e) { + const message = `Read from ${window.location.href} so I can ask questions about it.`; + window.open(`https://chat.openai.com/?q=${message}`, "_blank"); + }); + + $(document).on('click', '#menu-open-claude', function (e) { + const message = `Read from ${window.location.href} so I can ask questions about it.`; + window.open(`https://claude.ai/new?q=${message}`, "_blank"); + }); +}) + +// Close menu dropdown if clicking outside +$(document).on('click', function (e) { + if (!$(e.target).closest('.menu-item-main').length) { + $('.menu-dropdown-items').hide(); + } + if (!$(e.target).closest('.menu-item').length) { + $('.menu-dropdown-items').hide(); + } +}) \ No newline at end of file diff --git a/src/assets/js/example.js b/src/assets/js/example.js new file mode 100644 index 0000000..1f3bc31 --- /dev/null +++ b/src/assets/js/example.js @@ -0,0 +1,45 @@ +const icons = { + ai_outline: ``, + ai_active: ``, + fs_outline: ``, + fs_active: ``, + kv_outline: ``, + kv_active: ``, + hosting_outline: ``, + hosting_active: ``, + auth_outline: ``, + auth_active: ``, +} + +jQuery(document).ready(function () { + // add icons to .icon elements + $('.example-group').each(function () { + $(this).find('.icon').html(icons[$(this).data('icon')]); + }); + + $('.example-group.active').each(function () { + $(this).find('.icon').html(icons[$(this).data('icon-active')]); + }); +}); + +$(document).on('click', '.example-group', function (e) { + e.preventDefault(); + $('.example-group').removeClass('active'); + // change all icons to outline + $('.example-group').not(this).each(function () { + $(this).find('.icon').html(icons[$(this).data('icon')]); + }); + $(this).toggleClass('active'); + // change icon + if ($(this).hasClass('active')) { + $(this).find('.icon').html(icons[$(this).data('icon-active')]); + } else { + $(this).find('.icon').html(icons[$(this).data('icon')]); + } + // show content + $('.example-content').hide(); + let section = $(this).data('section'); + if ($(this).hasClass('active')) { + $(`.example-content[data-section="${section}"]`).show(); + } +}) diff --git a/src/assets/js/index.js b/src/assets/js/index.js index f2ce61e..3fb2768 100644 --- a/src/assets/js/index.js +++ b/src/assets/js/index.js @@ -1,2 +1,4 @@ import "./app.js"; import "./search.js"; +import "./context-menu.js" +import "./example.js"; \ No newline at end of file