upd link handling
This commit is contained in:
parent
90970322f1
commit
786b4374b4
|
|
@ -8,6 +8,7 @@
|
||||||
import { extractAssignments, extractProjects, extractMentions, extractCompanies } from '$lib/utils/extractors';
|
import { extractAssignments, extractProjects, extractMentions, extractCompanies } from '$lib/utils/extractors';
|
||||||
import { mention } from '$lib/actions/mention';
|
import { mention } from '$lib/actions/mention';
|
||||||
import RenderedMarkdown from './RenderedMarkdown.svelte';
|
import RenderedMarkdown from './RenderedMarkdown.svelte';
|
||||||
|
import LinkTitle from './LinkTitle.svelte';
|
||||||
import MarkdownEditor from './MarkdownEditor.svelte';
|
import MarkdownEditor from './MarkdownEditor.svelte';
|
||||||
import EditableMarkdown from './EditableMarkdown.svelte';
|
import EditableMarkdown from './EditableMarkdown.svelte';
|
||||||
import ConfirmDialog from './ConfirmDialog.svelte';
|
import ConfirmDialog from './ConfirmDialog.svelte';
|
||||||
|
|
@ -276,7 +277,7 @@
|
||||||
{#each topics as lt (lt.topic.id)}
|
{#each topics as lt (lt.topic.id)}
|
||||||
<div class="mb-1.5 flex items-center gap-2 text-sm {lt.isDone ? 'text-[#666] line-through' : 'text-[#ccc]'}">
|
<div class="mb-1.5 flex items-center gap-2 text-sm {lt.isDone ? 'text-[#666] line-through' : 'text-[#ccc]'}">
|
||||||
<span class="text-xs {lt.isDone ? 'text-green-600' : 'text-[#555]'}">{lt.isDone ? '✓' : '○'}</span>
|
<span class="text-xs {lt.isDone ? 'text-green-600' : 'text-[#555]'}">{lt.isDone ? '✓' : '○'}</span>
|
||||||
<span class="flex-1">{lt.topic.title}</span>
|
<span class="flex-1"><LinkTitle text={lt.topic.title} /></span>
|
||||||
<span class="text-xs text-[#666]">{lt.contextName}</span>
|
<span class="text-xs text-[#666]">{lt.contextName}</span>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
@ -450,7 +451,7 @@
|
||||||
>{isDone ? '✓' : ''}</button>
|
>{isDone ? '✓' : ''}</button>
|
||||||
<span class="mt-0.5 text-xs text-muted whitespace-nowrap">{entry.date} {formatTime(entry.updatedAt)}</span>
|
<span class="mt-0.5 text-xs text-muted whitespace-nowrap">{entry.date} {formatTime(entry.updatedAt)}</span>
|
||||||
<div class="flex-1 {isDone ? 'line-through opacity-50' : ''}">
|
<div class="flex-1 {isDone ? 'line-through opacity-50' : ''}">
|
||||||
<div class="font-bold">{title}</div>
|
<div class="font-bold"><LinkTitle text={title} /></div>
|
||||||
{#if body}
|
{#if body}
|
||||||
<RenderedMarkdown text={body} class="mt-1 text-sm text-[#ccc]" />
|
<RenderedMarkdown text={body} class="mt-1 text-sm text-[#ccc]" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
import DateNavigator from './DateNavigator.svelte';
|
import DateNavigator from './DateNavigator.svelte';
|
||||||
import ConfirmDialog from './ConfirmDialog.svelte';
|
import ConfirmDialog from './ConfirmDialog.svelte';
|
||||||
import WiedervorlageSection from './WiedervorlageSection.svelte';
|
import WiedervorlageSection from './WiedervorlageSection.svelte';
|
||||||
|
import LinkTitle from './LinkTitle.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
contextId: string;
|
contextId: string;
|
||||||
|
|
@ -69,17 +70,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function truncateUrlDisplay(text: string): string {
|
|
||||||
if (!/^https?:\/\//i.test(text)) return text;
|
|
||||||
try {
|
|
||||||
const u = new URL(text);
|
|
||||||
const path = u.pathname !== '/' ? u.pathname : '';
|
|
||||||
const display = u.hostname + path;
|
|
||||||
return display.length > 60 ? display.slice(0, 57) + '…' : display;
|
|
||||||
} catch {
|
|
||||||
return text.length > 60 ? text.slice(0, 57) + '…' : text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleAddEntry() {
|
async function handleAddEntry() {
|
||||||
let title = entryTitle.trim();
|
let title = entryTitle.trim();
|
||||||
|
|
@ -281,11 +271,7 @@
|
||||||
<span class="mt-0.5 text-xs text-muted whitespace-nowrap">{formatTime(entry.updatedAt)}</span>
|
<span class="mt-0.5 text-xs text-muted whitespace-nowrap">{formatTime(entry.updatedAt)}</span>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="font-bold">
|
<div class="font-bold">
|
||||||
{#if /^https?:\/\//i.test(title)}
|
<LinkTitle text={title} />
|
||||||
<a href={title} target="_blank" rel="noopener noreferrer" class="text-blue-400 hover:underline">{truncateUrlDisplay(title)}</a>
|
|
||||||
{:else}
|
|
||||||
{title}
|
|
||||||
{/if}
|
|
||||||
{#if entry.linkedContextId}
|
{#if entry.linkedContextId}
|
||||||
<span class="ml-1 inline-block rounded bg-accent/20 px-1.5 py-0.5 text-xs font-normal text-accent">
|
<span class="ml-1 inline-block rounded bg-accent/20 px-1.5 py-0.5 text-xs font-normal text-accent">
|
||||||
{contextNameMap().get(entry.linkedContextId) ?? entry.linkedContextId}
|
{contextNameMap().get(entry.linkedContextId) ?? entry.linkedContextId}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { isUrl, truncateUrlDisplay } from '$lib/utils/urlUtils';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
text: string;
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
let { text, class: className = '' }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if isUrl(text)}
|
||||||
|
<a
|
||||||
|
href={text}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="text-blue-400 hover:underline {className}"
|
||||||
|
onclick={(e) => e.stopPropagation()}
|
||||||
|
>{truncateUrlDisplay(text)}</a>
|
||||||
|
{:else}
|
||||||
|
<span class={className}>{text}</span>
|
||||||
|
{/if}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import { db } from '$lib/db/schema';
|
import { db } from '$lib/db/schema';
|
||||||
import { softDeleteRating } from '$lib/db/repositories';
|
import { softDeleteRating } from '$lib/db/repositories';
|
||||||
import { RATING_CONFIG, type RatingValue } from '$lib/utils/ratingConfig';
|
import { RATING_CONFIG, type RatingValue } from '$lib/utils/ratingConfig';
|
||||||
|
import LinkTitle from './LinkTitle.svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import type { Rating } from '@ka-note/shared';
|
import type { Rating } from '@ka-note/shared';
|
||||||
import ConfirmDialog from './ConfirmDialog.svelte';
|
import ConfirmDialog from './ConfirmDialog.svelte';
|
||||||
|
|
@ -143,7 +144,7 @@
|
||||||
<span class="text-[#666]">|</span>
|
<span class="text-[#666]">|</span>
|
||||||
<span class="text-accent">{item.contextName}</span>
|
<span class="text-accent">{item.contextName}</span>
|
||||||
<span class="text-[#666]">|</span>
|
<span class="text-[#666]">|</span>
|
||||||
<span>{item.topicTitle}</span>
|
<LinkTitle text={item.topicTitle} />
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 text-sm text-[#ccc]">{cfg.label}</div>
|
<div class="mt-1 text-sm text-[#ccc]">{cfg.label}</div>
|
||||||
{#if item.rating.comment}
|
{#if item.rating.comment}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import { liveQuery } from 'dexie';
|
import { liveQuery } from 'dexie';
|
||||||
import { db } from '$lib/db/schema';
|
import { db } from '$lib/db/schema';
|
||||||
import { updateTopic } from '$lib/db/repositories';
|
import { updateTopic } from '$lib/db/repositories';
|
||||||
|
import LinkTitle from './LinkTitle.svelte';
|
||||||
import type { Topic, AgendaContext } from '@ka-note/shared';
|
import type { Topic, AgendaContext } from '@ka-note/shared';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -49,7 +50,7 @@
|
||||||
<div class="mb-2 rounded-lg border-l-[5px] border-l-muted bg-card-bg opacity-60">
|
<div class="mb-2 rounded-lg border-l-[5px] border-l-muted bg-card-bg opacity-60">
|
||||||
<div class="flex items-center justify-between px-5 py-4">
|
<div class="flex items-center justify-between px-5 py-4">
|
||||||
<div>
|
<div>
|
||||||
<span class="text-lg font-bold">{topic.title}</span>
|
<span class="text-lg font-bold"><LinkTitle text={topic.title} /></span>
|
||||||
{#if isDailyLog && topic.contextId !== 'daily-log'}
|
{#if isDailyLog && topic.contextId !== 'daily-log'}
|
||||||
<span class="ml-2 text-xs text-muted">{contextMap.get(topic.contextId) ?? topic.contextId}</span>
|
<span class="ml-2 text-xs text-muted">{contextMap.get(topic.contextId) ?? topic.contextId}</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
import { today } from '$lib/db/helpers';
|
import { today } from '$lib/db/helpers';
|
||||||
import { processedTopicIds, collapsedTopicIds } from '$lib/stores/agenda';
|
import { processedTopicIds, collapsedTopicIds } from '$lib/stores/agenda';
|
||||||
import HistoryItem from './HistoryItem.svelte';
|
import HistoryItem from './HistoryItem.svelte';
|
||||||
|
import LinkTitle from './LinkTitle.svelte';
|
||||||
import MeetingControls from './MeetingControls.svelte';
|
import MeetingControls from './MeetingControls.svelte';
|
||||||
import MarkdownEditor from './MarkdownEditor.svelte';
|
import MarkdownEditor from './MarkdownEditor.svelte';
|
||||||
import ConfirmDialog from './ConfirmDialog.svelte';
|
import ConfirmDialog from './ConfirmDialog.svelte';
|
||||||
|
|
@ -149,7 +150,7 @@
|
||||||
autofocus
|
autofocus
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<span>{topic.title}</span>
|
<LinkTitle text={topic.title} />
|
||||||
<button
|
<button
|
||||||
class="opacity-0 group-hover:opacity-100 ml-2.5 bg-transparent border-none text-[#aaa] cursor-pointer text-xs hover:text-white"
|
class="opacity-0 group-hover:opacity-100 ml-2.5 bg-transparent border-none text-[#aaa] cursor-pointer text-xs hover:text-white"
|
||||||
onclick={(e) => { e.stopPropagation(); startTitleEdit(); }}
|
onclick={(e) => { e.stopPropagation(); startTitleEdit(); }}
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,7 @@
|
||||||
import { resolveWiedervorlage, setWiedervorlage, convertToTopic, softDeleteHistoryEntry } from '$lib/db/repositories';
|
import { resolveWiedervorlage, setWiedervorlage, convertToTopic, softDeleteHistoryEntry } from '$lib/db/repositories';
|
||||||
import type { HistoryEntry } from '@ka-note/shared';
|
import type { HistoryEntry } from '@ka-note/shared';
|
||||||
import RenderedMarkdown from './RenderedMarkdown.svelte';
|
import RenderedMarkdown from './RenderedMarkdown.svelte';
|
||||||
|
import LinkTitle from './LinkTitle.svelte';
|
||||||
function truncateUrlDisplay(text: string): string {
|
|
||||||
if (!/^https?:\/\//i.test(text)) return text;
|
|
||||||
try {
|
|
||||||
const u = new URL(text);
|
|
||||||
const path = u.pathname !== '/' ? u.pathname : '';
|
|
||||||
const display = u.hostname + path;
|
|
||||||
return display.length > 60 ? display.slice(0, 57) + '…' : display;
|
|
||||||
} catch {
|
|
||||||
return text.length > 60 ? text.slice(0, 57) + '…' : text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: HistoryEntry;
|
entry: HistoryEntry;
|
||||||
|
|
@ -65,13 +54,7 @@
|
||||||
<span class="text-xs font-semibold uppercase tracking-wider text-amber-400">⏰ Wiedervorlage {entry.wiedervorlageDate}</span>
|
<span class="text-xs font-semibold uppercase tracking-wider text-amber-400">⏰ Wiedervorlage {entry.wiedervorlageDate}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="font-bold text-white">
|
<div class="font-bold text-white"><LinkTitle text={title} /></div>
|
||||||
{#if /^https?:\/\//i.test(title)}
|
|
||||||
<a href={title} target="_blank" rel="noopener noreferrer" class="text-blue-400 hover:underline">{truncateUrlDisplay(title)}</a>
|
|
||||||
{:else}
|
|
||||||
{title}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{#if body}
|
{#if body}
|
||||||
<RenderedMarkdown text={body} class="mt-1 text-sm text-[#ccc]" />
|
<RenderedMarkdown text={body} class="mt-1 text-sm text-[#ccc]" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
export function isUrl(text: string): boolean {
|
||||||
|
return /^https?:\/\//i.test(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function truncateUrlDisplay(text: string, maxLen = 60): string {
|
||||||
|
if (!isUrl(text)) return text;
|
||||||
|
try {
|
||||||
|
const u = new URL(text);
|
||||||
|
const path = u.pathname !== '/' ? u.pathname : '';
|
||||||
|
const display = u.hostname + path;
|
||||||
|
return display.length > maxLen ? display.slice(0, maxLen - 3) + '…' : display;
|
||||||
|
} catch {
|
||||||
|
return text.length > maxLen ? text.slice(0, maxLen - 3) + '…' : text;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue