feat: markdown (#345)
@@ -29,16 +29,16 @@ jobs:
|
||||
uses: immich-app/devtools/actions/use-mise@697a75e2c3186d3c037c2c159855cf2d566542ba # use-mise-action-0.0.1
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
run: pnpm -r install
|
||||
|
||||
- name: Run build
|
||||
run: npm run build
|
||||
run: pnpm build
|
||||
|
||||
- name: Upload build output
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: build-output
|
||||
path: build
|
||||
path: packages/ui/build
|
||||
retention-days: 1
|
||||
|
||||
deploy:
|
||||
|
||||
@@ -23,10 +23,10 @@ jobs:
|
||||
uses: immich-app/devtools/actions/use-mise@697a75e2c3186d3c037c2c159855cf2d566542ba # use-mise-action-0.0.1
|
||||
|
||||
- name: Install
|
||||
run: npm ci
|
||||
run: pnpm -r install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
run: pnpm build
|
||||
|
||||
- name: Publish
|
||||
run: npm publish
|
||||
run: pnpm -r publish --no-git-checks
|
||||
|
||||
@@ -21,14 +21,14 @@ jobs:
|
||||
uses: immich-app/devtools/actions/use-mise@697a75e2c3186d3c037c2c159855cf2d566542ba # use-mise-action-0.0.1
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
run: pnpm -r install
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
run: pnpm -r lint
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run formatter
|
||||
run: npm run format
|
||||
run: pnpm -r format
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run check
|
||||
|
||||
@@ -4,9 +4,9 @@ node_modules
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
/.svelte-kit
|
||||
/build
|
||||
/dist
|
||||
.svelte-kit/
|
||||
build/
|
||||
dist/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
@@ -3,4 +3,6 @@ package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
src/lib/assets
|
||||
dist/
|
||||
CHANGELOG.md
|
||||
.svelte-kit/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{
|
||||
".": "0.39.2"
|
||||
"package/ui": "0.39.2",
|
||||
"package/svelte-markdown-preprocess": "0.0.0"
|
||||
}
|
||||
|
||||
@@ -30,6 +30,6 @@ export default ts.config(
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/'],
|
||||
ignores: ['packages/**/build/', 'packages/**/.svelte-kit/', 'packages/**/dist/'],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,85 +1,46 @@
|
||||
{
|
||||
"name": "@immich/ui",
|
||||
"version": "0.39.2",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"name": "immich-monorepo",
|
||||
"version": "0.0.1",
|
||||
"description": "monorepo for immich and friends",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.19.0",
|
||||
"engines": {
|
||||
"pnpm": ">=10.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/immich-app/ui.git"
|
||||
},
|
||||
"scripts": {
|
||||
"create": "node scripts/create.js",
|
||||
"start": "npm run start:dev",
|
||||
"start:dev": "vite dev",
|
||||
"watch": "vite build --watch",
|
||||
"build": "vite build && npm run package",
|
||||
"preview": "vite preview",
|
||||
"package": "npm run sync && svelte-package && publint",
|
||||
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "npm run sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"build": "pnpm -r --filter=./packages/* build",
|
||||
"lint": "eslint . --max-warnings 0",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"lint:fix": "pnpm lint -- --fix",
|
||||
"format": "prettier --check .",
|
||||
"format:fix": "prettier --write .",
|
||||
"test": "vitest",
|
||||
"sync": "svelte-kit sync"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/*.test.*",
|
||||
"!dist/**/*.spec.*"
|
||||
],
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"svelte": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"svelte": "./dist/index.js"
|
||||
},
|
||||
"./theme/default.css": "./dist/theme/default.css"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.0.0"
|
||||
"check": "pnpm --filter @immich/svelte-markdown-preprocess build && pnpm -r check",
|
||||
"test": "pnpm -r --filter=./packages/* test",
|
||||
"changeset:version": "changeset version && pnpm -r generate:version && git add --all",
|
||||
"changeset:publish": "changeset publish",
|
||||
"bench": "node --allow-natives-syntax ./benchmarking/run.js",
|
||||
"bench:compare": "node --allow-natives-syntax ./benchmarking/compare/index.js",
|
||||
"bench:debug": "node --allow-natives-syntax --inspect-brk ./benchmarking/run.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-static": "^3.0.6",
|
||||
"@sveltejs/kit": "^2.13.0",
|
||||
"@sveltejs/package": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
||||
"@tailwindcss/postcss": "^4.1.7",
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"@types/eslint": "^9.6.0",
|
||||
"@types/luxon": "^3.7.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.7.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-svelte": "^3.9.3",
|
||||
"globals": "^16.0.0",
|
||||
"prettier": "^3.3.2",
|
||||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"prettier-plugin-tailwindcss": "^0.7.0",
|
||||
"publint": "^0.3.0",
|
||||
"svelte": "^5.37.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"typescript": "^5.0.0",
|
||||
"typescript-eslint": "^8.34.1",
|
||||
"vite": "^7.0.0",
|
||||
"vitest": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"svelte-highlight": "^7.8.4",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"bits-ui": "^2.9.8",
|
||||
"luxon": "^3.7.2",
|
||||
"simple-icons": "^15.14.0",
|
||||
"tailwind-merge": "^3.0.0",
|
||||
"tailwind-variants": "^3.0.0"
|
||||
},
|
||||
"volta": {
|
||||
"node": "22.20.0"
|
||||
"@eslint/js": "^9.38.0",
|
||||
"@sveltejs/eslint-config": "^8.3.4",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"acorn": "latest",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.12.5",
|
||||
"globals": "^16.4.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"prettier-plugin-tailwindcss": "^0.7.1",
|
||||
"svelte": "^5.42.3",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"vitest": "^3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
src/
|
||||
tsconfig.json
|
||||
@@ -0,0 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 0.0.0 (2025-10-27)
|
||||
|
||||
Initial commit
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@immich/svelte-markdown-preprocess",
|
||||
"version": "0.0.0",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/immich-app/ui.git",
|
||||
"directory": "packages/svelte-markdown-preprocess"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run start:dev",
|
||||
"build": "tsc --project ./tsconfig.json",
|
||||
"check": "tsc --project ./tsconfig.json --noEmit",
|
||||
"check:watch": "tsc --project ./tsconfig.json --noEmit --watch",
|
||||
"lint": "eslint . --max-warnings 0",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"format": "prettier --check .",
|
||||
"format:fix": "prettier --write ."
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/*.test.*",
|
||||
"!dist/**/*.spec.*"
|
||||
],
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"front-matter": "^4.0.2",
|
||||
"marked": "^16.4.1"
|
||||
},
|
||||
"volta": {
|
||||
"node": "22.20.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import fm from 'front-matter';
|
||||
import type { PreprocessorGroup } from 'svelte/compiler';
|
||||
import { md } from './markdown.js';
|
||||
import { SvelteFile } from './svelte-file.js';
|
||||
|
||||
type Layouts = Record<string, string>;
|
||||
type Options = { layouts?: Layouts };
|
||||
|
||||
const resolveLayout = (layout?: string, layouts?: Layouts) => {
|
||||
if (layout && layouts) {
|
||||
return layouts[layout];
|
||||
}
|
||||
};
|
||||
|
||||
const SCRIPT_BODY_REGEX = /<script.*>(?<body>(.|\n)*?)<\/script>/;
|
||||
|
||||
const parse = (content: string) => {
|
||||
let scriptBody = '';
|
||||
|
||||
const match = SCRIPT_BODY_REGEX.exec(content);
|
||||
const bodyGroup = match?.groups?.body;
|
||||
if (bodyGroup) {
|
||||
scriptBody = bodyGroup.trim();
|
||||
content = content.substring(match.index + match[0].length).trim();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const { attributes, body } = fm(content);
|
||||
|
||||
return { attributes, body, scriptBody };
|
||||
};
|
||||
|
||||
export const svelteMarkdownPreprocess = (options: Options): PreprocessorGroup => {
|
||||
const { layouts } = options || {};
|
||||
|
||||
return {
|
||||
name: 'immich-markdown',
|
||||
async markup({ content, filename }) {
|
||||
if (!filename?.endsWith('.md')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { attributes, body, scriptBody } = parse(content);
|
||||
|
||||
const file = new SvelteFile();
|
||||
file.addScript(`import { Markdown } from '@immich/ui';`);
|
||||
|
||||
const layout = resolveLayout(attributes.layout, layouts);
|
||||
if (layout) {
|
||||
file.addScript(`import Layout from '${layout}';`);
|
||||
file.addTag(`<Layout>`, '</Layout>');
|
||||
}
|
||||
|
||||
file.addScript(scriptBody);
|
||||
file.addTemplate(await md.parse(body));
|
||||
|
||||
return { code: file.export() };
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Marked } from 'marked';
|
||||
|
||||
export const md = new Marked().use({
|
||||
tokenizer: {
|
||||
code(src) {
|
||||
if (src.startsWith(' ')) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
renderer: {
|
||||
heading({ tokens, depth }) {
|
||||
const text = this.parser.parseInline(tokens);
|
||||
let id = text
|
||||
.toLowerCase()
|
||||
.replace(/<[^>]*>/g, '')
|
||||
.replace(/[^a-z1-9 ]/g, '')
|
||||
.replace(/\s+/g, '-');
|
||||
|
||||
if (id.endsWith('-')) {
|
||||
id = id.slice(0, -1);
|
||||
}
|
||||
|
||||
switch (depth) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6: {
|
||||
return `<Markdown.Heading id="${id}" level={${depth}}>${text}</Markdown.Heading>\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return `<Markdown.Paragraph>${text}</Markdown.Paragraph>\n`;
|
||||
},
|
||||
|
||||
list({ items, ordered, start }) {
|
||||
const startAttribute = start && start !== 1 ? ` start={${start}}` : '';
|
||||
let output = '';
|
||||
output += `<Markdown.List ordered={${ordered}}${startAttribute}>\n`;
|
||||
for (const item of items) {
|
||||
const content = this.parser.parse(item.tokens);
|
||||
const attributes = [item.task && 'task', item.checked && 'checked'].filter(Boolean);
|
||||
output += ` <Markdown.ListItem ${attributes.join(' ')}>${content}</Markdown.ListItem>`;
|
||||
}
|
||||
output += `</Markdown.List>\n`;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
blockquote({ tokens }) {
|
||||
const children = this.parser.parse(tokens);
|
||||
return `<Markdown.BlockQuote>${children}</Markdown.BlockQuote>\n`;
|
||||
},
|
||||
|
||||
paragraph({ tokens }) {
|
||||
const children = this.parser.parseInline(tokens);
|
||||
return `<Markdown.Paragraph>${children}</Markdown.Paragraph>\n`;
|
||||
},
|
||||
|
||||
code({ text, lang }) {
|
||||
return `<Markdown.Code lang="${lang}" code={\`${text}\`} multiline />\n`;
|
||||
},
|
||||
|
||||
codespan({ text }) {
|
||||
return `<Markdown.Code code={\`${text}\`} />\n`;
|
||||
},
|
||||
|
||||
hr() {
|
||||
return `<Markdown.LineBreak />\n`;
|
||||
},
|
||||
|
||||
link({ href, title, text }) {
|
||||
return `<Markdown.Link href="${href}" title="${title ?? ''}">${text}</Markdown.Link>\n`;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
export class SvelteFile {
|
||||
private scripts: string[] = [];
|
||||
private lines: string[] = [];
|
||||
private closingTags: string[] = [];
|
||||
|
||||
addScript(line: string) {
|
||||
if (line) {
|
||||
this.scripts.push(line);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
addTemplate(line: string) {
|
||||
this.lines.push(line);
|
||||
return this;
|
||||
}
|
||||
|
||||
addTag(tag: string, closingTag: string) {
|
||||
this.lines.push(tag);
|
||||
this.closingTags.push(closingTag);
|
||||
}
|
||||
|
||||
export() {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push(`<script lang="ts">`);
|
||||
for (const line of this.scripts) {
|
||||
lines.push(' ' + line);
|
||||
}
|
||||
lines.push('</script>');
|
||||
|
||||
for (const line of this.lines) {
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
for (const line of this.closingTags.toReversed()) {
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": false,
|
||||
"strict": true,
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"outDir": "./dist"
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,10 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🌟 Enhancements
|
||||
* feat(toast): default titles by @jrasm91 in https://github.com/immich-app/ui/pull/336
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
- feat(toast): default titles by @jrasm91 in https://github.com/immich-app/ui/pull/336
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.38.0...v0.39.0
|
||||
|
||||
@@ -35,11 +36,14 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🚀 Features
|
||||
* feat: toast by @jrasm91 in https://github.com/immich-app/ui/pull/331
|
||||
### 🐛 Bug fixes
|
||||
* fix: close button color by @jrasm91 in https://github.com/immich-app/ui/pull/335
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- feat: toast by @jrasm91 in https://github.com/immich-app/ui/pull/331
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- fix: close button color by @jrasm91 in https://github.com/immich-app/ui/pull/335
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.37.1...v0.38.0
|
||||
|
||||
@@ -48,9 +52,10 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🐛 Bug fixes
|
||||
* fix: map empty string to undefined in number inputs by @jrasm91 in https://github.com/immich-app/ui/pull/330
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- fix: map empty string to undefined in number inputs by @jrasm91 in https://github.com/immich-app/ui/pull/330
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.37.0...v0.37.1
|
||||
|
||||
@@ -59,12 +64,15 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🚀 Features
|
||||
* feat: number input by @jrasm91 in https://github.com/immich-app/ui/pull/323
|
||||
### 🌟 Enhancements
|
||||
* feat: auto-expire announcements by @jrasm91 in https://github.com/immich-app/ui/pull/322
|
||||
* chore: tweak announcement banner background by @alextran1502 in https://github.com/immich-app/ui/pull/324
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- feat: number input by @jrasm91 in https://github.com/immich-app/ui/pull/323
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
- feat: auto-expire announcements by @jrasm91 in https://github.com/immich-app/ui/pull/322
|
||||
- chore: tweak announcement banner background by @alextran1502 in https://github.com/immich-app/ui/pull/324
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.36.4...v0.37.0
|
||||
|
||||
@@ -73,9 +81,10 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🐛 Bug fixes
|
||||
* fix: npm version by @jrasm91 in https://github.com/immich-app/ui/pull/318
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- fix: npm version by @jrasm91 in https://github.com/immich-app/ui/pull/318
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.36.3...v0.36.4
|
||||
|
||||
@@ -84,9 +93,10 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🐛 Bug fixes
|
||||
* fix: publish workflow by @jrasm91 in https://github.com/immich-app/ui/pull/316
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- fix: publish workflow by @jrasm91 in https://github.com/immich-app/ui/pull/316
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.36.2...v0.36.3
|
||||
|
||||
@@ -94,8 +104,6 @@
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.36.1...v0.36.2
|
||||
|
||||
## 0.36.1 (2025-10-16)
|
||||
@@ -103,11 +111,14 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
|
||||
### 🌟 Enhancements
|
||||
* chore: npm trusted publisher by @jrasm91 in https://github.com/immich-app/ui/pull/312
|
||||
|
||||
- chore: npm trusted publisher by @jrasm91 in https://github.com/immich-app/ui/pull/312
|
||||
|
||||
## New Contributors
|
||||
* @sacrosanctic made their first contribution in https://github.com/immich-app/ui/pull/311
|
||||
|
||||
- @sacrosanctic made their first contribution in https://github.com/immich-app/ui/pull/311
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.36.0...v0.36.1
|
||||
|
||||
@@ -116,15 +127,20 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🚀 Features
|
||||
* feat: announcement bar by @jrasm91 in https://github.com/immich-app/ui/pull/306
|
||||
### 🌟 Enhancements
|
||||
* chore: footer style tweak by @alextran1502 in https://github.com/immich-app/ui/pull/304
|
||||
* fix: simpler footer by @jrasm91 in https://github.com/immich-app/ui/pull/309
|
||||
### 🐛 Bug fixes
|
||||
* fix: z-index by @jrasm91 in https://github.com/immich-app/ui/pull/307
|
||||
* fix: site-footer by @jrasm91 in https://github.com/immich-app/ui/pull/308
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- feat: announcement bar by @jrasm91 in https://github.com/immich-app/ui/pull/306
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
- chore: footer style tweak by @alextran1502 in https://github.com/immich-app/ui/pull/304
|
||||
- fix: simpler footer by @jrasm91 in https://github.com/immich-app/ui/pull/309
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- fix: z-index by @jrasm91 in https://github.com/immich-app/ui/pull/307
|
||||
- fix: site-footer by @jrasm91 in https://github.com/immich-app/ui/pull/308
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.35.0...v0.36.0
|
||||
|
||||
@@ -133,9 +149,10 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🚀 Features
|
||||
* feat: expose openCount on ModalManager by @midzelis in https://github.com/immich-app/ui/pull/301
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- feat: expose openCount on ModalManager by @midzelis in https://github.com/immich-app/ui/pull/301
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.34.2...v0.35.0
|
||||
|
||||
@@ -144,9 +161,10 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🐛 Bug fixes
|
||||
* fix: move svelte-hightlight from dev to actual dependency by @midzelis in https://github.com/immich-app/ui/pull/299
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- fix: move svelte-hightlight from dev to actual dependency by @midzelis in https://github.com/immich-app/ui/pull/299
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.34.1...v0.34.2
|
||||
|
||||
@@ -154,9 +172,9 @@
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
|
||||
## New Contributors
|
||||
* @midzelis made their first contribution in https://github.com/immich-app/ui/pull/297
|
||||
|
||||
- @midzelis made their first contribution in https://github.com/immich-app/ui/pull/297
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.34.0...v0.34.1
|
||||
|
||||
@@ -165,25 +183,26 @@
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🌟 Enhancements
|
||||
* feat: site metadata article by @jrasm91 in https://github.com/immich-app/ui/pull/296
|
||||
### 🐛 Bug fixes
|
||||
* fix: site metadata by @jrasm91 in https://github.com/immich-app/ui/pull/295
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
- feat: site metadata article by @jrasm91 in https://github.com/immich-app/ui/pull/296
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- fix: site metadata by @jrasm91 in https://github.com/immich-app/ui/pull/295
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/v0.33.0...v0.34.0
|
||||
|
||||
## [0.33.0](https://github.com/immich-app/ui/compare/v0.32.0...v0.33.0) (2025-09-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* default release type ([#291](https://github.com/immich-app/ui/issues/291)) ([52d8d7e](https://github.com/immich-app/ui/commit/52d8d7e24e092ba44ed4557204dcbfe8fe6175c0))
|
||||
* prerender ([#288](https://github.com/immich-app/ui/issues/288)) ([cc85216](https://github.com/immich-app/ui/commit/cc8521625dad0eb2c07204f8730f4d4dad477abb))
|
||||
* release tag name ([9bac06b](https://github.com/immich-app/ui/commit/9bac06b4fb5e238c09a9a42f3ee5bd7511c03b5f))
|
||||
|
||||
- default release type ([#291](https://github.com/immich-app/ui/issues/291)) ([52d8d7e](https://github.com/immich-app/ui/commit/52d8d7e24e092ba44ed4557204dcbfe8fe6175c0))
|
||||
- prerender ([#288](https://github.com/immich-app/ui/issues/288)) ([cc85216](https://github.com/immich-app/ui/commit/cc8521625dad0eb2c07204f8730f4d4dad477abb))
|
||||
- release tag name ([9bac06b](https://github.com/immich-app/ui/commit/9bac06b4fb5e238c09a9a42f3ee5bd7511c03b5f))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* release tag name ([#292](https://github.com/immich-app/ui/issues/292)) ([9bac06b](https://github.com/immich-app/ui/commit/9bac06b4fb5e238c09a9a42f3ee5bd7511c03b5f))
|
||||
* set bootstrap-sha for release-please ([#286](https://github.com/immich-app/ui/issues/286)) ([8536088](https://github.com/immich-app/ui/commit/8536088e8bae2f1ceb5daa86f9bf6d436371ac7c))
|
||||
- release tag name ([#292](https://github.com/immich-app/ui/issues/292)) ([9bac06b](https://github.com/immich-app/ui/commit/9bac06b4fb5e238c09a9a42f3ee5bd7511c03b5f))
|
||||
- set bootstrap-sha for release-please ([#286](https://github.com/immich-app/ui/issues/286)) ([8536088](https://github.com/immich-app/ui/commit/8536088e8bae2f1ceb5daa86f9bf6d436371ac7c))
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "@immich/ui",
|
||||
"version": "0.39.2",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/immich-app/ui.git",
|
||||
"directory": "packages/ui"
|
||||
},
|
||||
"scripts": {
|
||||
"create": "node scripts/create.js",
|
||||
"start": "npm run start:dev",
|
||||
"start:dev": "vite dev",
|
||||
"watch": "vite build --watch",
|
||||
"build": "vite build && pnpm package",
|
||||
"preview": "vite preview",
|
||||
"package": "npm run sync && svelte-package && publint",
|
||||
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "npm run sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"test": "vitest",
|
||||
"sync": "svelte-kit sync"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/*.test.*",
|
||||
"!dist/**/*.spec.*"
|
||||
],
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"svelte": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"svelte": "./dist/index.js"
|
||||
},
|
||||
"./theme/default.css": "./dist/theme/default.css"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-static": "^3.0.6",
|
||||
"@sveltejs/kit": "^2.13.0",
|
||||
"@sveltejs/package": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
||||
"@tailwindcss/postcss": "^4.1.7",
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"@types/luxon": "^3.7.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"globals": "^16.0.0",
|
||||
"publint": "^0.3.0",
|
||||
"svelte": "^5.37.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^7.0.0",
|
||||
"vitest": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@immich/svelte-markdown-preprocess": "workspace:^",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"bits-ui": "^2.9.8",
|
||||
"luxon": "^3.7.2",
|
||||
"simple-icons": "^15.14.0",
|
||||
"svelte-highlight": "^7.8.4",
|
||||
"tailwind-merge": "^3.0.0",
|
||||
"tailwind-variants": "^3.0.0"
|
||||
},
|
||||
"volta": {
|
||||
"node": "22.20.0"
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
mdiImage,
|
||||
mdiImageOutline,
|
||||
mdiKeyboardVariant,
|
||||
mdiLanguageMarkdownOutline,
|
||||
mdiLink,
|
||||
mdiListBox,
|
||||
mdiListBoxOutline,
|
||||
@@ -128,8 +129,9 @@ export const componentGroups = [
|
||||
title: 'Miscellaneous',
|
||||
components: [
|
||||
{ name: 'Avatar', icon: mdiAccountCircleOutline, activeIcon: mdiAccountCircle },
|
||||
{ name: 'Logo', icon: mdiImageOutline, activeIcon: mdiImage },
|
||||
{ name: 'CommandPalette', icon: mdiMenu, activeIcon: mdiMenu },
|
||||
{ name: 'Logo', icon: mdiImageOutline, activeIcon: mdiImage },
|
||||
{ name: 'Markdown', icon: mdiLanguageMarkdownOutline, activeIcon: mdiLanguageMarkdownOutline },
|
||||
{ name: 'SupporterBadge', icon: mdiPartyPopper },
|
||||
{ name: 'ThemeSwitcher', icon: mdiThemeLightDark },
|
||||
],
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import Text from '$lib/components/Text/Text.svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
children: Snippet;
|
||||
};
|
||||
|
||||
const { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Text {children} class="border-muted border-s-4 ps-4" variant="italic" />
|
||||
@@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import Code from '$lib/components/Code/Code.svelte';
|
||||
import CodeBlock from '$lib/components/CodeBlock/CodeBlock.svelte';
|
||||
import * as languages from 'svelte-highlight/languages';
|
||||
|
||||
type Props = {
|
||||
code: string;
|
||||
multiline?: boolean;
|
||||
lang?: string;
|
||||
};
|
||||
|
||||
const { code, lang, multiline }: Props = $props();
|
||||
|
||||
const getLanguage = (lang: string | undefined) => {
|
||||
switch (lang) {
|
||||
case 'js': {
|
||||
lang = 'javascript';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ts': {
|
||||
lang = 'typescript';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang) {
|
||||
return languages[lang as keyof typeof languages];
|
||||
}
|
||||
};
|
||||
|
||||
let language = $derived(getLanguage(lang) ?? languages.plaintext);
|
||||
</script>
|
||||
|
||||
{#if multiline}
|
||||
<div class="mt-2 mb-2">
|
||||
<CodeBlock {code} {language} />
|
||||
</div>
|
||||
{:else}
|
||||
<Code>{code}</Code>
|
||||
{/if}
|
||||
@@ -0,0 +1,45 @@
|
||||
<script lang="ts">
|
||||
import Heading from '$lib/components/Heading/Heading.svelte';
|
||||
import type { HeadingTag, Size } from '$lib/types.js';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
id?: string;
|
||||
level?: number;
|
||||
children: Snippet;
|
||||
};
|
||||
|
||||
const { children, id, level }: Props = $props();
|
||||
|
||||
const getSizeAndTag = (level?: number): { size: Size; tag: HeadingTag } => {
|
||||
switch (level) {
|
||||
case 1: {
|
||||
return { size: 'giant', tag: 'h1' };
|
||||
}
|
||||
case 2: {
|
||||
return { size: 'large', tag: 'h2' };
|
||||
}
|
||||
case 3: {
|
||||
return { size: 'medium', tag: 'h3' };
|
||||
}
|
||||
case 4: {
|
||||
return { size: 'small', tag: 'h4' };
|
||||
}
|
||||
case 5: {
|
||||
return { size: 'tiny', tag: 'h5' };
|
||||
}
|
||||
case 6: {
|
||||
return { size: 'tiny', tag: 'h6' };
|
||||
}
|
||||
default: {
|
||||
return { size: 'tiny', tag: 'p' };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let { size, tag } = $derived(getSizeAndTag(level));
|
||||
</script>
|
||||
|
||||
<Heading {size} {tag} class="mt-4" {id}>
|
||||
{@render children()}
|
||||
</Heading>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { cleanClass } from '$lib/utilities/internal.js';
|
||||
|
||||
type Props = {
|
||||
class?: string;
|
||||
};
|
||||
|
||||
const { class: className }: Props = $props();
|
||||
</script>
|
||||
|
||||
<hr class={cleanClass('my-6 border-t', className)} />
|
||||
@@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import Link from '$lib/components/Link/Link.svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
href: string;
|
||||
title?: string;
|
||||
children?: Snippet;
|
||||
};
|
||||
|
||||
const { href, title, children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Link {href} {title} {children} />
|
||||
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
ordered?: boolean;
|
||||
children: Snippet;
|
||||
};
|
||||
|
||||
const { children, ordered }: Props = $props();
|
||||
const styles = 'list-outside ps-6 w-full flex flex-col gap-1';
|
||||
</script>
|
||||
|
||||
{#if ordered}
|
||||
<ol class="list-decimal {styles}">
|
||||
{@render children?.()}
|
||||
</ol>
|
||||
{:else}
|
||||
<ul class="list-disc {styles}">
|
||||
{@render children?.()}
|
||||
</ul>
|
||||
{/if}
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import Checkbox from '$lib/components/Checkbox/Checkbox.svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
checked?: boolean;
|
||||
task?: boolean;
|
||||
children: Snippet;
|
||||
};
|
||||
|
||||
const { children, checked, task }: Props = $props();
|
||||
</script>
|
||||
|
||||
<li class:list-none={task}>
|
||||
{#if task}
|
||||
<Checkbox {checked} readonly size="tiny" color="secondary" />
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
</li>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { Text } from '@immich/ui';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
children: Snippet;
|
||||
};
|
||||
|
||||
const { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Text class="mb-1">
|
||||
{@render children()}
|
||||
</Text>
|
||||
@@ -0,0 +1,19 @@
|
||||
import BlockQuote from './BlockQuote.svelte';
|
||||
import Code from './Code.svelte';
|
||||
import Heading from './Heading.svelte';
|
||||
import LineBreak from './LineBreak.svelte';
|
||||
import Link from './Link.svelte';
|
||||
import List from './List.svelte';
|
||||
import ListItem from './ListItem.svelte';
|
||||
import Paragraph from './Paragraph.svelte';
|
||||
|
||||
export const Markdown = {
|
||||
Link,
|
||||
BlockQuote,
|
||||
Code,
|
||||
Heading,
|
||||
LineBreak,
|
||||
ListItem,
|
||||
List,
|
||||
Paragraph,
|
||||
};
|
||||