create-quicklook 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.markdown +19 -0
- package/dist/index.mjs +82 -0
- package/files/biome.template.jsonc +28 -0
- package/files/ci.yml +27 -0
- package/files/gitignore +14 -0
- package/files/hk.pkl +27 -0
- package/files/mise.toml +21 -0
- package/files/release.yml +52 -0
- package/package.json +45 -3
- package/templates/analog/index.html +18 -0
- package/templates/analog/package.json +29 -0
- package/templates/analog/quicklook.config.ts +15 -0
- package/templates/analog/src/app/app.ts +45 -0
- package/templates/analog/src/app/file-viewer.ts +24 -0
- package/templates/analog/src/main.ts +4 -0
- package/templates/analog/tsconfig.json +16 -0
- package/templates/analog/vite.config.ts +15 -0
- package/templates/astro/astro.config.ts +6 -0
- package/templates/astro/package.json +21 -0
- package/templates/astro/quicklook.config.ts +15 -0
- package/templates/astro/src/components/FileViewer.astro +39 -0
- package/templates/astro/src/env.d.ts +1 -0
- package/templates/astro/src/pages/index.astro +73 -0
- package/templates/astro/tsconfig.json +5 -0
- package/templates/nuxt/app/app.vue +46 -0
- package/templates/nuxt/app/components/FileViewer.vue +24 -0
- package/templates/nuxt/nuxt.config.ts +16 -0
- package/templates/nuxt/package.json +23 -0
- package/templates/nuxt/quicklook.config.ts +15 -0
- package/templates/nuxt/tsconfig.json +9 -0
- package/templates/solid-start/package.json +24 -0
- package/templates/solid-start/quicklook.config.ts +15 -0
- package/templates/solid-start/src/app.css +12 -0
- package/templates/solid-start/src/app.tsx +12 -0
- package/templates/solid-start/src/components/FileViewer.css +8 -0
- package/templates/solid-start/src/components/FileViewer.tsx +8 -0
- package/templates/solid-start/src/entry-client.tsx +7 -0
- package/templates/solid-start/src/entry-server.tsx +22 -0
- package/templates/solid-start/src/routes/index.tsx +35 -0
- package/templates/solid-start/tsconfig.json +16 -0
- package/templates/solid-start/vite.config.ts +13 -0
- package/templates/sveltekit/package.json +25 -0
- package/templates/sveltekit/quicklook.config.ts +15 -0
- package/templates/sveltekit/src/app.d.ts +1 -0
- package/templates/sveltekit/src/app.html +12 -0
- package/templates/sveltekit/src/lib/FileViewer.svelte +28 -0
- package/templates/sveltekit/src/routes/+page.svelte +42 -0
- package/templates/sveltekit/svelte.config.js +11 -0
- package/templates/sveltekit/tsconfig.json +11 -0
- package/templates/sveltekit/vite.config.ts +7 -0
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Permission is hereby granted, free of charge, to any
|
|
2
|
+
person obtaining a copy of this software and associated
|
|
3
|
+
documentation files (the "Software"), to deal in the
|
|
4
|
+
Software without restriction, including without
|
|
5
|
+
limitation the rights to use, copy, modify, merge,
|
|
6
|
+
publish, distribute, sublicense, and/or sell copies of
|
|
7
|
+
the Software, and to permit persons to whom the Software
|
|
8
|
+
is furnished to do so, subject to the following
|
|
9
|
+
conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice
|
|
12
|
+
shall be included in all copies or substantial portions
|
|
13
|
+
of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
16
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
17
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
18
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
19
|
+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
22
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
23
|
+
DEALINGS IN THE SOFTWARE.
|
package/README.markdown
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# create-quicklook
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[](https://www.npmjs.org/package/quicklook-kit)
|
|
5
|
+
[](https://github.com/idleberg/quicklook-kit/actions)
|
|
6
|
+
|
|
7
|
+
> Create a macOS QuickLook preview with any Vite-based web framework
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm create quicklook my-preview
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Asks for a template (SvelteKit, Astro, SolidStart, Nuxt or Analog) and whether to add a GitHub release workflow. Skip the questions with `--template <name>` and `--release` or `--no-release`.
|
|
14
|
+
|
|
15
|
+
The project uses [quicklook-kit](https://www.npmjs.com/package/quicklook-kit); see its readme for what to do next.
|
|
16
|
+
|
|
17
|
+
## License
|
|
18
|
+
|
|
19
|
+
This work is licensed under [The MIT License](https://opensource.org/licenses/MIT).
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { cp, readFile, readdir, writeFile } from "node:fs/promises";
|
|
5
|
+
import { basename, relative, resolve } from "node:path";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { parseArgs } from "node:util";
|
|
9
|
+
import { cancel, confirm, intro, isCancel, note, outro, select, text } from "@clack/prompts";
|
|
10
|
+
//#region src/index.ts
|
|
11
|
+
const pkgRoot = fileURLToPath(new URL("..", import.meta.url));
|
|
12
|
+
const templates = (await readdir(`${pkgRoot}/templates`)).toSorted();
|
|
13
|
+
const { values, positionals } = parseArgs({
|
|
14
|
+
allowPositionals: true,
|
|
15
|
+
allowNegative: true,
|
|
16
|
+
options: {
|
|
17
|
+
template: {
|
|
18
|
+
type: "string",
|
|
19
|
+
short: "t"
|
|
20
|
+
},
|
|
21
|
+
release: { type: "boolean" }
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
/** Exits quietly when a prompt is cancelled with Ctrl+C or Esc. */
|
|
25
|
+
function answer(value) {
|
|
26
|
+
if (isCancel(value)) {
|
|
27
|
+
cancel("Cancelled, nothing created.");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
intro("create-quicklook");
|
|
33
|
+
const dir = resolve(positionals[0] ?? answer(await text({
|
|
34
|
+
message: "Project folder",
|
|
35
|
+
placeholder: "quicklook-preview",
|
|
36
|
+
defaultValue: "quicklook-preview"
|
|
37
|
+
})));
|
|
38
|
+
if (existsSync(dir) && (await readdir(dir)).length > 0) {
|
|
39
|
+
cancel(`${dir} is not empty.`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const template = values.template && templates.includes(values.template) ? values.template : answer(await select({
|
|
43
|
+
message: "Template",
|
|
44
|
+
options: templates.map((value) => ({ value }))
|
|
45
|
+
}));
|
|
46
|
+
const release = values.release ?? answer(await confirm({
|
|
47
|
+
message: "Add a GitHub release workflow?",
|
|
48
|
+
initialValue: false
|
|
49
|
+
}));
|
|
50
|
+
await cp(`${pkgRoot}/templates/${template}`, dir, { recursive: true });
|
|
51
|
+
for (const file of ["mise.toml", "hk.pkl"]) await cp(`${pkgRoot}/files/${file}`, `${dir}/${file}`);
|
|
52
|
+
await cp(`${pkgRoot}/files/biome.template.jsonc`, `${dir}/biome.jsonc`);
|
|
53
|
+
await cp(`${pkgRoot}/files/ci.yml`, `${dir}/.github/workflows/ci.yml`);
|
|
54
|
+
await cp(`${pkgRoot}/files/gitignore`, `${dir}/.gitignore`);
|
|
55
|
+
if (release) await cp(`${pkgRoot}/files/release.yml`, `${dir}/.github/workflows/release.yml`);
|
|
56
|
+
const pkg = JSON.parse(await readFile(`${dir}/package.json`, "utf8"));
|
|
57
|
+
pkg.name = basename(dir);
|
|
58
|
+
const pnpm = process.env.npm_config_user_agent?.match(/^pnpm\/(\S+)/)?.[1];
|
|
59
|
+
if (pnpm) pkg.packageManager = `pnpm@${pnpm}`;
|
|
60
|
+
await writeFile(`${dir}/package.json`, `${JSON.stringify(pkg, null, " ")}\n`);
|
|
61
|
+
function git(...args) {
|
|
62
|
+
execFileSync("git", args, {
|
|
63
|
+
cwd: dir,
|
|
64
|
+
stdio: "ignore"
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
git("rev-parse", "--is-inside-work-tree");
|
|
69
|
+
} catch {
|
|
70
|
+
try {
|
|
71
|
+
git("init", "--quiet");
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
note([
|
|
75
|
+
`cd ${relative(process.cwd(), dir)}`,
|
|
76
|
+
"mise install # or: pnpm install, with Xcode and XcodeGen installed",
|
|
77
|
+
"pnpm dev # drop a file on the page to preview it",
|
|
78
|
+
"pnpm register # build the app and register it with QuickLook"
|
|
79
|
+
].join("\n"), "Next steps");
|
|
80
|
+
outro(`Created from the ${template} template.`);
|
|
81
|
+
//#endregion
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": true
|
|
7
|
+
},
|
|
8
|
+
"formatter": {
|
|
9
|
+
"indentStyle": "tab",
|
|
10
|
+
"lineWidth": 100
|
|
11
|
+
},
|
|
12
|
+
"javascript": {
|
|
13
|
+
"formatter": {
|
|
14
|
+
"quoteStyle": "single",
|
|
15
|
+
"trailingCommas": "all"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"html": {
|
|
19
|
+
"experimentalFullSupportEnabled": true
|
|
20
|
+
},
|
|
21
|
+
"assist": {
|
|
22
|
+
"actions": {
|
|
23
|
+
"source": {
|
|
24
|
+
"organizeImports": "on"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/files/ci.yml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
check:
|
|
11
|
+
# Same runner as the release workflow, XcodeGen from mise.toml needs macOS.
|
|
12
|
+
runs-on: macos-15
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
# pnpm's default store on macOS.
|
|
17
|
+
- uses: actions/cache@v6
|
|
18
|
+
with:
|
|
19
|
+
path: ~/Library/pnpm/store
|
|
20
|
+
key: pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
21
|
+
restore-keys: pnpm-
|
|
22
|
+
|
|
23
|
+
- uses: jdx/mise-action@v2
|
|
24
|
+
|
|
25
|
+
- run: pnpm run lint
|
|
26
|
+
|
|
27
|
+
- run: pnpm run check
|
package/files/gitignore
ADDED
package/files/hk.pkl
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
amends "package://github.com/jdx/hk/releases/download/v2.5.0/hk@2.5.0#/Config.pkl"
|
|
2
|
+
import "package://github.com/jdx/hk/releases/download/v2.5.0/hk@2.5.0#/Builtins.pkl"
|
|
3
|
+
|
|
4
|
+
local linters = new Mapping<String, Step> {
|
|
5
|
+
// pnpm exec runs the project's own Biome, whatever else is on PATH.
|
|
6
|
+
["biome"] = (Builtins.biome) {
|
|
7
|
+
// The builtin only matches JS, TS and JSON.
|
|
8
|
+
glob = List("**/*.{js,jsx,ts,tsx,json,jsonc,css,html,svelte,astro,vue}")
|
|
9
|
+
check = "pnpm exec biome check --no-errors-on-unmatched --files-ignore-unknown=true {{ files }}"
|
|
10
|
+
fix = "pnpm exec biome check --write --no-errors-on-unmatched --files-ignore-unknown=true {{ files }}"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
hooks {
|
|
15
|
+
["pre-commit"] {
|
|
16
|
+
fix = true
|
|
17
|
+
stash = "git"
|
|
18
|
+
steps = linters
|
|
19
|
+
}
|
|
20
|
+
["fix"] {
|
|
21
|
+
fix = true
|
|
22
|
+
steps = linters
|
|
23
|
+
}
|
|
24
|
+
["check"] {
|
|
25
|
+
steps = linters
|
|
26
|
+
}
|
|
27
|
+
}
|
package/files/mise.toml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[tools]
|
|
2
|
+
node = "24"
|
|
3
|
+
xcodegen = "latest"
|
|
4
|
+
hk = "latest"
|
|
5
|
+
pkl = "latest"
|
|
6
|
+
|
|
7
|
+
[settings]
|
|
8
|
+
# Node ships pnpm through corepack, pinned by packageManager in package.json
|
|
9
|
+
node.corepack = true
|
|
10
|
+
|
|
11
|
+
[env]
|
|
12
|
+
HK_MISE = 1
|
|
13
|
+
COREPACK_ENABLE_DOWNLOAD_PROMPT = 0
|
|
14
|
+
_.path = ["{{config_root}}/node_modules/.bin"]
|
|
15
|
+
|
|
16
|
+
# The pre-commit hook needs a git repository; run `hk install --mise` after `git init`.
|
|
17
|
+
[hooks]
|
|
18
|
+
postinstall = """
|
|
19
|
+
pnpm install
|
|
20
|
+
if git rev-parse --git-dir >/dev/null 2>&1; then hk install --mise; fi
|
|
21
|
+
"""
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
env:
|
|
12
|
+
# The folder holding quicklook.config.*, relative to the repository root.
|
|
13
|
+
PROJECT: .
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
runs-on: macos-15
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- uses: actions/cache@v6
|
|
22
|
+
with:
|
|
23
|
+
path: ~/Library/pnpm/store
|
|
24
|
+
key: pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
25
|
+
restore-keys: pnpm-
|
|
26
|
+
|
|
27
|
+
- uses: jdx/mise-action@v2
|
|
28
|
+
|
|
29
|
+
# Release builds are universal (arm64 + x86_64) by default.
|
|
30
|
+
- run: pnpm install --frozen-lockfile && pnpm run build
|
|
31
|
+
working-directory: ${{ env.PROJECT }}
|
|
32
|
+
env:
|
|
33
|
+
VERSION: ${{ github.ref_name }}
|
|
34
|
+
|
|
35
|
+
- name: Package app
|
|
36
|
+
id: package
|
|
37
|
+
run: |
|
|
38
|
+
cd "$PROJECT/.quicklook/build"
|
|
39
|
+
app=$(ls -d *.app)
|
|
40
|
+
zip="${app%.app}"
|
|
41
|
+
zip="${zip// /-}-${GITHUB_REF_NAME}.zip"
|
|
42
|
+
zip -r -9 -y "$GITHUB_WORKSPACE/$zip" "$app"
|
|
43
|
+
cd "$GITHUB_WORKSPACE"
|
|
44
|
+
shasum -a 256 "$zip" > SHA256SUMS
|
|
45
|
+
echo "zip=$zip" >> "$GITHUB_OUTPUT"
|
|
46
|
+
|
|
47
|
+
- uses: softprops/action-gh-release@v2
|
|
48
|
+
with:
|
|
49
|
+
files: |
|
|
50
|
+
${{ steps.package.outputs.zip }}
|
|
51
|
+
SHA256SUMS
|
|
52
|
+
generate_release_notes: true
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-quicklook",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Create a macOS QuickLook preview with any Vite-based web framework",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"quicklook",
|
|
8
|
+
"quicklook-plugin",
|
|
9
|
+
"create",
|
|
10
|
+
"template"
|
|
11
|
+
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"bin": {
|
|
14
|
+
"create-quicklook": "./dist/index.mjs"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"files",
|
|
19
|
+
"templates",
|
|
20
|
+
"!templates/*/node_modules",
|
|
21
|
+
"!templates/*/.quicklook",
|
|
22
|
+
"!templates/*/.DS_Store",
|
|
23
|
+
"!templates/*/.svelte-kit",
|
|
24
|
+
"!templates/*/build",
|
|
25
|
+
"!templates/*/.astro",
|
|
26
|
+
"!templates/*/dist",
|
|
27
|
+
"!templates/*/.output",
|
|
28
|
+
"!templates/*/.nitro",
|
|
29
|
+
"!templates/*/.vinxi",
|
|
30
|
+
"!templates/*/.nuxt",
|
|
31
|
+
"!templates/*/.angular"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=22.17"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "24.19.2",
|
|
38
|
+
"tsdown": "0.23.0",
|
|
39
|
+
"typescript": "6.0.3"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@clack/prompts": "1.8.1"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsdown",
|
|
46
|
+
"check": "tsc --noEmit"
|
|
47
|
+
}
|
|
6
48
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="color-scheme" content="light dark" />
|
|
7
|
+
<title>Preview</title>
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
}
|
|
12
|
+
</style>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<app-root></app-root>
|
|
16
|
+
<script type="module" src="/src/main.ts"></script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "quicklook-analog",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "quicklook dev",
|
|
7
|
+
"build": "quicklook build",
|
|
8
|
+
"register": "quicklook build && quicklook register",
|
|
9
|
+
"unregister": "quicklook unregister",
|
|
10
|
+
"check": "ngc -p tsconfig.json --noEmit",
|
|
11
|
+
"lint": "biome check",
|
|
12
|
+
"format": "biome check --write"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@analogjs/vite-plugin-angular": "2.7.5",
|
|
16
|
+
"@angular/build": "22.2.0",
|
|
17
|
+
"@angular/common": "22.2.0",
|
|
18
|
+
"@angular/compiler": "22.2.0",
|
|
19
|
+
"@angular/compiler-cli": "22.2.0",
|
|
20
|
+
"@angular/core": "22.2.0",
|
|
21
|
+
"@angular/platform-browser": "22.2.0",
|
|
22
|
+
"@biomejs/biome": "2.5.14",
|
|
23
|
+
"quicklook-kit": "^0.3.0",
|
|
24
|
+
"rxjs": "7.8.2",
|
|
25
|
+
"tslib": "2.8.1",
|
|
26
|
+
"typescript": "6.0.3",
|
|
27
|
+
"vite": "8.3.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'quicklook-kit';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
name: 'QuickLook Example',
|
|
5
|
+
bundleId: 'com.example.QuickLookExample',
|
|
6
|
+
types: [
|
|
7
|
+
{
|
|
8
|
+
id: 'com.example.QuickLookExample.example',
|
|
9
|
+
description: 'Example File',
|
|
10
|
+
extensions: ['example'],
|
|
11
|
+
conformsTo: ['public.plain-text'],
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
framework: 'analog',
|
|
15
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { browserFile, nativeFile, type QuickLookFile } from 'virtual:quicklook';
|
|
2
|
+
import { Component, signal } from '@angular/core';
|
|
3
|
+
import { FileViewer } from './file-viewer';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'app-root',
|
|
7
|
+
imports: [FileViewer],
|
|
8
|
+
host: { '(window:dragover)': '$event.preventDefault()', '(window:drop)': 'drop($event)' },
|
|
9
|
+
template: `
|
|
10
|
+
@if (file(); as file) {
|
|
11
|
+
<app-file-viewer [file]="file" />
|
|
12
|
+
} @else {
|
|
13
|
+
<!-- Only reachable in the browser: QuickLook always injects a file. -->
|
|
14
|
+
<label class="drop">
|
|
15
|
+
Drop a file here, or click to pick one
|
|
16
|
+
<input type="file" hidden (change)="open($any($event.target).files)" />
|
|
17
|
+
</label>
|
|
18
|
+
}
|
|
19
|
+
`,
|
|
20
|
+
styles: `
|
|
21
|
+
.drop {
|
|
22
|
+
display: grid;
|
|
23
|
+
place-items: center;
|
|
24
|
+
height: 100vh;
|
|
25
|
+
font: 14px system-ui;
|
|
26
|
+
color: GrayText;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
}
|
|
29
|
+
`,
|
|
30
|
+
})
|
|
31
|
+
export class App {
|
|
32
|
+
protected readonly file = signal<QuickLookFile | undefined>(nativeFile());
|
|
33
|
+
|
|
34
|
+
protected async open(files: FileList | null | undefined) {
|
|
35
|
+
const picked = files?.[0];
|
|
36
|
+
if (picked) {
|
|
37
|
+
this.file.set(await browserFile(picked));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected drop(event: DragEvent) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
this.open(event.dataTransfer?.files);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Your preview goes here. `file.bytes` holds the raw file; decode it however
|
|
2
|
+
// the format needs. This example shows it as UTF-8 text.
|
|
3
|
+
|
|
4
|
+
import type { QuickLookFile } from 'virtual:quicklook';
|
|
5
|
+
import { Component, computed, input } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'app-file-viewer',
|
|
9
|
+
template: '<pre>{{ text() }}</pre>',
|
|
10
|
+
styles: `
|
|
11
|
+
pre {
|
|
12
|
+
margin: 0;
|
|
13
|
+
padding: 1rem;
|
|
14
|
+
font:
|
|
15
|
+
12px ui-monospace,
|
|
16
|
+
monospace;
|
|
17
|
+
white-space: pre-wrap;
|
|
18
|
+
}
|
|
19
|
+
`,
|
|
20
|
+
})
|
|
21
|
+
export class FileViewer {
|
|
22
|
+
readonly file = input.required<QuickLookFile>();
|
|
23
|
+
protected readonly text = computed(() => new TextDecoder().decode(this.file().bytes));
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2023",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["es2023", "dom"],
|
|
7
|
+
"types": ["vite/client", "quicklook-kit/virtual"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"useDefineForClassFields": false
|
|
13
|
+
},
|
|
14
|
+
"angularCompilerOptions": { "strictTemplates": true },
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import angular from '@analogjs/vite-plugin-angular';
|
|
2
|
+
import { quicklook } from 'quicklook-kit/vite';
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [
|
|
7
|
+
angular({
|
|
8
|
+
tsconfig: 'tsconfig.json',
|
|
9
|
+
// Only this app's components go through the Angular compiler; it would
|
|
10
|
+
// empty out a linked package's TypeScript, which isn't in the tsconfig.
|
|
11
|
+
transformFilter: (_code, id) => id.startsWith(`${import.meta.dirname}/src/`),
|
|
12
|
+
}),
|
|
13
|
+
quicklook(),
|
|
14
|
+
],
|
|
15
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "quicklook-astro",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "quicklook dev",
|
|
7
|
+
"build": "quicklook build",
|
|
8
|
+
"register": "quicklook build && quicklook register",
|
|
9
|
+
"unregister": "quicklook unregister",
|
|
10
|
+
"check": "astro check",
|
|
11
|
+
"lint": "biome check",
|
|
12
|
+
"format": "biome check --write"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@astrojs/check": "0.9.10",
|
|
16
|
+
"@biomejs/biome": "2.5.14",
|
|
17
|
+
"astro": "7.3.5",
|
|
18
|
+
"quicklook-kit": "^0.3.0",
|
|
19
|
+
"typescript": "6.0.3"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'quicklook-kit';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
name: 'QuickLook Example',
|
|
5
|
+
bundleId: 'com.example.QuickLookExample',
|
|
6
|
+
types: [
|
|
7
|
+
{
|
|
8
|
+
id: 'com.example.QuickLookExample.example',
|
|
9
|
+
description: 'Example File',
|
|
10
|
+
extensions: ['example'],
|
|
11
|
+
conformsTo: ['public.plain-text'],
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
framework: 'astro',
|
|
15
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Your preview goes here. `file.bytes` holds the raw file; decode it however
|
|
3
|
+
the format needs. This example shows it as UTF-8 text. Astro components only
|
|
4
|
+
render on the server, so the viewer is a custom element the page calls.
|
|
5
|
+
-->
|
|
6
|
+
<file-viewer hidden><pre></pre></file-viewer>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import type { QuickLookFile } from 'virtual:quicklook';
|
|
10
|
+
|
|
11
|
+
class FileViewer extends HTMLElement {
|
|
12
|
+
show(file: QuickLookFile) {
|
|
13
|
+
const pre = this.querySelector('pre');
|
|
14
|
+
if (pre) {
|
|
15
|
+
pre.textContent = new TextDecoder().decode(file.bytes);
|
|
16
|
+
}
|
|
17
|
+
this.hidden = false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare global {
|
|
22
|
+
interface HTMLElementTagNameMap {
|
|
23
|
+
'file-viewer': FileViewer;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
customElements.define('file-viewer', FileViewer);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
pre {
|
|
32
|
+
margin: 0;
|
|
33
|
+
padding: 1rem;
|
|
34
|
+
font:
|
|
35
|
+
12px ui-monospace,
|
|
36
|
+
monospace;
|
|
37
|
+
white-space: pre-wrap;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="quicklook-kit/virtual" />
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
import FileViewer from '../components/FileViewer.astro';
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<html lang="en">
|
|
6
|
+
<head>
|
|
7
|
+
<meta charset="utf-8" />
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
9
|
+
<meta name="color-scheme" content="light dark" />
|
|
10
|
+
<title>Preview</title>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<FileViewer />
|
|
14
|
+
<!-- Only reachable in the browser: QuickLook always injects a file. -->
|
|
15
|
+
<label id="drop">
|
|
16
|
+
Drop a file here, or click to pick one
|
|
17
|
+
<input type="file" hidden />
|
|
18
|
+
</label>
|
|
19
|
+
|
|
20
|
+
<script>
|
|
21
|
+
import { browserFile, nativeFile, type QuickLookFile } from 'virtual:quicklook';
|
|
22
|
+
|
|
23
|
+
const drop = document.querySelector<HTMLLabelElement>('#drop');
|
|
24
|
+
|
|
25
|
+
async function show(file: QuickLookFile) {
|
|
26
|
+
await customElements.whenDefined('file-viewer');
|
|
27
|
+
document.querySelector('file-viewer')?.show(file);
|
|
28
|
+
if (drop) {
|
|
29
|
+
drop.hidden = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function open(files: FileList | null | undefined) {
|
|
34
|
+
const picked = files?.[0];
|
|
35
|
+
if (picked) {
|
|
36
|
+
await show(await browserFile(picked));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const injected = nativeFile();
|
|
41
|
+
if (injected) {
|
|
42
|
+
show(injected);
|
|
43
|
+
}
|
|
44
|
+
window.addEventListener('dragover', (event) => event.preventDefault());
|
|
45
|
+
window.addEventListener('drop', (event) => {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
open(event.dataTransfer?.files);
|
|
48
|
+
});
|
|
49
|
+
drop?.querySelector('input')?.addEventListener('change', (event) => {
|
|
50
|
+
open((event.currentTarget as HTMLInputElement).files);
|
|
51
|
+
});
|
|
52
|
+
</script>
|
|
53
|
+
</body>
|
|
54
|
+
</html>
|
|
55
|
+
|
|
56
|
+
<style is:global>
|
|
57
|
+
body {
|
|
58
|
+
margin: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#drop {
|
|
62
|
+
display: grid;
|
|
63
|
+
place-items: center;
|
|
64
|
+
height: 100vh;
|
|
65
|
+
font: 14px system-ui;
|
|
66
|
+
color: GrayText;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#drop[hidden] {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { browserFile, nativeFile, type QuickLookFile } from 'virtual:quicklook';
|
|
3
|
+
import { onMounted, shallowRef } from 'vue';
|
|
4
|
+
import FileViewer from '~/components/FileViewer.vue';
|
|
5
|
+
|
|
6
|
+
const file = shallowRef<QuickLookFile | undefined>(nativeFile());
|
|
7
|
+
|
|
8
|
+
async function open(files: FileList | null | undefined) {
|
|
9
|
+
const picked = files?.[0];
|
|
10
|
+
if (picked) {
|
|
11
|
+
file.value = await browserFile(picked);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
onMounted(() => {
|
|
16
|
+
window.addEventListener('dragover', (event) => event.preventDefault());
|
|
17
|
+
window.addEventListener('drop', (event) => {
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
open(event.dataTransfer?.files);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<FileViewer v-if="file" :file="file" />
|
|
26
|
+
<!-- Only reachable in the browser: QuickLook always injects a file. -->
|
|
27
|
+
<label v-else class="drop">
|
|
28
|
+
Drop a file here, or click to pick one
|
|
29
|
+
<input type="file" hidden @change="open(($event.target as HTMLInputElement).files)" />
|
|
30
|
+
</label>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
body {
|
|
35
|
+
margin: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.drop {
|
|
39
|
+
display: grid;
|
|
40
|
+
place-items: center;
|
|
41
|
+
height: 100vh;
|
|
42
|
+
font: 14px system-ui;
|
|
43
|
+
color: GrayText;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!-- Your preview goes here. `file.bytes` holds the raw file; decode it however
|
|
2
|
+
the format needs. This example shows it as UTF-8 text. -->
|
|
3
|
+
<script setup lang="ts">
|
|
4
|
+
import type { QuickLookFile } from 'virtual:quicklook';
|
|
5
|
+
import { computed } from 'vue';
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{ file: QuickLookFile }>();
|
|
8
|
+
const text = computed(() => new TextDecoder().decode(props.file.bytes));
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<pre v-text="text" />
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<style scoped>
|
|
16
|
+
pre {
|
|
17
|
+
margin: 0;
|
|
18
|
+
padding: 1rem;
|
|
19
|
+
font:
|
|
20
|
+
12px ui-monospace,
|
|
21
|
+
monospace;
|
|
22
|
+
white-space: pre-wrap;
|
|
23
|
+
}
|
|
24
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineNuxtConfig } from 'nuxt/config';
|
|
2
|
+
import { quicklook } from 'quicklook-kit/vite';
|
|
3
|
+
|
|
4
|
+
export default defineNuxtConfig({
|
|
5
|
+
compatibilityDate: '2026-07-01',
|
|
6
|
+
ssr: false,
|
|
7
|
+
app: { head: { meta: [{ name: 'color-scheme', content: 'light dark' }] } },
|
|
8
|
+
// The build manifest is fetched at runtime, which the extension's CSP blocks.
|
|
9
|
+
experimental: { appManifest: false },
|
|
10
|
+
vite: { plugins: [quicklook()] },
|
|
11
|
+
typescript: {
|
|
12
|
+
tsConfig: {
|
|
13
|
+
compilerOptions: { allowImportingTsExtensions: true, types: ['quicklook-kit/virtual'] },
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "quicklook-nuxt",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"postinstall": "nuxt prepare",
|
|
7
|
+
"dev": "quicklook dev",
|
|
8
|
+
"build": "quicklook build",
|
|
9
|
+
"register": "quicklook build && quicklook register",
|
|
10
|
+
"unregister": "quicklook unregister",
|
|
11
|
+
"check": "nuxt typecheck",
|
|
12
|
+
"lint": "biome check",
|
|
13
|
+
"format": "biome check --write"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@biomejs/biome": "2.5.14",
|
|
17
|
+
"nuxt": "4.5.2",
|
|
18
|
+
"quicklook-kit": "^0.3.0",
|
|
19
|
+
"typescript": "6.0.3",
|
|
20
|
+
"vue": "3.5.43",
|
|
21
|
+
"vue-tsc": "3.3.11"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'quicklook-kit';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
name: 'QuickLook Example',
|
|
5
|
+
bundleId: 'com.example.QuickLookExample',
|
|
6
|
+
types: [
|
|
7
|
+
{
|
|
8
|
+
id: 'com.example.QuickLookExample.example',
|
|
9
|
+
description: 'Example File',
|
|
10
|
+
extensions: ['example'],
|
|
11
|
+
conformsTo: ['public.plain-text'],
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
framework: 'nuxt',
|
|
15
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "quicklook-solid-start",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "quicklook dev",
|
|
7
|
+
"build": "quicklook build",
|
|
8
|
+
"register": "quicklook build && quicklook register",
|
|
9
|
+
"unregister": "quicklook unregister",
|
|
10
|
+
"check": "tsc --noEmit",
|
|
11
|
+
"lint": "biome check",
|
|
12
|
+
"format": "biome check --write"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@biomejs/biome": "2.5.14",
|
|
16
|
+
"@solidjs/router": "1.0.0",
|
|
17
|
+
"@solidjs/start": "2.0.6",
|
|
18
|
+
"nitro": "3.0.260903-beta",
|
|
19
|
+
"quicklook-kit": "^0.3.0",
|
|
20
|
+
"solid-js": "1.9.17",
|
|
21
|
+
"typescript": "6.0.3",
|
|
22
|
+
"vite": "8.3.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'quicklook-kit';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
name: 'QuickLook Example',
|
|
5
|
+
bundleId: 'com.example.QuickLookExample',
|
|
6
|
+
types: [
|
|
7
|
+
{
|
|
8
|
+
id: 'com.example.QuickLookExample.example',
|
|
9
|
+
description: 'Example File',
|
|
10
|
+
extensions: ['example'],
|
|
11
|
+
conformsTo: ['public.plain-text'],
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
framework: 'solid-start',
|
|
15
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Router } from '@solidjs/router';
|
|
2
|
+
import { FileRoutes } from '@solidjs/start/router';
|
|
3
|
+
import { Suspense } from 'solid-js';
|
|
4
|
+
import './app.css';
|
|
5
|
+
|
|
6
|
+
export default function App() {
|
|
7
|
+
return (
|
|
8
|
+
<Router root={(props) => <Suspense>{props.children}</Suspense>}>
|
|
9
|
+
<FileRoutes />
|
|
10
|
+
</Router>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Your preview goes here. `file.bytes` holds the raw file; decode it however
|
|
2
|
+
// the format needs. This example shows it as UTF-8 text.
|
|
3
|
+
import type { QuickLookFile } from 'virtual:quicklook';
|
|
4
|
+
import './FileViewer.css';
|
|
5
|
+
|
|
6
|
+
export default function FileViewer(props: { file: QuickLookFile }) {
|
|
7
|
+
return <pre>{new TextDecoder().decode(props.file.bytes)}</pre>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @refresh reload
|
|
2
|
+
import { createHandler, StartServer } from '@solidjs/start/server';
|
|
3
|
+
|
|
4
|
+
export default createHandler(() => (
|
|
5
|
+
<StartServer
|
|
6
|
+
document={({ assets, children, scripts }) => (
|
|
7
|
+
<html lang="en">
|
|
8
|
+
<head>
|
|
9
|
+
<meta charset="utf-8" />
|
|
10
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
11
|
+
<meta name="color-scheme" content="light dark" />
|
|
12
|
+
<title>Preview</title>
|
|
13
|
+
{assets}
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<div id="app">{children}</div>
|
|
17
|
+
{scripts}
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
20
|
+
)}
|
|
21
|
+
/>
|
|
22
|
+
));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { browserFile, nativeFile, type QuickLookFile } from 'virtual:quicklook';
|
|
2
|
+
import { createSignal, Show } from 'solid-js';
|
|
3
|
+
import FileViewer from '~/components/FileViewer';
|
|
4
|
+
|
|
5
|
+
export default function Home() {
|
|
6
|
+
const [file, setFile] = createSignal<QuickLookFile | undefined>(nativeFile());
|
|
7
|
+
|
|
8
|
+
async function open(files: FileList | null | undefined) {
|
|
9
|
+
const picked = files?.[0];
|
|
10
|
+
if (picked) {
|
|
11
|
+
setFile(await browserFile(picked));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
window.addEventListener('dragover', (event) => event.preventDefault());
|
|
16
|
+
window.addEventListener('drop', (event) => {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
open(event.dataTransfer?.files);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Show
|
|
23
|
+
when={file()}
|
|
24
|
+
fallback={
|
|
25
|
+
// Only reachable in the browser: QuickLook always injects a file.
|
|
26
|
+
<label class="drop">
|
|
27
|
+
Drop a file here, or click to pick one
|
|
28
|
+
<input type="file" hidden={true} onChange={(event) => open(event.currentTarget.files)} />
|
|
29
|
+
</label>
|
|
30
|
+
}
|
|
31
|
+
>
|
|
32
|
+
{(current) => <FileViewer file={current()} />}
|
|
33
|
+
</Show>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2023",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"jsx": "preserve",
|
|
7
|
+
"jsxImportSource": "solid-js",
|
|
8
|
+
"types": ["vite/client", "quicklook-kit/virtual"],
|
|
9
|
+
"strict": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"paths": { "~/*": ["./src/*"] }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { solidStart } from '@solidjs/start/config';
|
|
2
|
+
import { nitro } from 'nitro/vite';
|
|
3
|
+
import { quicklook } from 'quicklook-kit/vite';
|
|
4
|
+
import { defineConfig } from 'vite';
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
// Client-only, prerendered to a static index.html in .output/public.
|
|
9
|
+
solidStart({ ssr: false }),
|
|
10
|
+
nitro({ preset: 'static', prerender: { routes: ['/'] } }),
|
|
11
|
+
quicklook(),
|
|
12
|
+
],
|
|
13
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "quicklook-sveltekit",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "quicklook dev",
|
|
7
|
+
"build": "quicklook build",
|
|
8
|
+
"register": "quicklook build && quicklook register",
|
|
9
|
+
"unregister": "quicklook unregister",
|
|
10
|
+
"check": "svelte-kit sync && svelte-check",
|
|
11
|
+
"lint": "biome check",
|
|
12
|
+
"format": "biome check --write"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@biomejs/biome": "2.5.14",
|
|
16
|
+
"@sveltejs/adapter-static": "3.0.10",
|
|
17
|
+
"@sveltejs/kit": "2.70.3",
|
|
18
|
+
"@sveltejs/vite-plugin-svelte": "7.3.1",
|
|
19
|
+
"quicklook-kit": "^0.3.0",
|
|
20
|
+
"svelte": "5.57.1",
|
|
21
|
+
"svelte-check": "4.7.6",
|
|
22
|
+
"typescript": "6.0.3",
|
|
23
|
+
"vite": "8.3.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'quicklook-kit';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
name: 'QuickLook Example',
|
|
5
|
+
bundleId: 'com.example.QuickLookExample',
|
|
6
|
+
types: [
|
|
7
|
+
{
|
|
8
|
+
id: 'com.example.QuickLookExample.example',
|
|
9
|
+
description: 'Example File',
|
|
10
|
+
extensions: ['example'],
|
|
11
|
+
conformsTo: ['public.plain-text'],
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
framework: 'sveltekit',
|
|
15
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="quicklook-kit/virtual" />
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="color-scheme" content="light dark" />
|
|
7
|
+
%sveltekit.head%
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div style="display: contents">%sveltekit.body%</div>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Your preview goes here. `file.bytes` holds the raw file; decode it however
|
|
3
|
+
the format needs. This example shows it as UTF-8 text.
|
|
4
|
+
-->
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import type { QuickLookFile } from 'virtual:quicklook';
|
|
7
|
+
|
|
8
|
+
let { file }: { file: QuickLookFile } = $props();
|
|
9
|
+
|
|
10
|
+
const text = $derived(new TextDecoder().decode(file.bytes));
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<pre>{text}</pre>
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
:global(body) {
|
|
17
|
+
margin: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pre {
|
|
21
|
+
margin: 0;
|
|
22
|
+
padding: 1rem;
|
|
23
|
+
font:
|
|
24
|
+
12px ui-monospace,
|
|
25
|
+
monospace;
|
|
26
|
+
white-space: pre-wrap;
|
|
27
|
+
}
|
|
28
|
+
</style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { browserFile, nativeFile, type QuickLookFile } from 'virtual:quicklook';
|
|
3
|
+
import FileViewer from '$lib/FileViewer.svelte';
|
|
4
|
+
|
|
5
|
+
let file = $state<QuickLookFile | undefined>(nativeFile());
|
|
6
|
+
|
|
7
|
+
async function open(files: FileList | null | undefined) {
|
|
8
|
+
const picked = files?.[0];
|
|
9
|
+
if (picked) {
|
|
10
|
+
file = await browserFile(picked);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<!-- Only reachable in the browser: QuickLook always injects a file. -->
|
|
16
|
+
<svelte:window
|
|
17
|
+
ondragover={(e) => e.preventDefault()}
|
|
18
|
+
ondrop={(e) => {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
open(e.dataTransfer?.files);
|
|
21
|
+
}}
|
|
22
|
+
/>
|
|
23
|
+
|
|
24
|
+
{#if file}
|
|
25
|
+
<FileViewer {file} />
|
|
26
|
+
{:else}
|
|
27
|
+
<label class="drop">
|
|
28
|
+
Drop a file here, or click to pick one
|
|
29
|
+
<input type="file" hidden onchange={(e) => open(e.currentTarget.files)} />
|
|
30
|
+
</label>
|
|
31
|
+
{/if}
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
.drop {
|
|
35
|
+
display: grid;
|
|
36
|
+
place-items: center;
|
|
37
|
+
height: 100vh;
|
|
38
|
+
font: 14px system-ui;
|
|
39
|
+
color: GrayText;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import adapter from '@sveltejs/adapter-static';
|
|
2
|
+
|
|
3
|
+
/** @type {import('@sveltejs/kit').Config} */
|
|
4
|
+
export default {
|
|
5
|
+
kit: {
|
|
6
|
+
// A static build; the extension serves it and sets the CSP from quicklook.config.ts.
|
|
7
|
+
adapter: adapter(),
|
|
8
|
+
// Builds a client-only app shell, which is all the preview needs.
|
|
9
|
+
router: { type: 'hash' },
|
|
10
|
+
},
|
|
11
|
+
};
|
package/README.md
DELETED