mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-09-14 13:09:49 +02:00
Replace Svelte components with Lit elements (#1174)
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import { mount } from "svelte";
|
|
||||||
import { Behavior, registerBehavior } from "./index";
|
import { Behavior, registerBehavior } from "./index";
|
||||||
import SearchAutoCompleteComponent from "../components/SearchAutoComplete.svelte";
|
import "../components/SearchAutocomplete.js";
|
||||||
|
|
||||||
class SearchAutocomplete extends Behavior {
|
class SearchAutocomplete extends Behavior {
|
||||||
constructor(element) {
|
constructor(element) {
|
||||||
@@ -11,26 +10,20 @@ class SearchAutocomplete extends Behavior {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = document.createElement("div");
|
const autocomplete = document.createElement("ld-search-autocomplete");
|
||||||
|
autocomplete.name = "q";
|
||||||
mount(SearchAutoCompleteComponent, {
|
autocomplete.placeholder = input.getAttribute("placeholder") || "";
|
||||||
target: container,
|
autocomplete.value = input.value;
|
||||||
props: {
|
autocomplete.linkTarget = input.dataset.linkTarget || "_blank";
|
||||||
name: "q",
|
autocomplete.mode = input.dataset.mode || "";
|
||||||
placeholder: input.getAttribute("placeholder") || "",
|
autocomplete.search = {
|
||||||
value: input.value,
|
user: input.dataset.user,
|
||||||
linkTarget: input.dataset.linkTarget,
|
shared: input.dataset.shared,
|
||||||
mode: input.dataset.mode,
|
unread: input.dataset.unread,
|
||||||
search: {
|
};
|
||||||
user: input.dataset.user,
|
|
||||||
shared: input.dataset.shared,
|
|
||||||
unread: input.dataset.unread,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.autocomplete = container.firstElementChild;
|
this.autocomplete = autocomplete;
|
||||||
input.replaceWith(this.autocomplete);
|
input.replaceWith(this.autocomplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { Behavior, registerBehavior } from "./index";
|
import { Behavior, registerBehavior } from "./index";
|
||||||
import TagAutoCompleteComponent from "../components/TagAutocomplete.svelte";
|
import "../components/TagAutocomplete.js";
|
||||||
import { mount } from "svelte";
|
|
||||||
|
|
||||||
class TagAutocomplete extends Behavior {
|
class TagAutocomplete extends Behavior {
|
||||||
constructor(element) {
|
constructor(element) {
|
||||||
@@ -11,22 +10,16 @@ class TagAutocomplete extends Behavior {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = document.createElement("div");
|
const autocomplete = document.createElement("ld-tag-autocomplete");
|
||||||
|
autocomplete.id = input.id;
|
||||||
mount(TagAutoCompleteComponent, {
|
autocomplete.name = input.name;
|
||||||
target: container,
|
autocomplete.value = input.value;
|
||||||
props: {
|
autocomplete.placeholder = input.getAttribute("placeholder") || "";
|
||||||
id: input.id,
|
autocomplete.ariaDescribedBy = input.getAttribute("aria-describedby") || "";
|
||||||
name: input.name,
|
autocomplete.variant = input.getAttribute("variant") || "default";
|
||||||
value: input.value,
|
|
||||||
placeholder: input.getAttribute("placeholder") || "",
|
|
||||||
ariaDescribedBy: input.getAttribute("aria-describedby") || "",
|
|
||||||
variant: input.getAttribute("variant"),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.autocomplete = container.firstElementChild;
|
this.autocomplete = autocomplete;
|
||||||
input.replaceWith(this.autocomplete);
|
input.replaceWith(this.autocomplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,264 +0,0 @@
|
|||||||
<script>
|
|
||||||
import {SearchHistory} from "./SearchHistory";
|
|
||||||
import {api} from "../api";
|
|
||||||
import {cache} from "../cache";
|
|
||||||
import {clampText, debounce, getCurrentWord, getCurrentWordBounds, preventDefault} from "../util";
|
|
||||||
|
|
||||||
const searchHistory = new SearchHistory()
|
|
||||||
|
|
||||||
let {
|
|
||||||
name,
|
|
||||||
placeholder,
|
|
||||||
value = $bindable(),
|
|
||||||
mode = '',
|
|
||||||
search,
|
|
||||||
linkTarget = '_blank'
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
let isFocus = $state(false);
|
|
||||||
let isOpen = $state(false);
|
|
||||||
let suggestions = $state([])
|
|
||||||
let selectedIndex = $state(undefined);
|
|
||||||
let input = $state(null);
|
|
||||||
|
|
||||||
// Track current search query after loading the page
|
|
||||||
searchHistory.pushCurrent()
|
|
||||||
updateSuggestions()
|
|
||||||
|
|
||||||
function handleFocus() {
|
|
||||||
isFocus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBlur() {
|
|
||||||
isFocus = false;
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleInput(e) {
|
|
||||||
value = e.target.value
|
|
||||||
debouncedLoadSuggestions()
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleKeyDown(e) {
|
|
||||||
// Enter
|
|
||||||
if (isOpen && selectedIndex !== undefined && (e.keyCode === 13 || e.keyCode === 9)) {
|
|
||||||
const suggestion = suggestions.total[selectedIndex];
|
|
||||||
if (suggestion) completeSuggestion(suggestion);
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
// Escape
|
|
||||||
if (e.keyCode === 27) {
|
|
||||||
close();
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
// Up arrow
|
|
||||||
if (e.keyCode === 38) {
|
|
||||||
updateSelection(-1);
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
// Down arrow
|
|
||||||
if (e.keyCode === 40) {
|
|
||||||
if (!isOpen) {
|
|
||||||
loadSuggestions()
|
|
||||||
} else {
|
|
||||||
updateSelection(1);
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function open() {
|
|
||||||
isOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function close() {
|
|
||||||
isOpen = false;
|
|
||||||
updateSuggestions()
|
|
||||||
selectedIndex = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasSuggestions() {
|
|
||||||
return suggestions.total.length > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadSuggestions() {
|
|
||||||
|
|
||||||
let suggestionIndex = 0
|
|
||||||
|
|
||||||
function nextIndex() {
|
|
||||||
return suggestionIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tag suggestions
|
|
||||||
const tags = await cache.getTags();
|
|
||||||
let tagSuggestions = []
|
|
||||||
const currentWord = getCurrentWord(input)
|
|
||||||
if (currentWord && currentWord.length > 1 && currentWord[0] === '#') {
|
|
||||||
const searchTag = currentWord.substring(1, currentWord.length)
|
|
||||||
tagSuggestions = (tags || []).filter(tag => tag.name.toLowerCase().indexOf(searchTag.toLowerCase()) === 0)
|
|
||||||
.slice(0, 5)
|
|
||||||
.map(tag => ({
|
|
||||||
type: 'tag',
|
|
||||||
index: nextIndex(),
|
|
||||||
label: `#${tag.name}`,
|
|
||||||
tagName: tag.name
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recent search suggestions
|
|
||||||
const recentSearches = searchHistory.getRecentSearches(value, 5).map(value => ({
|
|
||||||
type: 'search',
|
|
||||||
index: nextIndex(),
|
|
||||||
label: value,
|
|
||||||
value
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Bookmark suggestions
|
|
||||||
let bookmarks = []
|
|
||||||
|
|
||||||
if (value && value.length >= 3) {
|
|
||||||
const path = mode ? `/${mode}` : ''
|
|
||||||
const suggestionSearch = {
|
|
||||||
...search,
|
|
||||||
q: value
|
|
||||||
}
|
|
||||||
const fetchedBookmarks = await api.listBookmarks(suggestionSearch, {limit: 5, offset: 0, path})
|
|
||||||
bookmarks = fetchedBookmarks.map(bookmark => {
|
|
||||||
const fullLabel = bookmark.title || bookmark.url
|
|
||||||
const label = clampText(fullLabel, 60)
|
|
||||||
return {
|
|
||||||
type: 'bookmark',
|
|
||||||
index: nextIndex(),
|
|
||||||
label,
|
|
||||||
bookmark
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSuggestions(recentSearches, bookmarks, tagSuggestions)
|
|
||||||
|
|
||||||
if (hasSuggestions()) {
|
|
||||||
open()
|
|
||||||
} else {
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const debouncedLoadSuggestions = debounce(loadSuggestions)
|
|
||||||
|
|
||||||
function updateSuggestions(recentSearches, bookmarks, tagSuggestions) {
|
|
||||||
recentSearches = recentSearches || []
|
|
||||||
bookmarks = bookmarks || []
|
|
||||||
tagSuggestions = tagSuggestions || []
|
|
||||||
suggestions = {
|
|
||||||
recentSearches,
|
|
||||||
bookmarks,
|
|
||||||
tags: tagSuggestions,
|
|
||||||
total: [
|
|
||||||
...tagSuggestions,
|
|
||||||
...recentSearches,
|
|
||||||
...bookmarks,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function completeSuggestion(suggestion) {
|
|
||||||
if (suggestion.type === 'search') {
|
|
||||||
value = suggestion.value
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
if (suggestion.type === 'bookmark') {
|
|
||||||
window.open(suggestion.bookmark.url, linkTarget)
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
if (suggestion.type === 'tag') {
|
|
||||||
const bounds = getCurrentWordBounds(input);
|
|
||||||
const inputValue = input.value;
|
|
||||||
input.value = inputValue.substring(0, bounds.start) + `#${suggestion.tagName} ` + inputValue.substring(bounds.end);
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSelection(dir) {
|
|
||||||
|
|
||||||
const length = suggestions.total.length;
|
|
||||||
|
|
||||||
if (length === 0) return
|
|
||||||
|
|
||||||
if (selectedIndex === undefined) {
|
|
||||||
selectedIndex = dir > 0 ? 0 : Math.max(length - 1, 0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let newIndex = selectedIndex + dir;
|
|
||||||
|
|
||||||
if (newIndex < 0) newIndex = Math.max(length - 1, 0);
|
|
||||||
if (newIndex >= length) newIndex = 0;
|
|
||||||
|
|
||||||
selectedIndex = newIndex;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="form-autocomplete">
|
|
||||||
<div class="form-autocomplete-input form-input" class:is-focused={isFocus}>
|
|
||||||
<input type="search" class="form-input" name="{name}" placeholder="{placeholder}" autocomplete="off" value="{value}"
|
|
||||||
bind:this={input}
|
|
||||||
oninput={handleInput} onkeydown={handleKeyDown} onfocus={handleFocus} onblur={handleBlur}>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="menu" class:open={isOpen}>
|
|
||||||
{#if suggestions.tags.length > 0}
|
|
||||||
<li class="menu-item group-item">Tags</li>
|
|
||||||
{/if}
|
|
||||||
{#each suggestions.tags as suggestion}
|
|
||||||
<li class="menu-item" class:selected={selectedIndex === suggestion.index}>
|
|
||||||
<a href="#" onmousedown={preventDefault(() => completeSuggestion(suggestion))}>
|
|
||||||
{suggestion.label}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
|
|
||||||
{#if suggestions.recentSearches.length > 0}
|
|
||||||
<li class="menu-item group-item">Recent Searches</li>
|
|
||||||
{/if}
|
|
||||||
{#each suggestions.recentSearches as suggestion}
|
|
||||||
<li class="menu-item" class:selected={selectedIndex === suggestion.index}>
|
|
||||||
<a href="#" onmousedown={preventDefault(() => completeSuggestion(suggestion))}>
|
|
||||||
{suggestion.label}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
|
|
||||||
{#if suggestions.bookmarks.length > 0}
|
|
||||||
<li class="menu-item group-item">Bookmarks</li>
|
|
||||||
{/if}
|
|
||||||
{#each suggestions.bookmarks as suggestion}
|
|
||||||
<li class="menu-item" class:selected={selectedIndex === suggestion.index}>
|
|
||||||
<a href="#" onmousedown={preventDefault(() => completeSuggestion(suggestion))}>
|
|
||||||
{suggestion.label}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.menu {
|
|
||||||
display: none;
|
|
||||||
max-height: 400px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu.open {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-autocomplete-input {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-autocomplete-input.is-focused {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
304
bookmarks/frontend/components/SearchAutocomplete.js
Normal file
304
bookmarks/frontend/components/SearchAutocomplete.js
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import { LitElement, html } from "lit";
|
||||||
|
import { SearchHistory } from "./SearchHistory.js";
|
||||||
|
import { api } from "../api.js";
|
||||||
|
import { cache } from "../cache.js";
|
||||||
|
import {
|
||||||
|
clampText,
|
||||||
|
debounce,
|
||||||
|
getCurrentWord,
|
||||||
|
getCurrentWordBounds,
|
||||||
|
} from "../util.js";
|
||||||
|
|
||||||
|
export class SearchAutocomplete extends LitElement {
|
||||||
|
static properties = {
|
||||||
|
name: { type: String },
|
||||||
|
placeholder: { type: String },
|
||||||
|
value: { type: String },
|
||||||
|
mode: { type: String },
|
||||||
|
search: { type: Object },
|
||||||
|
linkTarget: { type: String },
|
||||||
|
isFocus: { state: true },
|
||||||
|
isOpen: { state: true },
|
||||||
|
suggestions: { state: true },
|
||||||
|
selectedIndex: { state: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.name = "";
|
||||||
|
this.placeholder = "";
|
||||||
|
this.value = "";
|
||||||
|
this.mode = "";
|
||||||
|
this.search = {};
|
||||||
|
this.linkTarget = "_blank";
|
||||||
|
this.isFocus = false;
|
||||||
|
this.isOpen = false;
|
||||||
|
this.suggestions = {
|
||||||
|
recentSearches: [],
|
||||||
|
bookmarks: [],
|
||||||
|
tags: [],
|
||||||
|
total: [],
|
||||||
|
};
|
||||||
|
this.selectedIndex = undefined;
|
||||||
|
this.input = null;
|
||||||
|
this.searchHistory = new SearchHistory();
|
||||||
|
this.debouncedLoadSuggestions = debounce(() => this.loadSuggestions());
|
||||||
|
}
|
||||||
|
|
||||||
|
createRenderRoot() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.style.setProperty("--menu-max-height", "400px");
|
||||||
|
this.input = this.querySelector("input");
|
||||||
|
// Track current search query after loading the page
|
||||||
|
this.searchHistory.pushCurrent();
|
||||||
|
this.updateSuggestions();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFocus() {
|
||||||
|
this.isFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBlur() {
|
||||||
|
this.isFocus = false;
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput(e) {
|
||||||
|
this.value = e.target.value;
|
||||||
|
this.debouncedLoadSuggestions();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown(e) {
|
||||||
|
// Enter
|
||||||
|
if (
|
||||||
|
this.isOpen &&
|
||||||
|
this.selectedIndex !== undefined &&
|
||||||
|
(e.keyCode === 13 || e.keyCode === 9)
|
||||||
|
) {
|
||||||
|
const suggestion = this.suggestions.total[this.selectedIndex];
|
||||||
|
if (suggestion) this.completeSuggestion(suggestion);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
// Escape
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
this.close();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
// Up arrow
|
||||||
|
if (e.keyCode === 38) {
|
||||||
|
this.updateSelection(-1);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
// Down arrow
|
||||||
|
if (e.keyCode === 40) {
|
||||||
|
if (!this.isOpen) {
|
||||||
|
this.loadSuggestions();
|
||||||
|
} else {
|
||||||
|
this.updateSelection(1);
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
this.isOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.isOpen = false;
|
||||||
|
this.updateSuggestions();
|
||||||
|
this.selectedIndex = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSuggestions() {
|
||||||
|
return this.suggestions.total.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadSuggestions() {
|
||||||
|
let suggestionIndex = 0;
|
||||||
|
|
||||||
|
function nextIndex() {
|
||||||
|
return suggestionIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag suggestions
|
||||||
|
const tags = await cache.getTags();
|
||||||
|
let tagSuggestions = [];
|
||||||
|
const currentWord = getCurrentWord(this.input);
|
||||||
|
if (currentWord && currentWord.length > 1 && currentWord[0] === "#") {
|
||||||
|
const searchTag = currentWord.substring(1, currentWord.length);
|
||||||
|
tagSuggestions = (tags || [])
|
||||||
|
.filter(
|
||||||
|
(tag) =>
|
||||||
|
tag.name.toLowerCase().indexOf(searchTag.toLowerCase()) === 0,
|
||||||
|
)
|
||||||
|
.slice(0, 5)
|
||||||
|
.map((tag) => ({
|
||||||
|
type: "tag",
|
||||||
|
index: nextIndex(),
|
||||||
|
label: `#${tag.name}`,
|
||||||
|
tagName: tag.name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recent search suggestions
|
||||||
|
const recentSearches = this.searchHistory
|
||||||
|
.getRecentSearches(this.value, 5)
|
||||||
|
.map((value) => ({
|
||||||
|
type: "search",
|
||||||
|
index: nextIndex(),
|
||||||
|
label: value,
|
||||||
|
value,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Bookmark suggestions
|
||||||
|
let bookmarks = [];
|
||||||
|
|
||||||
|
if (this.value && this.value.length >= 3) {
|
||||||
|
const path = this.mode ? `/${this.mode}` : "";
|
||||||
|
const suggestionSearch = {
|
||||||
|
...this.search,
|
||||||
|
q: this.value,
|
||||||
|
};
|
||||||
|
const fetchedBookmarks = await api.listBookmarks(suggestionSearch, {
|
||||||
|
limit: 5,
|
||||||
|
offset: 0,
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
bookmarks = fetchedBookmarks.map((bookmark) => {
|
||||||
|
const fullLabel = bookmark.title || bookmark.url;
|
||||||
|
const label = clampText(fullLabel, 60);
|
||||||
|
return {
|
||||||
|
type: "bookmark",
|
||||||
|
index: nextIndex(),
|
||||||
|
label,
|
||||||
|
bookmark,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateSuggestions(recentSearches, bookmarks, tagSuggestions);
|
||||||
|
|
||||||
|
if (this.hasSuggestions()) {
|
||||||
|
this.open();
|
||||||
|
} else {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSuggestions(recentSearches, bookmarks, tagSuggestions) {
|
||||||
|
recentSearches = recentSearches || [];
|
||||||
|
bookmarks = bookmarks || [];
|
||||||
|
tagSuggestions = tagSuggestions || [];
|
||||||
|
this.suggestions = {
|
||||||
|
recentSearches,
|
||||||
|
bookmarks,
|
||||||
|
tags: tagSuggestions,
|
||||||
|
total: [...tagSuggestions, ...recentSearches, ...bookmarks],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
completeSuggestion(suggestion) {
|
||||||
|
if (suggestion.type === "search") {
|
||||||
|
this.value = suggestion.value;
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
if (suggestion.type === "bookmark") {
|
||||||
|
window.open(suggestion.bookmark.url, this.linkTarget);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
if (suggestion.type === "tag") {
|
||||||
|
const bounds = getCurrentWordBounds(this.input);
|
||||||
|
const inputValue = this.input.value;
|
||||||
|
this.input.value =
|
||||||
|
inputValue.substring(0, bounds.start) +
|
||||||
|
`#${suggestion.tagName} ` +
|
||||||
|
inputValue.substring(bounds.end);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelection(dir) {
|
||||||
|
const length = this.suggestions.total.length;
|
||||||
|
|
||||||
|
if (length === 0) return;
|
||||||
|
|
||||||
|
if (this.selectedIndex === undefined) {
|
||||||
|
this.selectedIndex = dir > 0 ? 0 : Math.max(length - 1, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newIndex = this.selectedIndex + dir;
|
||||||
|
|
||||||
|
if (newIndex < 0) newIndex = Math.max(length - 1, 0);
|
||||||
|
if (newIndex >= length) newIndex = 0;
|
||||||
|
|
||||||
|
this.selectedIndex = newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuggestions(suggestions, title) {
|
||||||
|
if (suggestions.length === 0) return "";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<li class="menu-item group-item">${title}</li>
|
||||||
|
${suggestions.map(
|
||||||
|
(suggestion) => html`
|
||||||
|
<li
|
||||||
|
class="menu-item ${this.selectedIndex === suggestion.index
|
||||||
|
? "selected"
|
||||||
|
: ""}"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
@mousedown=${(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.completeSuggestion(suggestion);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${suggestion.label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div class="form-autocomplete">
|
||||||
|
<div
|
||||||
|
class="form-autocomplete-input form-input ${this.isFocus
|
||||||
|
? "is-focused"
|
||||||
|
: ""}"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
class="form-input"
|
||||||
|
name="${this.name}"
|
||||||
|
placeholder="${this.placeholder}"
|
||||||
|
autocomplete="off"
|
||||||
|
.value="${this.value}"
|
||||||
|
@input=${this.handleInput}
|
||||||
|
@keydown=${this.handleKeyDown}
|
||||||
|
@focus=${this.handleFocus}
|
||||||
|
@blur=${this.handleBlur}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="menu ${this.isOpen ? "open" : ""}">
|
||||||
|
${this.renderSuggestions(this.suggestions.tags, "Tags")}
|
||||||
|
${this.renderSuggestions(
|
||||||
|
this.suggestions.recentSearches,
|
||||||
|
"Recent Searches",
|
||||||
|
)}
|
||||||
|
${this.renderSuggestions(this.suggestions.bookmarks, "Bookmarks")}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ld-search-autocomplete", SearchAutocomplete);
|
194
bookmarks/frontend/components/TagAutocomplete.js
Normal file
194
bookmarks/frontend/components/TagAutocomplete.js
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import { LitElement, html } from "lit";
|
||||||
|
import { cache } from "../cache.js";
|
||||||
|
import { getCurrentWord, getCurrentWordBounds } from "../util.js";
|
||||||
|
|
||||||
|
export class TagAutocomplete extends LitElement {
|
||||||
|
static properties = {
|
||||||
|
id: { type: String },
|
||||||
|
name: { type: String },
|
||||||
|
value: { type: String },
|
||||||
|
placeholder: { type: String },
|
||||||
|
ariaDescribedBy: { type: String, attribute: "aria-described-by" },
|
||||||
|
variant: { type: String },
|
||||||
|
isFocus: { state: true },
|
||||||
|
isOpen: { state: true },
|
||||||
|
suggestions: { state: true },
|
||||||
|
selectedIndex: { state: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.id = "";
|
||||||
|
this.name = "";
|
||||||
|
this.value = "";
|
||||||
|
this.placeholder = "";
|
||||||
|
this.ariaDescribedBy = "";
|
||||||
|
this.variant = "default";
|
||||||
|
this.isFocus = false;
|
||||||
|
this.isOpen = false;
|
||||||
|
this.suggestions = [];
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
this.input = null;
|
||||||
|
this.suggestionList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
createRenderRoot() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.input = this.querySelector("input");
|
||||||
|
this.suggestionList = this.querySelector(".menu");
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFocus() {
|
||||||
|
this.isFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBlur() {
|
||||||
|
this.isFocus = false;
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleInput(e) {
|
||||||
|
this.input = e.target;
|
||||||
|
|
||||||
|
const tags = await cache.getTags();
|
||||||
|
const word = getCurrentWord(this.input);
|
||||||
|
|
||||||
|
this.suggestions = word
|
||||||
|
? tags.filter(
|
||||||
|
(tag) => tag.name.toLowerCase().indexOf(word.toLowerCase()) === 0,
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (word && this.suggestions.length > 0) {
|
||||||
|
this.open();
|
||||||
|
} else {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown(e) {
|
||||||
|
if (this.isOpen && (e.keyCode === 13 || e.keyCode === 9)) {
|
||||||
|
const suggestion = this.suggestions[this.selectedIndex];
|
||||||
|
this.complete(suggestion);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
this.close();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
if (e.keyCode === 38) {
|
||||||
|
this.updateSelection(-1);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
if (e.keyCode === 40) {
|
||||||
|
this.updateSelection(1);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
this.isOpen = true;
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.isOpen = false;
|
||||||
|
this.suggestions = [];
|
||||||
|
this.selectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
complete(suggestion) {
|
||||||
|
const bounds = getCurrentWordBounds(this.input);
|
||||||
|
const value = this.input.value;
|
||||||
|
this.input.value =
|
||||||
|
value.substring(0, bounds.start) +
|
||||||
|
suggestion.name +
|
||||||
|
" " +
|
||||||
|
value.substring(bounds.end);
|
||||||
|
this.input.dispatchEvent(new CustomEvent("change", { bubbles: true }));
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelection(dir) {
|
||||||
|
const length = this.suggestions.length;
|
||||||
|
let newIndex = this.selectedIndex + dir;
|
||||||
|
|
||||||
|
if (newIndex < 0) newIndex = Math.max(length - 1, 0);
|
||||||
|
if (newIndex >= length) newIndex = 0;
|
||||||
|
|
||||||
|
this.selectedIndex = newIndex;
|
||||||
|
|
||||||
|
// Scroll to selected list item
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.suggestionList) {
|
||||||
|
const selectedListItem =
|
||||||
|
this.suggestionList.querySelector("li.selected");
|
||||||
|
if (selectedListItem) {
|
||||||
|
selectedListItem.scrollIntoView({ block: "center" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div class="form-autocomplete ${this.variant === "small" ? "small" : ""}">
|
||||||
|
<!-- autocomplete input container -->
|
||||||
|
<div
|
||||||
|
class="form-autocomplete-input form-input ${this.isFocus
|
||||||
|
? "is-focused"
|
||||||
|
: ""}"
|
||||||
|
>
|
||||||
|
<!-- autocomplete real input box -->
|
||||||
|
<input
|
||||||
|
id="${this.id}"
|
||||||
|
name="${this.name}"
|
||||||
|
.value="${this.value || ""}"
|
||||||
|
placeholder="${this.placeholder || " "}"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
autocapitalize="off"
|
||||||
|
aria-describedby="${this.ariaDescribedBy}"
|
||||||
|
@input=${this.handleInput}
|
||||||
|
@keydown=${this.handleKeyDown}
|
||||||
|
@focus=${this.handleFocus}
|
||||||
|
@blur=${this.handleBlur}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- autocomplete suggestion list -->
|
||||||
|
<ul
|
||||||
|
class="menu ${this.isOpen && this.suggestions.length > 0
|
||||||
|
? "open"
|
||||||
|
: ""}"
|
||||||
|
>
|
||||||
|
<!-- menu list items -->
|
||||||
|
${this.suggestions.map(
|
||||||
|
(tag, i) => html`
|
||||||
|
<li
|
||||||
|
class="menu-item ${this.selectedIndex === i ? "selected" : ""}"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
@mousedown=${(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.complete(tag);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${tag.name}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ld-tag-autocomplete", TagAutocomplete);
|
@@ -1,173 +0,0 @@
|
|||||||
<script>
|
|
||||||
import {cache} from "../cache";
|
|
||||||
import {getCurrentWord, getCurrentWordBounds, preventDefault} from "../util";
|
|
||||||
|
|
||||||
let {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
placeholder,
|
|
||||||
ariaDescribedBy,
|
|
||||||
variant = 'default'
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
let isFocus = $state(false);
|
|
||||||
let isOpen = $state(false);
|
|
||||||
let input = null;
|
|
||||||
let suggestionList = $state(null);
|
|
||||||
|
|
||||||
let suggestions = $state([]);
|
|
||||||
let selectedIndex = $state(0);
|
|
||||||
|
|
||||||
function handleFocus() {
|
|
||||||
isFocus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBlur() {
|
|
||||||
isFocus = false;
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleInput(e) {
|
|
||||||
input = e.target;
|
|
||||||
|
|
||||||
const tags = await cache.getTags();
|
|
||||||
const word = getCurrentWord(input);
|
|
||||||
|
|
||||||
suggestions = word
|
|
||||||
? tags.filter(tag => tag.name.toLowerCase().indexOf(word.toLowerCase()) === 0)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
if (word && suggestions.length > 0) {
|
|
||||||
open();
|
|
||||||
} else {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleKeyDown(e) {
|
|
||||||
if (isOpen && (e.keyCode === 13 || e.keyCode === 9)) {
|
|
||||||
const suggestion = suggestions[selectedIndex];
|
|
||||||
complete(suggestion);
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
if (e.keyCode === 27) {
|
|
||||||
close();
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
if (e.keyCode === 38) {
|
|
||||||
updateSelection(-1);
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
if (e.keyCode === 40) {
|
|
||||||
updateSelection(1);
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function open() {
|
|
||||||
isOpen = true;
|
|
||||||
selectedIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function close() {
|
|
||||||
isOpen = false;
|
|
||||||
suggestions = [];
|
|
||||||
selectedIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function complete(suggestion) {
|
|
||||||
const bounds = getCurrentWordBounds(input);
|
|
||||||
const value = input.value;
|
|
||||||
input.value = value.substring(0, bounds.start) + suggestion.name + ' ' + value.substring(bounds.end);
|
|
||||||
input.dispatchEvent(new CustomEvent('change', {bubbles: true}));
|
|
||||||
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSelection(dir) {
|
|
||||||
|
|
||||||
const length = suggestions.length;
|
|
||||||
let newIndex = selectedIndex + dir;
|
|
||||||
|
|
||||||
if (newIndex < 0) newIndex = Math.max(length - 1, 0);
|
|
||||||
if (newIndex >= length) newIndex = 0;
|
|
||||||
|
|
||||||
selectedIndex = newIndex;
|
|
||||||
|
|
||||||
// Scroll to selected list item
|
|
||||||
setTimeout(() => {
|
|
||||||
if (suggestionList) {
|
|
||||||
const selectedListItem = suggestionList.querySelector('li.selected');
|
|
||||||
if (selectedListItem) {
|
|
||||||
selectedListItem.scrollIntoView({block: 'center'});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="form-autocomplete" class:small={variant === 'small'}>
|
|
||||||
<!-- autocomplete input container -->
|
|
||||||
<div class="form-autocomplete-input form-input" class:is-focused={isFocus}>
|
|
||||||
<!-- autocomplete real input box -->
|
|
||||||
<input id="{id}" name="{name}" value="{value ||''}" placeholder="{placeholder || ' '}"
|
|
||||||
class="form-input" type="text" autocomplete="off" autocapitalize="off"
|
|
||||||
aria-describedby="{ariaDescribedBy}"
|
|
||||||
oninput={handleInput} onkeydown={handleKeyDown}
|
|
||||||
onfocus={handleFocus} onblur={handleBlur}>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- autocomplete suggestion list -->
|
|
||||||
<ul class="menu" class:open={isOpen && suggestions.length > 0}
|
|
||||||
bind:this={suggestionList}>
|
|
||||||
<!-- menu list items -->
|
|
||||||
{#each suggestions as tag,i}
|
|
||||||
<li class="menu-item" class:selected={selectedIndex === i}>
|
|
||||||
<a href="#" onmousedown={preventDefault(() => complete(tag))}>
|
|
||||||
{tag.name}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.menu {
|
|
||||||
display: none;
|
|
||||||
max-height: 200px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu.open {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-autocomplete .form-autocomplete-input {
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: var(--control-size);
|
|
||||||
min-height: var(--control-size);
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-autocomplete .form-autocomplete-input input {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-autocomplete.small .form-autocomplete-input {
|
|
||||||
height: var(--control-size-sm);
|
|
||||||
min-height: var(--control-size-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-autocomplete.small .form-autocomplete-input input {
|
|
||||||
padding: 0.05rem 0.3rem;
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-autocomplete.small .menu .menu-item {
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -11,7 +11,5 @@ import "./behaviors/global-shortcuts";
|
|||||||
import "./behaviors/search-autocomplete";
|
import "./behaviors/search-autocomplete";
|
||||||
import "./behaviors/tag-autocomplete";
|
import "./behaviors/tag-autocomplete";
|
||||||
|
|
||||||
export { default as TagAutoComplete } from "./components/TagAutocomplete.svelte";
|
|
||||||
export { default as SearchAutoComplete } from "./components/SearchAutoComplete.svelte";
|
|
||||||
export { api } from "./api";
|
export { api } from "./api";
|
||||||
export { cache } from "./cache";
|
export { cache } from "./cache";
|
||||||
|
@@ -3,13 +3,14 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& .form-autocomplete-input {
|
& .form-autocomplete-input {
|
||||||
|
box-sizing: border-box;
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
height: auto;
|
|
||||||
min-height: var(--unit-8);
|
|
||||||
padding: var(--unit-h);
|
|
||||||
background: var(--input-bg-color);
|
background: var(--input-bg-color);
|
||||||
|
height: var(--control-size);
|
||||||
|
min-height: var(--control-size);
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
&.is-focused {
|
&.is-focused {
|
||||||
outline: var(--focus-outline);
|
outline: var(--focus-outline);
|
||||||
@@ -22,10 +23,11 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
height: var(--unit-6);
|
|
||||||
line-height: var(--unit-4);
|
line-height: var(--unit-4);
|
||||||
margin: var(--unit-h);
|
width: 100%;
|
||||||
width: auto;
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -33,11 +35,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
.form-autocomplete-input {
|
||||||
|
height: var(--control-size-sm);
|
||||||
|
min-height: var(--control-size-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-autocomplete-input input {
|
||||||
|
padding: 0.05rem 0.3rem;
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu .menu-item {
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
& .menu {
|
& .menu {
|
||||||
|
display: none;
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-height: var(--menu-max-height, 200px);
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
& .menu-item.selected > a,
|
& .menu-item.selected > a,
|
||||||
& .menu-item > a:hover {
|
& .menu-item > a:hover {
|
||||||
@@ -54,4 +75,8 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .menu.open {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
223
package-lock.json
generated
223
package-lock.json
generated
@@ -14,12 +14,11 @@
|
|||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"@rollup/wasm-node": "^4.13.0",
|
"@rollup/wasm-node": "^4.13.0",
|
||||||
"cssnano": "^7.0.6",
|
"cssnano": "^7.0.6",
|
||||||
|
"lit": "^3.3.1",
|
||||||
"postcss": "^8.4.45",
|
"postcss": "^8.4.45",
|
||||||
"postcss-cli": "^11.0.0",
|
"postcss-cli": "^11.0.0",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.0",
|
||||||
"postcss-nesting": "^13.0.0",
|
"postcss-nesting": "^13.0.0"
|
||||||
"rollup-plugin-svelte": "^7.2.2",
|
|
||||||
"svelte": "^5.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.3.3"
|
"prettier": "^3.3.3"
|
||||||
@@ -88,16 +87,6 @@
|
|||||||
"@jridgewell/trace-mapping": "^0.3.24"
|
"@jridgewell/trace-mapping": "^0.3.24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/remapping": {
|
|
||||||
"version": "2.3.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
|
||||||
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/gen-mapping": "^0.3.5",
|
|
||||||
"@jridgewell/trace-mapping": "^0.3.24"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@jridgewell/resolve-uri": {
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
@@ -133,6 +122,21 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@lit-labs/ssr-dom-shim": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@lit/reactive-element": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit-labs/ssr-dom-shim": "^1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-node-resolve": {
|
"node_modules/@rollup/plugin-node-resolve": {
|
||||||
"version": "16.0.1",
|
"version": "16.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
|
||||||
@@ -500,15 +504,6 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sveltejs/acorn-typescript": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"acorn": "^8.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||||
@@ -521,6 +516,12 @@
|
|||||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.15.0",
|
"version": "8.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
@@ -582,24 +583,6 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/aria-query": {
|
|
||||||
"version": "5.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
|
|
||||||
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/axobject-query": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
@@ -738,15 +721,6 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/clsx": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -1060,21 +1034,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esm-env": {
|
|
||||||
"version": "1.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
|
|
||||||
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/esrap": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/estree-walker": {
|
"node_modules/estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
@@ -1258,15 +1217,6 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-reference": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/estree": "^1.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jsonfile": {
|
"node_modules/jsonfile": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||||
@@ -1291,11 +1241,36 @@
|
|||||||
"url": "https://github.com/sponsors/antonk52"
|
"url": "https://github.com/sponsors/antonk52"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/locate-character": {
|
"node_modules/lit": {
|
||||||
"version": "3.0.0",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lit/-/lit-3.3.1.tgz",
|
||||||
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
|
"integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==",
|
||||||
"license": "MIT"
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit/reactive-element": "^2.1.0",
|
||||||
|
"lit-element": "^4.2.0",
|
||||||
|
"lit-html": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lit-element": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit-labs/ssr-dom-shim": "^1.4.0",
|
||||||
|
"@lit/reactive-element": "^2.1.0",
|
||||||
|
"lit-html": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lit-html": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/trusted-types": "^2.0.2"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash.memoize": {
|
"node_modules/lodash.memoize": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
@@ -1309,15 +1284,6 @@
|
|||||||
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
|
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/magic-string": {
|
|
||||||
"version": "0.30.18",
|
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
|
|
||||||
"integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mdn-data": {
|
"node_modules/mdn-data": {
|
||||||
"version": "2.12.2",
|
"version": "2.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
|
||||||
@@ -2093,20 +2059,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/resolve.exports": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
|
|
||||||
"integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.48.0",
|
"version": "4.48.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.0.tgz",
|
||||||
"integrity": "sha512-BXHRqK1vyt9XVSEHZ9y7xdYtuYbwVod2mLwOMFP7t/Eqoc1pHRlG/WdV2qNeNvZHRQdLedaFycljaYYM96RqJQ==",
|
"integrity": "sha512-BXHRqK1vyt9XVSEHZ9y7xdYtuYbwVod2mLwOMFP7t/Eqoc1pHRlG/WdV2qNeNvZHRQdLedaFycljaYYM96RqJQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.8"
|
"@types/estree": "1.0.8"
|
||||||
@@ -2142,48 +2100,6 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup-plugin-svelte": {
|
|
||||||
"version": "7.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.2.2.tgz",
|
|
||||||
"integrity": "sha512-hgnIblTRewaBEVQD6N0Q43o+y6q1TmDRhBjaEzQCi50bs8TXqjc+d1zFZyE8tsfgcfNHZQzclh4RxlFUB85H8Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@rollup/pluginutils": "^4.1.0",
|
|
||||||
"resolve.exports": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"rollup": ">=2.0.0",
|
|
||||||
"svelte": ">=3.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rollup-plugin-svelte/node_modules/@rollup/pluginutils": {
|
|
||||||
"version": "4.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
|
|
||||||
"integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"estree-walker": "^2.0.1",
|
|
||||||
"picomatch": "^2.2.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rollup-plugin-svelte/node_modules/picomatch": {
|
|
||||||
"version": "2.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
@@ -2319,31 +2235,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte": {
|
|
||||||
"version": "5.38.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.38.2.tgz",
|
|
||||||
"integrity": "sha512-iAcp/oFAWauVSGILdD67n7DiwgLHXZzWZIdzl7araRxu72jUr7PFAo2Iie7gXt0IbnlYvhxCb9GT3ZJUquO3PA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/remapping": "^2.3.4",
|
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
|
||||||
"@sveltejs/acorn-typescript": "^1.0.5",
|
|
||||||
"@types/estree": "^1.0.5",
|
|
||||||
"acorn": "^8.12.1",
|
|
||||||
"aria-query": "^5.3.1",
|
|
||||||
"axobject-query": "^4.1.0",
|
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"esm-env": "^1.2.1",
|
|
||||||
"esrap": "^2.1.0",
|
|
||||||
"is-reference": "^3.0.3",
|
|
||||||
"locate-character": "^3.0.0",
|
|
||||||
"magic-string": "^0.30.11",
|
|
||||||
"zimmerframe": "^1.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svgo": {
|
"node_modules/svgo": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
|
||||||
@@ -2536,12 +2427,6 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/zimmerframe": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,12 +27,11 @@
|
|||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"@rollup/wasm-node": "^4.13.0",
|
"@rollup/wasm-node": "^4.13.0",
|
||||||
"cssnano": "^7.0.6",
|
"cssnano": "^7.0.6",
|
||||||
|
"lit": "^3.3.1",
|
||||||
"postcss": "^8.4.45",
|
"postcss": "^8.4.45",
|
||||||
"postcss-cli": "^11.0.0",
|
"postcss-cli": "^11.0.0",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.0",
|
||||||
"postcss-nesting": "^13.0.0",
|
"postcss-nesting": "^13.0.0"
|
||||||
"rollup-plugin-svelte": "^7.2.2",
|
|
||||||
"svelte": "^5.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.3.3"
|
"prettier": "^3.3.3"
|
||||||
|
@@ -1,27 +1,18 @@
|
|||||||
import svelte from "rollup-plugin-svelte";
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
import resolve from "@rollup/plugin-node-resolve";
|
import terser from '@rollup/plugin-terser';
|
||||||
import terser from "@rollup/plugin-terser";
|
|
||||||
|
|
||||||
const production = !process.env.ROLLUP_WATCH;
|
const production = !process.env.ROLLUP_WATCH;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: "bookmarks/frontend/index.js",
|
input: 'bookmarks/frontend/index.js',
|
||||||
output: {
|
output: {
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
format: "iife",
|
format: 'iife',
|
||||||
name: "linkding",
|
name: 'linkding',
|
||||||
// Generate bundle in static folder to that it is picked up by Django static files finder
|
// Generate bundle in static folder to that it is picked up by Django static files finder
|
||||||
file: "bookmarks/static/bundle.js",
|
file: 'bookmarks/static/bundle.js',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
svelte({
|
|
||||||
emitCss: false,
|
|
||||||
compilerOptions: {
|
|
||||||
// Workaround for rollup-plugin-svelte not setting the compiler option when emitCss is false
|
|
||||||
css: "injected",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
// If you have external dependencies installed from
|
// If you have external dependencies installed from
|
||||||
// npm, you'll most likely need these plugins. In
|
// npm, you'll most likely need these plugins. In
|
||||||
// some cases you'll need additional configuration —
|
// some cases you'll need additional configuration —
|
||||||
|
Reference in New Issue
Block a user