This commit is contained in:
parent
11fa3d1558
commit
ad01868a9f
23 changed files with 328 additions and 97 deletions
29
Dockerfile
29
Dockerfile
|
|
@ -1,29 +0,0 @@
|
||||||
FROM oven/bun:1 AS base
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
# Installe les dépendances de développement.
|
|
||||||
FROM base AS install
|
|
||||||
RUN mkdir -p /temp/dev
|
|
||||||
COPY package.json bun.lock /temp/dev/
|
|
||||||
RUN cd /temp/dev && bun install --frozen-lockfile
|
|
||||||
# Installe les dépendances de production.
|
|
||||||
RUN mkdir -p /temp/prod
|
|
||||||
COPY package.json bun.lock /temp/prod/
|
|
||||||
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
|
||||||
|
|
||||||
# Récupère node_modules et les fichiers du projet.
|
|
||||||
FROM base AS prerelease
|
|
||||||
COPY --from=install /temp/dev/node_modules/ node_modules
|
|
||||||
COPY . .
|
|
||||||
# Compile le projet.
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
RUN bun --bun vite build
|
|
||||||
|
|
||||||
# Créé le nécessaire pour Angie, le proxy inversé servant l'application.
|
|
||||||
FROM docker.angie.software/angie:minimal AS release
|
|
||||||
COPY --from=prerelease /usr/src/app/dist/ /usr/share/angie/html/
|
|
||||||
COPY ./docker/default.conf /etc/angie/http.d/default.conf
|
|
||||||
|
|
||||||
# Démarre Angie.
|
|
||||||
EXPOSE 80
|
|
||||||
CMD ["angie", "-g", "daemon off;"]
|
|
||||||
12
bun.lock
12
bun.lock
|
|
@ -213,11 +213,11 @@
|
||||||
|
|
||||||
"@effect/experimental": ["@effect/experimental@0.41.4", "", { "dependencies": { "msgpackr": "^1.10.2", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.77.4", "@effect/platform-node": "^0.73.4", "effect": "^3.13.4", "ioredis": "^5", "lmdb": "^3", "ws": "^8" }, "optionalPeers": ["@effect/platform-node", "ioredis", "lmdb", "ws"] }, "sha512-celrEhl/K2Eis906d3/oOv/3/w+M1jcONvrChG5qQgWYo6CJjSJm4xXopcTbiBhxAhvkd06zABkB3tr/lptUCA=="],
|
"@effect/experimental": ["@effect/experimental@0.41.4", "", { "dependencies": { "msgpackr": "^1.10.2", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.77.4", "@effect/platform-node": "^0.73.4", "effect": "^3.13.4", "ioredis": "^5", "lmdb": "^3", "ws": "^8" }, "optionalPeers": ["@effect/platform-node", "ioredis", "lmdb", "ws"] }, "sha512-celrEhl/K2Eis906d3/oOv/3/w+M1jcONvrChG5qQgWYo6CJjSJm4xXopcTbiBhxAhvkd06zABkB3tr/lptUCA=="],
|
||||||
|
|
||||||
"@effect/platform": ["@effect/platform@0.77.4", "", { "dependencies": { "find-my-way-ts": "^0.1.5", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.13.4" } }, "sha512-3jBDn4bAWqRSKARmoMY2NmMxIMM5Py5BqJdUuYJqPyI6jbz7qkOMwgeaF62rWqiqhQLHLTgU4QL/cPwyZl7mVg=="],
|
"@effect/platform": ["@effect/platform@0.77.6", "", { "dependencies": { "find-my-way-ts": "^0.1.5", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.13.6" } }, "sha512-ghhLNyj/UoQvmp2I29nqngMlAzQB72BhjUKcOA58cUPaUUwNy3K2jmUAzdU6SB3RHIObsX44CM/jXZiYfTv59A=="],
|
||||||
|
|
||||||
"@effect/sql": ["@effect/sql@0.30.4", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.25.1", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.41.4", "@effect/platform": "^0.77.4", "effect": "^3.13.4" } }, "sha512-hyGOZsNRlw09yqBcXCTr1o2+vVIVxEnBMNZzi0ZX5JbS8JBT05O7xAu3OYEzSU6bC5i+uVpJJUgdx2zDlOLkeA=="],
|
"@effect/sql": ["@effect/sql@0.30.4", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.25.1", "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.41.4", "@effect/platform": "^0.77.4", "effect": "^3.13.4" } }, "sha512-hyGOZsNRlw09yqBcXCTr1o2+vVIVxEnBMNZzi0ZX5JbS8JBT05O7xAu3OYEzSU6bC5i+uVpJJUgdx2zDlOLkeA=="],
|
||||||
|
|
||||||
"@effect/sql-drizzle": ["@effect/sql-drizzle@0.29.4", "", { "peerDependencies": { "@effect/sql": "^0.30.4", "drizzle-orm": "^0.31", "effect": "^3.13.4" } }, "sha512-Hz8RypUu5g/V3DVmD0WLK+Nqy9H/V/GA2ew41sSZhJ0fTmxaCoQ08it23qypeNN66CCJNF5uE9OWnHl10cfKGA=="],
|
"@effect/sql-drizzle": ["@effect/sql-drizzle@0.29.6", "", { "peerDependencies": { "@effect/sql": "^0.30.6", "drizzle-orm": "^0.31", "effect": "^3.13.6" } }, "sha512-ST3HEXQZfp9tUXKOKxpfh9bbTukJsomyXXzZLvPo/KvF8nPpRltovYNT55Dsk+0vN8JyOHri+mX8hKBG5JzXcA=="],
|
||||||
|
|
||||||
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
||||||
|
|
||||||
|
|
@ -623,7 +623,7 @@
|
||||||
|
|
||||||
"easy-table": ["easy-table@1.2.0", "", { "dependencies": { "ansi-regex": "^5.0.1" }, "optionalDependencies": { "wcwidth": "^1.0.1" } }, "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww=="],
|
"easy-table": ["easy-table@1.2.0", "", { "dependencies": { "ansi-regex": "^5.0.1" }, "optionalDependencies": { "wcwidth": "^1.0.1" } }, "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww=="],
|
||||||
|
|
||||||
"effect": ["effect@3.13.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-XZgCImyBpONuKdsBRk37JfV7242yxTu8r/TcL5ZELIyqRbMYa+Prr86cz1INxXi7iFXfU0havZkCJGyYp1BsiA=="],
|
"effect": ["effect@3.13.6", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-NKmzyIuOb2UuHFPRz9EYScbhMBxXkzjPRuu+4axE+hMk1f0U7TZxzi2CP3TVVxA2kzvh00aBQEbyH7Opq4PnWg=="],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.109", "", {}, "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.109", "", {}, "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ=="],
|
||||||
|
|
||||||
|
|
@ -649,7 +649,7 @@
|
||||||
|
|
||||||
"eslint-plugin-perfectionist": ["eslint-plugin-perfectionist@4.9.0", "", { "dependencies": { "@typescript-eslint/types": "^8.24.0", "@typescript-eslint/utils": "^8.24.0", "natural-orderby": "^5.0.0" }, "peerDependencies": { "eslint": ">=8.0.0" } }, "sha512-76lDfJnonOcXGW3bEXuqhEGId0LrOlvIE1yLHvK/eKMMPOc0b43KchAIR2Bdbqlg+LPXU5/Q+UzuzkO+cWHT6w=="],
|
"eslint-plugin-perfectionist": ["eslint-plugin-perfectionist@4.9.0", "", { "dependencies": { "@typescript-eslint/types": "^8.24.0", "@typescript-eslint/utils": "^8.24.0", "natural-orderby": "^5.0.0" }, "peerDependencies": { "eslint": ">=8.0.0" } }, "sha512-76lDfJnonOcXGW3bEXuqhEGId0LrOlvIE1yLHvK/eKMMPOc0b43KchAIR2Bdbqlg+LPXU5/Q+UzuzkO+cWHT6w=="],
|
||||||
|
|
||||||
"eslint-plugin-vue": ["eslint-plugin-vue@9.32.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "globals": "^13.24.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.15", "semver": "^7.6.3", "vue-eslint-parser": "^9.4.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug=="],
|
"eslint-plugin-vue": ["eslint-plugin-vue@10.0.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.15", "semver": "^7.6.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" } }, "sha512-XKckedtajqwmaX6u1VnECmZ6xJt+YvlmMzBPZd+/sI3ub2lpYZyFnsyWo7c3nMOQKJQudeyk1lw/JxdgeKT64w=="],
|
||||||
|
|
||||||
"eslint-scope": ["eslint-scope@8.2.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A=="],
|
"eslint-scope": ["eslint-scope@8.2.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A=="],
|
||||||
|
|
||||||
|
|
@ -1037,8 +1037,6 @@
|
||||||
|
|
||||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||||
|
|
||||||
"type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="],
|
|
||||||
|
|
||||||
"typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="],
|
"typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="],
|
||||||
|
|
||||||
"typescript-eslint": ["typescript-eslint@8.25.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.25.0", "@typescript-eslint/parser": "8.25.0", "@typescript-eslint/utils": "8.25.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-TxRdQQLH4g7JkoFlYG3caW5v1S6kEkz8rqt80iQJZUYPq1zD1Ra7HfQBJJ88ABRaMvHAXnwRvRB4V+6sQ9xN5Q=="],
|
"typescript-eslint": ["typescript-eslint@8.25.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.25.0", "@typescript-eslint/parser": "8.25.0", "@typescript-eslint/utils": "8.25.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-TxRdQQLH4g7JkoFlYG3caW5v1S6kEkz8rqt80iQJZUYPq1zD1Ra7HfQBJJ88ABRaMvHAXnwRvRB4V+6sQ9xN5Q=="],
|
||||||
|
|
@ -1129,8 +1127,6 @@
|
||||||
|
|
||||||
"eslint/file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
"eslint/file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||||
|
|
||||||
"eslint-plugin-vue/globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="],
|
|
||||||
|
|
||||||
"fast-glob/@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
"fast-glob/@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||||
|
|
||||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
|
||||||
53
containers/Dockerfile
Normal file
53
containers/Dockerfile
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
FROM oven/bun:1 AS base
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Définis les variables d'environnement.
|
||||||
|
ARG VERSION="0.1"
|
||||||
|
|
||||||
|
# Installe les dépendances de développement.
|
||||||
|
FROM base AS install
|
||||||
|
RUN mkdir -p /temp/dev
|
||||||
|
COPY package.json bun.lock /temp/dev/
|
||||||
|
RUN cd /temp/dev && bun install --frozen-lockfile
|
||||||
|
# Installe les dépendances de production.
|
||||||
|
RUN mkdir -p /temp/prod
|
||||||
|
COPY package.json bun.lock /temp/prod/
|
||||||
|
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
||||||
|
|
||||||
|
# Récupère node_modules et les fichiers du projet.
|
||||||
|
FROM base AS prerelease
|
||||||
|
COPY --from=install /temp/dev/node_modules/ node_modules
|
||||||
|
COPY . .
|
||||||
|
# Compile le projet.
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
RUN bun --bun vite build
|
||||||
|
|
||||||
|
# Créé le nécessaire pour Angie, le proxy inversé servant l'application.
|
||||||
|
FROM docker.angie.software/angie:minimal AS release
|
||||||
|
COPY --from=prerelease /usr/src/app/dist/ /usr/share/angie/html/
|
||||||
|
COPY containers/angie/default.conf /etc/angie/http.d/default.conf
|
||||||
|
|
||||||
|
# Met en place les métadonnées OCI.
|
||||||
|
LABEL org.opencontainers.image.title=journal-media-vue \
|
||||||
|
org.opencontainers.image.description="A modern client-server application for the Soulseek file sharing network" \
|
||||||
|
org.opencontainers.image.authors="gcch" \
|
||||||
|
org.opencontainers.image.vendor="gcch" \
|
||||||
|
org.opencontainers.image.licenses=AGPL-3.0
|
||||||
|
# org.opencontainers.image.url=https://slskd.org \
|
||||||
|
# org.opencontainers.image.source=https://github.com/slskd/slskd \
|
||||||
|
# org.opencontainers.image.documentation=https://github.com/slskd/slskd \
|
||||||
|
# org.opencontainers.image.version=$VERSION \
|
||||||
|
# org.opencontainers.image.revision=$REVISION \
|
||||||
|
# org.opencontainers.image.created=$BUILD_DATE
|
||||||
|
|
||||||
|
# Démarre Angie.
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["angie", "-g", "daemon off;"]
|
||||||
|
|
||||||
|
# Met en place une vérification périodique de la bonne santé du conteneur.
|
||||||
|
HEALTHCHECK \
|
||||||
|
--interval=60s \
|
||||||
|
--timeout=3s \
|
||||||
|
--start-period=5s \
|
||||||
|
--retries=3 \
|
||||||
|
CMD wget -q -O - http://localhost:80/
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
"quartary",
|
"quartary",
|
||||||
"fieldset",
|
"fieldset",
|
||||||
"tabindex",
|
"tabindex",
|
||||||
"currentcolor"
|
"currentcolor",
|
||||||
|
"labelledby"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ services:
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: containers/Dockerfile
|
||||||
container_name: journal-media-vue
|
container_name: journal-media-vue
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:8080:8
|
- 127.0.0.1:8080:8
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform": "^0.77.4",
|
"@effect/platform": "^0.77.6",
|
||||||
"@effect/sql-drizzle": "^0.29.4",
|
"@effect/sql-drizzle": "^0.29.6",
|
||||||
"@thi.ng/color-palettes": "^1.4.36",
|
"@thi.ng/color-palettes": "^1.4.36",
|
||||||
"@thi.ng/pixel": "^7.3.19",
|
"@thi.ng/pixel": "^7.3.19",
|
||||||
"@thi.ng/pixel-dither": "^1.1.159",
|
"@thi.ng/pixel-dither": "^1.1.159",
|
||||||
"a11y-dialog": "^8.1.1",
|
"a11y-dialog": "^8.1.1",
|
||||||
"drizzle-orm": "^0.40.0",
|
"drizzle-orm": "^0.40.0",
|
||||||
"effect": "^3.13.4",
|
"effect": "^3.13.6",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"sqlocal": "^0.14.0",
|
"sqlocal": "^0.14.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
"drizzle-kit": "^0.30.5",
|
"drizzle-kit": "^0.30.5",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"eslint-plugin-perfectionist": "^4.9.0",
|
"eslint-plugin-perfectionist": "^4.9.0",
|
||||||
"eslint-plugin-vue": "^9.32.0",
|
"eslint-plugin-vue": "^10.0.0",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"jiti": "^2.4.2",
|
"jiti": "^2.4.2",
|
||||||
"knip": "^5.45.0",
|
"knip": "^5.45.0",
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,12 @@
|
||||||
tabindex="0" @click="onRowClicked(result[0])" @keypress="onRowClicked(result[0])"
|
tabindex="0" @click="onRowClicked(result[0])" @keypress="onRowClicked(result[0])"
|
||||||
>
|
>
|
||||||
<th class="name" scope="row">
|
<th class="name" scope="row">
|
||||||
{{ result[1].original_result_index }}
|
{{ result[1].originalResultIndex }}
|
||||||
</th>
|
</th>
|
||||||
<th class="name" scope="row">
|
<th class="name" scope="row">
|
||||||
{{ result[1].original_title }}
|
{{ result[1].originalTitle }}
|
||||||
</th>
|
</th>
|
||||||
<td class="release-date">{{ result[1].release_date }}</td>
|
<td class="release-date">{{ result[1].releaseDate }}</td>
|
||||||
<td class="popularite">{{ result[1].popularity }}</td>
|
<td class="popularite">{{ result[1].popularity }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,62 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MergedTmdbLocalData } from "@/libs/search/schemas";
|
import type { MergedTmdbLocalData } from "@/libs/search/schemas";
|
||||||
|
import type { ComputedRef, Ref, ShallowRef } from "vue";
|
||||||
|
|
||||||
import ImposterBox from "@/components/dialogs/ImposterBox.vue";
|
import ImposterBox from "@/components/dialogs/ImposterBox.vue";
|
||||||
import { Images } from "@/services/images.ts";
|
import { Images } from "@/services/images.ts";
|
||||||
import { RuntimeClient } from "@/services/runtime-client";
|
import { RuntimeClient } from "@/services/runtime-client";
|
||||||
import { Url } from "@effect/platform";
|
import { Url } from "@effect/platform";
|
||||||
import { Effect } from "effect";
|
import { Effect, pipe } from "effect";
|
||||||
import { onMounted } from "vue";
|
import { isTruthy } from "effect/Predicate";
|
||||||
import { ref } from "vue";
|
import { onMounted, ref, useTemplateRef, watch } from "vue";
|
||||||
import { watchEffect } from "vue";
|
import { computed } from "vue";
|
||||||
import { useTemplateRef } from "vue";
|
|
||||||
|
|
||||||
|
// Émissions et props
|
||||||
const emit = defineEmits(["dialog-hidden"]);
|
const emit = defineEmits(["dialog-hidden"]);
|
||||||
const { entryData } = defineProps<{ entryData: MergedTmdbLocalData }>();
|
const { entryData } = defineProps<{ entryData: MergedTmdbLocalData }>();
|
||||||
|
|
||||||
const ditheredPoster = ref<HTMLCanvasElement>();
|
const imageContainer: Readonly<ShallowRef<HTMLDivElement | null>> = useTemplateRef("imageContainer");
|
||||||
const imageContainer = useTemplateRef("imageContainer");
|
const ditheredPoster: Ref<HTMLCanvasElement | undefined> = ref<HTMLCanvasElement>();
|
||||||
|
const isFirstTimeEntryEditing: Ref<boolean> = ref<boolean>(false);
|
||||||
|
|
||||||
const closeDialog = () => {
|
const hasEntry: ComputedRef<boolean> = computed(() => isTruthy(entryData.entryId));
|
||||||
|
const hasUniqueOriginalTitle: ComputedRef<boolean> = computed(() =>
|
||||||
|
entryData.originalTitle.toLowerCase() !== entryData.title.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstTimeEditingButtonText: ComputedRef<"Ajouter" | "Annuler"> = computed(() =>
|
||||||
|
isFirstTimeEntryEditing.value ? "Annuler" : "Ajouter"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Gestionnaires d'événements
|
||||||
|
const closeDialog = (event?: Event): void => {
|
||||||
|
event?.preventDefault();
|
||||||
emit("dialog-hidden");
|
emit("dialog-hidden");
|
||||||
};
|
};
|
||||||
|
const toggleFirstTimeEditing = (event?: Event): void => {
|
||||||
|
event?.preventDefault();
|
||||||
|
isFirstTimeEntryEditing.value = !isFirstTimeEntryEditing.value;
|
||||||
|
};
|
||||||
|
|
||||||
watchEffect(async () => {
|
// Cycles
|
||||||
|
watch(() => entryData, async (): Promise<void> => {
|
||||||
ditheredPoster.value = await RuntimeClient.runPromise(Effect.gen(function*() {
|
ditheredPoster.value = await RuntimeClient.runPromise(Effect.gen(function*() {
|
||||||
if (!entryData.artWorkCoverPath || !imageContainer.value) return undefined;
|
if (!entryData.posterUrl) return undefined;
|
||||||
const imageService: Images = yield* Images;
|
console.debug("dithering");
|
||||||
const originalUrl = yield* Url.fromString(`https://image.tmdb.org/t/p/w500/${entryData.artWorkCoverPath}`);
|
|
||||||
const originalImage = yield* imageService.imageFromUrl(originalUrl);
|
|
||||||
const ditheredImage = yield* imageService.ditherImage(originalImage, imageContainer.value);
|
|
||||||
|
|
||||||
return ditheredImage;
|
const imageService: Images = yield* Images;
|
||||||
|
|
||||||
|
return yield* pipe(
|
||||||
|
Url.fromString(`https://image.tmdb.org/t/p/w500/${entryData.posterUrl}`),
|
||||||
|
Effect.andThen((url: URL) => imageService.imageFromUrl(url)),
|
||||||
|
Effect.andThen((img: HTMLImageElement) => imageService.ditherImage(img, imageContainer.value ?? undefined)),
|
||||||
|
Effect.andThen(dithered => dithered.canvas),
|
||||||
|
);
|
||||||
|
|
||||||
|
// const base64 = encodeBase64Url(ditheredImage.buffer.data);
|
||||||
|
// console.debug(base64.length);
|
||||||
}));
|
}));
|
||||||
});
|
}, { immediate: true });
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.debug("EditEntryDialog mounted");
|
console.debug("EditEntryDialog mounted");
|
||||||
|
|
@ -43,13 +68,47 @@
|
||||||
<template #title>Éditer une entrée</template>
|
<template #title>Éditer une entrée</template>
|
||||||
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<section aria-labelledby="media-title" class="switcher">
|
<section aria-labelledby="media-title" class="switcher container">
|
||||||
<div ref="imageContainer" class="canvas-container"> </div>
|
<div ref="imageContainer" class="canvas-container"> </div>
|
||||||
|
|
||||||
<div class="stack">
|
<div class="stack">
|
||||||
<h3 id="media-title">{{ entryData.original_title }}</h3>
|
<h3 id="media-title">{{ entryData.originalTitle }}</h3>
|
||||||
<p class="center">{{ entryData.release_date }} | {{ entryData.original_language }} </p>
|
<p class="original-metadata">
|
||||||
|
<span v-if="hasUniqueOriginalTitle">{{ entryData.title }} | </span>
|
||||||
|
{{ entryData.releaseDate }} | {{ entryData.originalLanguage }} | {{ entryData.popularity }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<p class="overview">{{ entryData.overview }}</p>
|
<p class="overview">{{ entryData.overview }}</p>
|
||||||
|
|
||||||
|
<h3>Journal</h3>
|
||||||
|
<div class="cluster entry-state">
|
||||||
|
<p>
|
||||||
|
<strong>État : </strong>
|
||||||
|
<span v-if="hasEntry"></span>
|
||||||
|
<span v-else>Pas encore dans le journal.</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button v-if="!hasEntry" :class="{ invert: isFirstTimeEntryEditing }" @click="toggleFirstTimeEditing">
|
||||||
|
{{ firstTimeEditingButtonText }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form v-if="hasEntry || isFirstTimeEntryEditing" class="cluster entry-metadata">
|
||||||
|
<div class="field stack">
|
||||||
|
<label for="date-created">Date de création</label>
|
||||||
|
<input id="date-created" type="datetime-local">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field stack">
|
||||||
|
<label for="date-created">Date de modification</label>
|
||||||
|
<input id="date-created" type="datetime-local">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field stack">
|
||||||
|
<label for="date-created">Date d'obtention</label>
|
||||||
|
<input id="date-created" type="datetime-local">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -72,7 +131,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
inline-size: 85vi;
|
||||||
|
max-inline-size: 74rem;
|
||||||
|
}
|
||||||
|
|
||||||
.overview {
|
.overview {
|
||||||
max-inline-size: 40rem;
|
max-inline-size: 40rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entry-metadata {
|
||||||
|
--layout-cluster-gap: var(--s0);
|
||||||
|
margin-block-start: var(--s2);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
4
src/components/loading/LoadingBox.vue
Normal file
4
src/components/loading/LoadingBox.vue
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<script lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template></template>
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
CREATE TABLE `diary_entries` (
|
CREATE TABLE `diary_entries` (
|
||||||
|
`appreciation` text NOT NULL,
|
||||||
`art_work_id` integer NOT NULL,
|
`art_work_id` integer NOT NULL,
|
||||||
|
`commentary` text,
|
||||||
`date_created` integer NOT NULL,
|
`date_created` integer NOT NULL,
|
||||||
`date_modified` integer NOT NULL,
|
`date_modified` integer NOT NULL,
|
||||||
|
`date_obtained` integer NOT NULL,
|
||||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
`state_id` integer NOT NULL,
|
`state_id` integer NOT NULL,
|
||||||
FOREIGN KEY (`art_work_id`) REFERENCES `art_works`(`id`) ON UPDATE no action ON DELETE no action,
|
FOREIGN KEY (`art_work_id`) REFERENCES `art_works`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
|
@ -59,4 +62,10 @@ CREATE TABLE `media_types` (
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `media_types_name_unique` ON `media_types` (`name`);--> statement-breakpoint
|
CREATE UNIQUE INDEX `media_types_name_unique` ON `media_types` (`name`);--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX `media_types_slug_unique` ON `media_types` (`slug`);
|
CREATE UNIQUE INDEX `media_types_slug_unique` ON `media_types` (`slug`);--> statement-breakpoint
|
||||||
|
CREATE TABLE `posters` (
|
||||||
|
`art_work_id` integer NOT NULL,
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`image` blob NOT NULL,
|
||||||
|
FOREIGN KEY (`art_work_id`) REFERENCES `art_works`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
|
@ -1,12 +1,19 @@
|
||||||
{
|
{
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"id": "8b217318-7662-4f81-bc55-92d5c6ddf2b2",
|
"id": "01d72a44-6bdd-4f2a-b1dd-34546aa1b734",
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"tables": {
|
"tables": {
|
||||||
"diary_entries": {
|
"diary_entries": {
|
||||||
"name": "diary_entries",
|
"name": "diary_entries",
|
||||||
"columns": {
|
"columns": {
|
||||||
|
"appreciation": {
|
||||||
|
"name": "appreciation",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
"art_work_id": {
|
"art_work_id": {
|
||||||
"name": "art_work_id",
|
"name": "art_work_id",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|
@ -14,6 +21,13 @@
|
||||||
"notNull": true,
|
"notNull": true,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
},
|
},
|
||||||
|
"commentary": {
|
||||||
|
"name": "commentary",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
"date_created": {
|
"date_created": {
|
||||||
"name": "date_created",
|
"name": "date_created",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|
@ -28,6 +42,13 @@
|
||||||
"notNull": true,
|
"notNull": true,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
},
|
},
|
||||||
|
"date_obtained": {
|
||||||
|
"name": "date_obtained",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|
@ -404,6 +425,51 @@
|
||||||
"compositePrimaryKeys": {},
|
"compositePrimaryKeys": {},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
"checkConstraints": {}
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"posters": {
|
||||||
|
"name": "posters",
|
||||||
|
"columns": {
|
||||||
|
"art_work_id": {
|
||||||
|
"name": "art_work_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"name": "image",
|
||||||
|
"type": "blob",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"posters_art_work_id_art_works_id_fk": {
|
||||||
|
"name": "posters_art_work_id_art_works_id_fk",
|
||||||
|
"tableFrom": "posters",
|
||||||
|
"tableTo": "art_works",
|
||||||
|
"columnsFrom": [
|
||||||
|
"art_work_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"views": {},
|
"views": {},
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "6",
|
"version": "6",
|
||||||
"when": 1740814587298,
|
"when": 1741093519331,
|
||||||
"tag": "0000_unusual_karen_page",
|
"tag": "0000_goofy_vanisher",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,14 @@ export const DIARY_ENTRY_STATES = {
|
||||||
/** Un média ayant été regardé au mois une fois. */
|
/** Un média ayant été regardé au mois une fois. */
|
||||||
WATCHED: "watched",
|
WATCHED: "watched",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const APPRECIATION_STATES = {
|
||||||
|
/** Oeuvre appréciée. */
|
||||||
|
APPRECIATED: "appreciated",
|
||||||
|
/** Oeuvre non appréciée (détestée). */
|
||||||
|
DISLIKED: "disliked",
|
||||||
|
/** Oeuvre laissant de marbre. */
|
||||||
|
NEUTRAL: "neutral",
|
||||||
|
/** Appréciation inconnue. */
|
||||||
|
UNKNOWN: "unknown",
|
||||||
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,18 @@ import * as t from "drizzle-orm/sqlite-core";
|
||||||
import { sqliteTable as table } from "drizzle-orm/sqlite-core";
|
import { sqliteTable as table } from "drizzle-orm/sqlite-core";
|
||||||
import { Schema } from "effect";
|
import { Schema } from "effect";
|
||||||
|
|
||||||
import { DIARY_ENTRY_STATES } from "./constants";
|
import { APPRECIATION_STATES, DIARY_ENTRY_STATES } from "./constants";
|
||||||
import { ArtWorks, Genres } from "./works";
|
import { ArtWorks, Genres } from "./works";
|
||||||
|
|
||||||
// Tables
|
// Tables
|
||||||
|
|
||||||
export const DiaryEntries = table("diary_entries", {
|
export const DiaryEntries = table("diary_entries", {
|
||||||
|
appreciation: t.text("appreciation").$type<Values<typeof APPRECIATION_STATES>>().notNull(),
|
||||||
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id).notNull(),
|
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id).notNull(),
|
||||||
|
commentary: t.text("commentary").notNull(),
|
||||||
dateCreated: t.integer("date_created", { mode: "timestamp" }).notNull(),
|
dateCreated: t.integer("date_created", { mode: "timestamp" }).notNull(),
|
||||||
dateModified: t.integer("date_modified", { mode: "timestamp" }).notNull(),
|
dateModified: t.integer("date_modified", { mode: "timestamp" }).notNull(),
|
||||||
|
dateObtained: t.integer("date_obtained", { mode: "timestamp" }).notNull(),
|
||||||
id: t.integer("id").primaryKey({ autoIncrement: true }),
|
id: t.integer("id").primaryKey({ autoIncrement: true }),
|
||||||
stateId: t.integer("state_id").references((): AnySQLiteColumn => DiaryEntriesStates.id).notNull(),
|
stateId: t.integer("state_id").references((): AnySQLiteColumn => DiaryEntriesStates.id).notNull(),
|
||||||
});
|
});
|
||||||
|
|
@ -38,9 +41,12 @@ export const Viewings = table("viewings", {
|
||||||
// Schémas
|
// Schémas
|
||||||
|
|
||||||
export const DiaryEntrySchema = Schema.Struct({
|
export const DiaryEntrySchema = Schema.Struct({
|
||||||
|
appreciation: Schema.Enums(APPRECIATION_STATES),
|
||||||
artWorkId: Schema.NonNegativeInt,
|
artWorkId: Schema.NonNegativeInt,
|
||||||
|
commentary: Schema.String.pipe(Schema.optional),
|
||||||
dateCreated: Schema.Number,
|
dateCreated: Schema.Number,
|
||||||
dateModified: Schema.Number,
|
dateModified: Schema.Number,
|
||||||
|
dateObtained: Schema.Number,
|
||||||
id: Schema.NonNegativeInt,
|
id: Schema.NonNegativeInt,
|
||||||
stateId: Schema.NonNegativeInt,
|
stateId: Schema.NonNegativeInt,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ export const MediaTypes = table("media_types", {
|
||||||
slug: t.text("slug").$type<Values<typeof MEDIA_TYPES>>().notNull().unique(),
|
slug: t.text("slug").$type<Values<typeof MEDIA_TYPES>>().notNull().unique(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const Posters = table("posters", {
|
||||||
|
artWorkId: t.integer("art_work_id").references((): AnySQLiteColumn => ArtWorks.id).notNull(),
|
||||||
|
id: t.integer("id").primaryKey({ autoIncrement: true }),
|
||||||
|
image: t.blob("image", { mode: "buffer" }).notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
export const ArtWorks = table("art_works", {
|
export const ArtWorks = table("art_works", {
|
||||||
coverPath: t.text("cover_path").unique(),
|
coverPath: t.text("cover_path").unique(),
|
||||||
dateCreated: t.integer("date_created", { mode: "timestamp" }).notNull(),
|
dateCreated: t.integer("date_created", { mode: "timestamp" }).notNull(),
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export const getTmdbSortFunction = (sortData: TmdbSortData) =>
|
||||||
|
|
||||||
export const byOriginalIndexAscending = Order.mapInput(
|
export const byOriginalIndexAscending = Order.mapInput(
|
||||||
Order.number,
|
Order.number,
|
||||||
(data: [number, MergedTmdbLocalData]) => data[1].original_result_index,
|
(data: [number, MergedTmdbLocalData]) => data[1].originalResultIndex,
|
||||||
);
|
);
|
||||||
export const byPopularityAscending = Order.mapInput(
|
export const byPopularityAscending = Order.mapInput(
|
||||||
Order.number,
|
Order.number,
|
||||||
|
|
@ -55,7 +55,7 @@ export const byPopularityAscending = Order.mapInput(
|
||||||
);
|
);
|
||||||
export const byReleaseDateAscending = Order.mapInput(
|
export const byReleaseDateAscending = Order.mapInput(
|
||||||
Order.string,
|
Order.string,
|
||||||
(data: [number, MergedTmdbLocalData]) => data[1].release_date,
|
(data: [number, MergedTmdbLocalData]) => data[1].releaseDate,
|
||||||
);
|
);
|
||||||
export const byTitleAscending = Order.mapInput(
|
export const byTitleAscending = Order.mapInput(
|
||||||
Order.string,
|
Order.string,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { MEDIA_TYPES } from "@/db/schemas/constants";
|
import { APPRECIATION_STATES, MEDIA_TYPES } from "@/db/schemas/constants";
|
||||||
import { Schema } from "effect";
|
import { Schema } from "effect";
|
||||||
|
|
||||||
export class SearchPageQueryParams extends Schema.Class<SearchPageQueryParams>("SearchPageQueryParams")({
|
export class SearchPageQueryParams extends Schema.Class<SearchPageQueryParams>("SearchPageQueryParams")({
|
||||||
|
|
@ -8,20 +8,24 @@ export class SearchPageQueryParams extends Schema.Class<SearchPageQueryParams>("
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
export class MergedTmdbLocalData extends Schema.Class<MergedTmdbLocalData>("MergedTmdbLocalData")({
|
export class MergedTmdbLocalData extends Schema.Class<MergedTmdbLocalData>("MergedTmdbLocalData")({
|
||||||
artWorkCoverPath: Schema.Union(Schema.String, Schema.Null),
|
|
||||||
artWorkId: Schema.NonNegativeInt.pipe(Schema.optional),
|
artWorkId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||||
artWorkMediumTypeId: Schema.NonNegativeInt.pipe(Schema.optional),
|
artWorkMediumTypeId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||||
|
entryAppreciation: Schema.Enums(APPRECIATION_STATES).pipe(Schema.optional),
|
||||||
|
entryCommentary: Schema.String.pipe(Schema.optional),
|
||||||
entryDateCreated: Schema.Date.pipe(Schema.optional),
|
entryDateCreated: Schema.Date.pipe(Schema.optional),
|
||||||
entryDateModified: Schema.Date.pipe(Schema.optional),
|
entryDateModified: Schema.Date.pipe(Schema.optional),
|
||||||
|
entryDateObtained: Schema.Date.pipe(Schema.optional),
|
||||||
entryId: Schema.NonNegativeInt.pipe(Schema.optional),
|
entryId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||||
entryStateId: Schema.NonNegativeInt.pipe(Schema.optional),
|
entryStateId: Schema.NonNegativeInt.pipe(Schema.optional),
|
||||||
genre_ids: Schema.Array(Schema.NonNegativeInt),
|
genreIds: Schema.Array(Schema.NonNegativeInt),
|
||||||
original_language: Schema.String,
|
originalLanguage: Schema.String,
|
||||||
original_result_index: Schema.Int,
|
originalResultIndex: Schema.Int,
|
||||||
original_title: Schema.String,
|
originalTitle: Schema.String,
|
||||||
overview: Schema.String,
|
overview: Schema.String,
|
||||||
popularity: Schema.Number,
|
popularity: Schema.Number,
|
||||||
release_date: Schema.String,
|
posterBlob: Schema.Unknown.pipe(Schema.optional),
|
||||||
|
posterUrl: Schema.Union(Schema.String, Schema.Null),
|
||||||
|
releaseDate: Schema.String,
|
||||||
title: Schema.String,
|
title: Schema.String,
|
||||||
tmdbId: Schema.NonNegativeInt,
|
tmdbId: Schema.NonNegativeInt,
|
||||||
}) {}
|
}) {}
|
||||||
|
|
|
||||||
|
|
@ -153,20 +153,23 @@
|
||||||
result.id,
|
result.id,
|
||||||
yield* Schema.decodeUnknown(MergedTmdbLocalData)(
|
yield* Schema.decodeUnknown(MergedTmdbLocalData)(
|
||||||
{
|
{
|
||||||
artWorkCoverPath: result.poster_path,
|
|
||||||
artWorkId: artWork?.id,
|
artWorkId: artWork?.id,
|
||||||
artWorkMediumTypeId: artWork?.mediumTypeId,
|
artWorkMediumTypeId: artWork?.mediumTypeId,
|
||||||
|
entryAppreciation: entry?.appreciation,
|
||||||
|
entryCommentary: entry?.commentary,
|
||||||
entryDateCreated: entry?.dateCreated,
|
entryDateCreated: entry?.dateCreated,
|
||||||
entryDateModified: entry?.dateModified,
|
entryDateModified: entry?.dateModified,
|
||||||
|
entryDateObtained: entry?.dateObtained,
|
||||||
entryId: entry?.id,
|
entryId: entry?.id,
|
||||||
entryStateId: entry?.stateId,
|
entryStateId: entry?.stateId,
|
||||||
genre_ids: result.genre_ids,
|
genreIds: result.genre_ids,
|
||||||
original_language: result.original_language,
|
originalLanguage: result.original_language,
|
||||||
original_result_index: index,
|
originalResultIndex: index,
|
||||||
original_title: result.original_title,
|
originalTitle: result.original_title,
|
||||||
overview: result.overview,
|
overview: result.overview,
|
||||||
popularity: result.popularity,
|
popularity: result.popularity,
|
||||||
release_date: result.release_date,
|
posterUrl: result.poster_path,
|
||||||
|
releaseDate: result.release_date,
|
||||||
title: result.title,
|
title: result.title,
|
||||||
tmdbId: result.id,
|
tmdbId: result.id,
|
||||||
} satisfies MergedTmdbLocalData,
|
} satisfies MergedTmdbLocalData,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
import { asInt } from "@thi.ng/color-palettes";
|
import { asInt } from "@thi.ng/color-palettes";
|
||||||
import { ARGB8888, canvasFromPixelBuffer, defIndexed, imageFromURL, intBufferFromImage } from "@thi.ng/pixel";
|
import {
|
||||||
|
ARGB8888,
|
||||||
|
canvasFromPixelBuffer,
|
||||||
|
defIndexed,
|
||||||
|
imageFromURL,
|
||||||
|
IntBuffer,
|
||||||
|
intBufferFromImage,
|
||||||
|
type IntFormat,
|
||||||
|
} from "@thi.ng/pixel";
|
||||||
import { ATKINSON, ditherWith } from "@thi.ng/pixel-dither";
|
import { ATKINSON, ditherWith } from "@thi.ng/pixel-dither";
|
||||||
import { Data, Effect, pipe } from "effect";
|
import { Data, Effect, pipe } from "effect";
|
||||||
|
|
||||||
|
|
@ -10,12 +18,12 @@ export class Images extends Effect.Service<Images>()("Images", {
|
||||||
return {
|
return {
|
||||||
ditherImage: (image: HTMLImageElement, parent?: HTMLElement) =>
|
ditherImage: (image: HTMLImageElement, parent?: HTMLElement) =>
|
||||||
Effect.gen(function*() {
|
Effect.gen(function*() {
|
||||||
const buf = intBufferFromImage(image, ARGB8888).scale(0.8, "cubic");
|
const buf: IntBuffer = intBufferFromImage(image, ARGB8888).scale(0.8, "cubic");
|
||||||
const theme = defIndexed(asInt(["salmon", "black"]));
|
const theme: IntFormat = defIndexed(asInt(["salmon", "black"]));
|
||||||
const ditheredBuf = ditherWith(ATKINSON, buf.copy(), {}).as(theme);
|
const ditheredBuffer: IntBuffer = ditherWith(ATKINSON, buf.copy(), {}).as(theme);
|
||||||
|
|
||||||
const canvas = canvasFromPixelBuffer(ditheredBuf, parent, { pixelated: true });
|
const canvas = canvasFromPixelBuffer(ditheredBuffer, parent, { pixelated: true });
|
||||||
return canvas;
|
return { buffer: ditheredBuffer, canvas: canvas };
|
||||||
}),
|
}),
|
||||||
imageFromUrl: (url: URL) =>
|
imageFromUrl: (url: URL) =>
|
||||||
pipe(
|
pipe(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { SQLocalDrizzle } from "sqlocal/drizzle";
|
import type { SQLocalDrizzle } from "sqlocal/drizzle";
|
||||||
|
|
||||||
import v0000 from "@/db/drizzle/0000_unusual_karen_page.sql?raw";
|
import v0000 from "@/db/drizzle/0000_goofy_vanisher.sql?raw";
|
||||||
import { Data, Effect } from "effect";
|
import { Data, Effect } from "effect";
|
||||||
|
|
||||||
import { LocalSqlite } from "./db";
|
import { LocalSqlite } from "./db";
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,10 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: var(--brkly-font-weight-semibold);
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
--layout-center-max-width: 100%;
|
--layout-center-max-width: 100%;
|
||||||
--layout-center-inline-padding: var(--s1);
|
--layout-center-inline-padding: var(--s1);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,13 @@
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"jsxImportSource": "vue",
|
"jsxImportSource": "vue",
|
||||||
"lib": ["ESNext", "DOM", "DOM.Iterable", "DOM.AsyncIterable", "WebWorker"],
|
"lib": [
|
||||||
|
"ESNext",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable",
|
||||||
|
"DOM.AsyncIterable",
|
||||||
|
"WebWorker"
|
||||||
|
],
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
|
|
@ -32,8 +38,12 @@
|
||||||
"noUncheckedSideEffectImports": false,
|
"noUncheckedSideEffectImports": false,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"paths": { "@/*": ["./src/*"] },
|
"paths": {
|
||||||
"plugins": [{ "name": "@vue/typescript-plugin" }],
|
"@/*": ["./src/*"]
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
{ "name": "@vue/typescript-plugin" }
|
||||||
|
],
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipDefaultLibCheck": true,
|
"skipDefaultLibCheck": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
@ -43,10 +53,20 @@
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"strictPropertyInitialization": true,
|
"strictPropertyInitialization": true,
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
|
"tsBuildInfoFile": ".cache/tsbuildinfo",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"useUnknownInCatchVariables": true,
|
"useUnknownInCatchVariables": true,
|
||||||
"verbatimModuleSyntax": true
|
"verbatimModuleSyntax": true
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist/", ".cache"],
|
"exclude": [
|
||||||
"include": ["src/router/typed-routes.d.ts", "src/env.d.ts", "**/*", "**/*.vue"]
|
".cache",
|
||||||
|
"dist/",
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"**/*",
|
||||||
|
"**/*.vue",
|
||||||
|
"src/vite-env.d.ts",
|
||||||
|
"src/router/typed-routes.d.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue