Skip to content

Commit 8890939

Browse files
goniszewskiSparkensteingpoussel
authored
Release v0.5.0 (#168)
* chore: release v0.4.1-hotfix.3 * fix(data-migration): early return if no categories have parents (#128) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * Closes #130 (#131) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(database): use dynamic path for SQLite database file Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * docs(readme): use single README file for latest/preview version Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(ci): add manual deployment workflow and adjust tag conditions Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(workflow): simplify manual-deploy GitHub Action Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(metadata): handle multiple image URLs in mainImageUrl field Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix: auth error handling (#144) * refactor(api): migrate Swagger UI to external documentation and enhance health endpoint Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * chore: release v0.4.3 * fix(docker): resolve issue with data directory permissions (#150) * Fix issue #153: creation/update of root categories (#157) * chore: release v0.4.3 (#149) * chore: release v0.4.1-hotfix.3 * fix(data-migration): early return if no categories have parents (#128) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * Closes #130 (#131) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(database): use dynamic path for SQLite database file Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * docs(readme): use single README file for latest/preview version Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(ci): add manual deployment workflow and adjust tag conditions Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(workflow): simplify manual-deploy GitHub Action Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(metadata): handle multiple image URLs in mainImageUrl field Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix: auth error handling (#144) * refactor(api): migrate Swagger UI to external documentation and enhance health endpoint Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * chore: release v0.4.3 --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> * Fix creation/update of root categories --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Robert Goniszewski <43510122+goniszewski@users.noreply.github.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> * Fix issue #145: bookmarks without images (#156) * chore: release v0.4.3 (#149) * chore: release v0.4.1-hotfix.3 * fix(data-migration): early return if no categories have parents (#128) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * Closes #130 (#131) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(database): use dynamic path for SQLite database file Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * docs(readme): use single README file for latest/preview version Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(ci): add manual deployment workflow and adjust tag conditions Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(workflow): simplify manual-deploy GitHub Action Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(metadata): handle multiple image URLs in mainImageUrl field Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix: auth error handling (#144) * refactor(api): migrate Swagger UI to external documentation and enhance health endpoint Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * chore: release v0.4.3 --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> * Fix bookmark creation/update without image --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Robert Goniszewski <43510122+goniszewski@users.noreply.github.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> * chore: release v0.4.4 * Feat: import bookmarks (#139) * feat(import): add Netscape bookmark import function Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(components): add bulk list management components Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(bulk-list): add metadata loading states and indicators Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(bulk-list): centralize bookmark management with dedicated store Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(import): refactor bookmark import to handle file content directly Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor: improve URL display and pagination handling Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(import): add WIP bookmark import page with bulk processing capabilities Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(components): add custom Select component and improve bulk list editing Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(import): restructure bookmark import flow and add edit capabilities Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(bookmarks): improve metadata handling and type definitions Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(import): implement WIP bookmark import execution with metadata support Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(import): update HTML bookmark import flow Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(import): unable to create DB entities from import data Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix: add missing preventDefault to action buttons Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(import): resolve issues with updating to-be-imported bookmarks * feat(import): finalize basic bookmark import flow Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(import): add styling, handle errors Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(import): add batch processing, transactions, storing images, display errors Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * chore: release v0.5.0-pre.1 * Resolves #133 (#162) * chore: release v0.4.4 (#158) * chore: release v0.4.1-hotfix.3 * fix(data-migration): early return if no categories have parents (#128) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * Closes #130 (#131) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(database): use dynamic path for SQLite database file Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * docs(readme): use single README file for latest/preview version Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(ci): add manual deployment workflow and adjust tag conditions Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(workflow): simplify manual-deploy GitHub Action Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(metadata): handle multiple image URLs in mainImageUrl field Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix: auth error handling (#144) * refactor(api): migrate Swagger UI to external documentation and enhance health endpoint Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * chore: release v0.4.3 * fix(docker): resolve issue with data directory permissions (#150) * Fix issue #153: creation/update of root categories (#157) * chore: release v0.4.3 (#149) * chore: release v0.4.1-hotfix.3 * fix(data-migration): early return if no categories have parents (#128) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * Closes #130 (#131) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(database): use dynamic path for SQLite database file Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * docs(readme): use single README file for latest/preview version Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(ci): add manual deployment workflow and adjust tag conditions Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(workflow): simplify manual-deploy GitHub Action Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(metadata): handle multiple image URLs in mainImageUrl field Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix: auth error handling (#144) * refactor(api): migrate Swagger UI to external documentation and enhance health endpoint Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * chore: release v0.4.3 --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> * Fix creation/update of root categories --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Robert Goniszewski <43510122+goniszewski@users.noreply.github.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> * Fix issue #145: bookmarks without images (#156) * chore: release v0.4.3 (#149) * chore: release v0.4.1-hotfix.3 * fix(data-migration): early return if no categories have parents (#128) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * Closes #130 (#131) Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(database): use dynamic path for SQLite database file Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * docs(readme): use single README file for latest/preview version Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * feat(ci): add manual deployment workflow and adjust tag conditions Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * refactor(workflow): simplify manual-deploy GitHub Action Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(metadata): handle multiple image URLs in mainImageUrl field Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix: auth error handling (#144) * refactor(api): migrate Swagger UI to external documentation and enhance health endpoint Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * chore: release v0.4.3 --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> * Fix bookmark creation/update without image --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Robert Goniszewski <43510122+goniszewski@users.noreply.github.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> * chore: release v0.4.4 --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> Co-authored-by: Guillaume Poussel <gpoussel@users.noreply.github.com> * feat(docker): implement s6-overlay Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(docker): use proper tag in compose Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> Co-authored-by: Guillaume Poussel <gpoussel@users.noreply.github.com> * chore: release v0.5.0-pre.2 * fix(build): resolve issue with lodash causing build fail Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(Dockerfile): remove grimoire user causing problems, limit layers count, simplify Dockerfile Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * fix(Dockerfile): ensure all packages are in sync Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> * chore: release v0.5.0 (#167) --------- Signed-off-by: Robert Goniszewski <robertgoniszewski@outlook.com> Co-authored-by: Prabhanjan <zetabytes.pp@gmail.com> Co-authored-by: Guillaume Poussel <gpoussel@users.noreply.github.com>
1 parent df7971a commit 8890939

30 files changed

+1377
-132
lines changed

Dockerfile

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,47 @@
1-
FROM oven/bun AS base
1+
FROM oven/bun AS builder
22
LABEL maintainer="Grimoire Developers <contact@grimoire.pro>"
33
LABEL description="Bookmark manager for the wizards"
44
LABEL org.opencontainers.image.source="https://github.com/goniszewski/grimoire"
55

6-
RUN apt-get update && apt-get install -y python3 python3-pip wget build-essential && \
6+
RUN apt-get update && \
7+
apt-get upgrade -y && \
8+
apt-get install -y --no-install-recommends \
9+
xz-utils python3 python3-pip wget build-essential && \
10+
dpkg --configure -a && \
711
rm -rf /var/lib/apt/lists/* && \
8-
bun i -g svelte-kit@latest
12+
mkdir -p /etc/s6-overlay/s6-rc.d/grimoire /etc/s6-overlay/s6-rc.d/user/contents.d
13+
14+
RUN mkdir -p /app/data
15+
16+
ARG S6_OVERLAY_VERSION=3.1.6.2
17+
ARG TARGETARCH=x86_64
18+
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
19+
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${TARGETARCH}.tar.xz /tmp
20+
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz && \
21+
tar -C / -Jxpf /tmp/s6-overlay-${TARGETARCH}.tar.xz && \
22+
rm /tmp/s6-overlay-*xz
23+
24+
COPY docker/etc/s6-overlay /etc/s6-overlay/
25+
RUN chmod +x /etc/s6-overlay/s6-rc.d/grimoire/run
26+
27+
ENV S6_KEEP_ENV=1 \
28+
S6_SERVICES_GRACETIME=15000 \
29+
S6_KILL_GRACETIME=10000 \
30+
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
31+
S6_SYNC_DISKS=1
32+
33+
RUN bun i -g svelte-kit@latest
934

1035
RUN adduser --disabled-password --gecos '' grimoire
1136
RUN mkdir -p /app/data && chown -R grimoire:grimoire /app/data && chmod 766 /app/data
1237
WORKDIR /app
1338

14-
FROM base AS dependencies
39+
FROM builder AS dependencies
1540
COPY package.json bun.lockb ./
16-
RUN bun install --frozen-lockfile
17-
RUN bun install --frozen-lockfile --production
41+
RUN bun install --frozen-lockfile && \
42+
bun install --frozen-lockfile --production
1843

19-
FROM base AS build
44+
FROM builder AS build
2045
COPY --from=dependencies /app/node_modules ./node_modules
2146
COPY . .
2247
RUN bun run svelte-kit sync
@@ -29,7 +54,8 @@ ENV NODE_ENV=production \
2954
NODE_OPTIONS="--max-old-space-size=4096"
3055
RUN bun --bun run build
3156

32-
FROM base AS release
57+
FROM builder AS release
58+
3359
COPY --from=dependencies /app/node_modules ./node_modules
3460
COPY --from=build /app/build ./build
3561
COPY --from=build /app/migrations ./migrations

bun.lockb

392 Bytes
Binary file not shown.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/command/with-contenv bash
2+
3+
cd /app
4+
5+
# Run migrations first
6+
/usr/local/bin/bun run run-migrations
7+
8+
# Start the application
9+
exec /usr/local/bin/bun ./build/index.js
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
longrun
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

run-dev.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
trap 'echo "Received SIGINT or SIGTERM. Exiting..." >&2; exit 1' SIGINT SIGTERM
44

5+
bun --bun run run-migrations
56
bun --bun run dev
67

78
kill -- -$$

src/lib/components/AddBookmarkForm/AddBookmarkForm.svelte

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { addBookmarkToSearchIndex } from '$lib/utils/search';
1111
import { showToast } from '$lib/utils/show-toast';
1212
1313
import { IconInfoCircle } from '@tabler/icons-svelte';
14-
import _ from 'lodash';
14+
import { debounce } from 'es-toolkit';
1515
import { onDestroy } from 'svelte';
1616
import Select from 'svelte-select';
1717
import { writable, type Writable } from 'svelte/store';
@@ -112,7 +112,7 @@ let error = '';
112112
let warning = '';
113113
const loading = writable(false);
114114
115-
const onGetMetadata = _.debounce(
115+
const onGetMetadata = debounce(
116116
async (event: Event) => {
117117
const target = event.target as HTMLButtonElement;
118118
const url = target.value;
@@ -188,9 +188,8 @@ const onGetMetadata = _.debounce(
188188
},
189189
1000,
190190
{
191-
leading: false,
192-
trailing: true,
193-
maxWait: 1000
191+
signal: AbortSignal.timeout(5000),
192+
edges: ['trailing']
194193
}
195194
);
196195
</script>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<script lang="ts">
2+
import { importBookmarkStore } from '$lib/stores/import-bookmarks.store';
3+
import { type Readable } from 'svelte/store';
4+
import BulkListItem from '../BulkListItem/BulkListItem.svelte';
5+
import type { BookmarkEdit } from '$lib/types/Bookmark.type';
6+
7+
export let itemList: Readable<BookmarkEdit[]>;
8+
export let isLoading: boolean;
9+
10+
const selectAllItems = ({ target }: Event) => {
11+
if (target instanceof HTMLInputElement) {
12+
importBookmarkStore.setSelectStatusForAll(target.checked);
13+
}
14+
};
15+
</script>
16+
17+
<div class="flex flex-col gap-2">
18+
<div class="max-h-[calc(100vh-16rem)] overflow-x-auto">
19+
<table class="table table-pin-rows table-pin-cols table-xs">
20+
<!-- head -->
21+
<thead>
22+
<tr>
23+
<th>
24+
<label>
25+
<input type="checkbox" class="checkbox" on:change={selectAllItems} />
26+
</label>
27+
</th>
28+
<th>URL</th>
29+
<th>Title</th>
30+
<th>Category</th>
31+
<th></th>
32+
</tr>
33+
</thead>
34+
<tbody>
35+
{#each $itemList as item (item.id)}
36+
<BulkListItem
37+
id={item.id}
38+
icon={item.icon}
39+
url={item.url}
40+
title={item.title}
41+
category={item.category}
42+
selected={item.selected}
43+
isLoading={isLoading}
44+
metadataFetched={!!item.domain}
45+
metadata={item} />
46+
{/each}
47+
</tbody>
48+
<!-- foot -->
49+
<tfoot>
50+
<tr>
51+
<th></th>
52+
<th>URL</th>
53+
<th>Title</th>
54+
<th>Category</th>
55+
<th></th>
56+
</tr>
57+
</tfoot>
58+
</table>
59+
</div>
60+
</div>
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<script lang="ts">
2+
import { editBookmarkStore } from '$lib/stores/edit-bookmark.store';
3+
import { importBookmarkStore } from '$lib/stores/import-bookmarks.store';
4+
import { createSlug } from '$lib/utils/create-slug';
5+
import {
6+
IconCircleDashedCheck,
7+
IconExclamationCircle,
8+
IconPhotoX,
9+
IconStopwatch
10+
} from '@tabler/icons-svelte';
11+
12+
export let id: number;
13+
export let selected = false;
14+
export let icon: string | null;
15+
export let url: string;
16+
export let title: string;
17+
export let category: {
18+
id?: number;
19+
name: string;
20+
};
21+
export let isLoading: boolean;
22+
export let metadataFetched: boolean;
23+
export let metadata: any;
24+
25+
let urlObj = new URL(url);
26+
27+
const onEditItem = () => {
28+
editBookmarkStore.set({
29+
...metadata,
30+
category: {
31+
id: category.id,
32+
name: category.name
33+
}
34+
});
35+
};
36+
37+
const onRemoveItem = () => {
38+
importBookmarkStore.removeItem(id);
39+
};
40+
</script>
41+
42+
<tr>
43+
<th>
44+
<label>
45+
<input
46+
type="checkbox"
47+
class="checkbox"
48+
bind:checked={selected}
49+
on:change={() => importBookmarkStore.toggleSelectionForItem(id)} />
50+
</label>
51+
</th>
52+
<td>
53+
<div class="flex flex-col gap-1">
54+
<div class="flex items-center gap-3">
55+
<div class="avatar">
56+
<div class="mask mask-squircle h-12 w-12">
57+
{#if icon}
58+
<img src={icon} alt="Icon" />
59+
{:else}
60+
<IconPhotoX class="m-2 h-8 w-8 opacity-80" />
61+
{/if}
62+
</div>
63+
</div>
64+
<div class="max-w-lg">
65+
<div class="tooltip" data-tip={url}>
66+
<a href={url} target="_blank" class="font-bold">
67+
{urlObj.pathname !== '/'
68+
? `${urlObj.hostname}/.../${urlObj.pathname.slice(-5)}`
69+
: urlObj.hostname}
70+
</a>
71+
</div>
72+
<div class="flex items-center gap-1 text-sm tracking-tight text-secondary">
73+
{new URL(url).hostname.replace(/^www\./, '')}
74+
{#if metadataFetched}
75+
<div class="tooltip" data-tip="Metadata fetched">
76+
<IconCircleDashedCheck class="h-4 w-4 text-success" />
77+
</div>
78+
{:else if isLoading}
79+
<div class="tooltip" data-tip="Loading metadata">
80+
<IconStopwatch class="h-4 w-4 text-warning" />
81+
</div>
82+
{:else}
83+
<div class="tooltip" data-tip="Failed to fetch metadata">
84+
<IconExclamationCircle class="h-4 w-4 text-error" />
85+
</div>
86+
{/if}
87+
</div>
88+
</div>
89+
</div>
90+
<div class="ml-2 flex gap-1 text-sm">
91+
{#if metadata?.bookmarkTags?.length}
92+
<span class="font-sans text-xs">Tags: </span>
93+
{/if}
94+
{#each metadata?.bookmarkTags || [] as tag (tag.value)}
95+
<a href={`/tags/${createSlug(tag.value)}`} class="link font-sans text-xs">{tag.value}</a>
96+
{/each}
97+
</div>
98+
</div>
99+
</td>
100+
<td class="max-w-xs">
101+
<div class="tooltip" data-tip={title}>
102+
<span title={title} class="line-clamp-2">{title}</span>
103+
</div>
104+
</td>
105+
<td
106+
><a
107+
class="link hover:link-secondary"
108+
href={`/categories/${createSlug(category.name)}`}
109+
target="_blank">{category.name}</a
110+
></td>
111+
<th>
112+
<button class="btn btn-ghost btn-xs text-secondary" on:click|preventDefault={onEditItem}
113+
>edit</button>
114+
<button class="btn btn-ghost btn-xs text-error" on:click|preventDefault={onRemoveItem}
115+
>remove</button>
116+
</th>
117+
</tr>

0 commit comments

Comments
 (0)