feat: server shorcuts
This commit is contained in:
parent
1bfdccc566
commit
2d5c94587b
|
|
@ -1,7 +1,14 @@
|
|||
import { createSettingAtom } from "@follow/atoms/helper/setting.js"
|
||||
import { defaultAISettings } from "@follow/shared/settings/defaults"
|
||||
import type { AISettings, MCPService } from "@follow/shared/settings/interface"
|
||||
import type {
|
||||
AISettings,
|
||||
AIShortcut,
|
||||
AIShortcutTarget,
|
||||
MCPService,
|
||||
} from "@follow/shared/settings/interface"
|
||||
import { DEFAULT_SHORTCUT_TARGETS } from "@follow/shared/settings/interface"
|
||||
import { jotaiStore } from "@follow/utils"
|
||||
import type { ExtractResponseData, GetStatusConfigsResponse } from "@follow-app/client-sdk"
|
||||
import { clamp } from "es-toolkit"
|
||||
import { atom, useAtomValue } from "jotai"
|
||||
|
||||
|
|
@ -12,8 +19,98 @@ export interface WebAISettings extends AISettings {
|
|||
showSplineButton: boolean
|
||||
}
|
||||
|
||||
type ServerShortcutConfig = ExtractResponseData<GetStatusConfigsResponse>["AI_SHORTCUTS"][number]
|
||||
|
||||
const FALLBACK_SHORTCUT_ICON = "i-mgc-hotkey-cute-re"
|
||||
const VALID_SHORTCUT_TARGETS = new Set<AIShortcutTarget>(DEFAULT_SHORTCUT_TARGETS)
|
||||
|
||||
const isValidShortcutTarget = (target: string): target is AIShortcutTarget =>
|
||||
VALID_SHORTCUT_TARGETS.has(target as AIShortcutTarget)
|
||||
|
||||
const sanitizeShortcutTargets = (targets?: readonly string[]): AIShortcutTarget[] => {
|
||||
if (!targets || targets.length === 0) {
|
||||
return [...DEFAULT_SHORTCUT_TARGETS]
|
||||
}
|
||||
|
||||
const filtered = targets.filter(isValidShortcutTarget) as AIShortcutTarget[]
|
||||
return filtered.length > 0 ? [...filtered] : [...DEFAULT_SHORTCUT_TARGETS]
|
||||
}
|
||||
|
||||
const normalizeShortcut = (shortcut: AIShortcut): AIShortcut => {
|
||||
return {
|
||||
...shortcut,
|
||||
displayTargets: sanitizeShortcutTargets(shortcut.displayTargets),
|
||||
enabled: typeof shortcut.enabled === "boolean" ? shortcut.enabled : true,
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeShortcuts = (shortcuts: readonly AIShortcut[] | undefined): AIShortcut[] =>
|
||||
(shortcuts ?? []).map((shortcut) => normalizeShortcut({ ...shortcut }))
|
||||
|
||||
const mergeWithServerShortcuts = (
|
||||
localShortcuts: readonly AIShortcut[],
|
||||
serverShortcuts: readonly ServerShortcutConfig[],
|
||||
): AIShortcut[] => {
|
||||
const normalizedLocal = normalizeShortcuts(localShortcuts)
|
||||
if (serverShortcuts.length === 0) {
|
||||
return normalizedLocal
|
||||
}
|
||||
|
||||
const serverShortcutMap = new Map<string, ServerShortcutConfig>()
|
||||
serverShortcuts.forEach((shortcut) => {
|
||||
serverShortcutMap.set(shortcut.id, shortcut)
|
||||
})
|
||||
|
||||
const seenServerShortcutIds = new Set<string>()
|
||||
const mergedShortcuts: AIShortcut[] = []
|
||||
|
||||
normalizedLocal.forEach((shortcut) => {
|
||||
const serverShortcut = serverShortcutMap.get(shortcut.id)
|
||||
if (!serverShortcut) {
|
||||
mergedShortcuts.push(shortcut)
|
||||
return
|
||||
}
|
||||
|
||||
seenServerShortcutIds.add(serverShortcut.id)
|
||||
const shouldClearPrompt = shortcut.prompt === serverShortcut.defaultPrompt
|
||||
|
||||
mergedShortcuts.push({
|
||||
...shortcut,
|
||||
name: shortcut.name || serverShortcut.name,
|
||||
prompt: shouldClearPrompt ? "" : shortcut.prompt,
|
||||
defaultPrompt: serverShortcut.defaultPrompt,
|
||||
displayTargets: sanitizeShortcutTargets(
|
||||
shortcut.displayTargets || serverShortcut.displayTargets,
|
||||
),
|
||||
})
|
||||
})
|
||||
|
||||
serverShortcuts.forEach((serverShortcut) => {
|
||||
if (seenServerShortcutIds.has(serverShortcut.id)) return
|
||||
|
||||
mergedShortcuts.push({
|
||||
id: serverShortcut.id,
|
||||
name: serverShortcut.name,
|
||||
prompt: "",
|
||||
defaultPrompt: serverShortcut.defaultPrompt,
|
||||
enabled: true,
|
||||
icon: FALLBACK_SHORTCUT_ICON,
|
||||
displayTargets: sanitizeShortcutTargets(serverShortcut.displayTargets),
|
||||
})
|
||||
})
|
||||
|
||||
return mergedShortcuts
|
||||
}
|
||||
|
||||
export const getShortcutEffectivePrompt = (shortcut: AIShortcut): string => {
|
||||
return shortcut.prompt || shortcut.defaultPrompt || ""
|
||||
}
|
||||
|
||||
export const isServerShortcut = (shortcut: AIShortcut) => !!shortcut.defaultPrompt
|
||||
|
||||
export const createDefaultSettings = (): WebAISettings => ({
|
||||
...defaultAISettings,
|
||||
shortcuts: normalizeShortcuts(defaultAISettings.shortcuts),
|
||||
panelStyle: AIChatPanelStyle.Fixed,
|
||||
showSplineButton: true,
|
||||
})
|
||||
|
|
@ -30,6 +127,16 @@ export const {
|
|||
} = createSettingAtom("ai", createDefaultSettings)
|
||||
export const aiServerSyncWhiteListKeys = []
|
||||
|
||||
export const syncServerShortcuts = (
|
||||
serverShortcuts: readonly ServerShortcutConfig[] | null | undefined,
|
||||
) => {
|
||||
const storedShortcuts = getAISettings().shortcuts ?? []
|
||||
const serverShortcutList = Array.isArray(serverShortcuts) ? serverShortcuts : []
|
||||
const mergedShortcuts = mergeWithServerShortcuts(storedShortcuts, serverShortcutList)
|
||||
|
||||
setAISetting("shortcuts", mergedShortcuts)
|
||||
}
|
||||
|
||||
////////// AI Panel Style
|
||||
export enum AIChatPanelStyle {
|
||||
Fixed = "fixed",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { AIShortcut } from "@follow/shared/settings/interface"
|
|||
import type { FC } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { getShortcutEffectivePrompt } from "~/atoms/settings/ai"
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
|
|
@ -34,7 +35,10 @@ export const ShortcutsMenuContent: FC<ShortcutsMenuContentProps> = ({
|
|||
<div className="p-3 text-center text-xs text-text-tertiary">{emptyMessage}</div>
|
||||
) : (
|
||||
enabledShortcuts.map((shortcut) => (
|
||||
<DropdownMenuItem key={shortcut.id} onClick={() => onSendShortcut?.(shortcut.prompt)}>
|
||||
<DropdownMenuItem
|
||||
key={shortcut.id}
|
||||
onClick={() => onSendShortcut?.(getShortcutEffectivePrompt(shortcut))}
|
||||
>
|
||||
<i className="i-mgc-magic-2-cute-re mr-1.5 size-3.5" />
|
||||
<span className="truncate">{shortcut.name}</span>
|
||||
</DropdownMenuItem>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useMemo } from "react"
|
||||
|
||||
import { useAISettingValue } from "~/atoms/settings/ai"
|
||||
import { getShortcutEffectivePrompt, useAISettingValue } from "~/atoms/settings/ai"
|
||||
|
||||
import type { ShortcutData } from "../types"
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ export const useShortcutSearchService = () => {
|
|||
const normalizedShortcuts: ShortcutData[] = shortcuts.map((shortcut) => ({
|
||||
id: shortcut.id,
|
||||
name: shortcut.name,
|
||||
prompt: shortcut.prompt,
|
||||
prompt: getShortcutEffectivePrompt(shortcut),
|
||||
hotkey: shortcut.hotkey,
|
||||
displayTargets: shortcut.displayTargets,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { getAISettings } from "~/atoms/settings/ai"
|
||||
import { getAISettings, getShortcutEffectivePrompt } from "~/atoms/settings/ai"
|
||||
|
||||
import type { ShortcutData } from "../types"
|
||||
|
||||
export function getShortcutTextValue(shortcutData: ShortcutData): string {
|
||||
const allShortcuts = getAISettings().shortcuts ?? []
|
||||
const matchedShortcut = allShortcuts.find((shortcut) => shortcut.id === shortcutData.id)
|
||||
if (matchedShortcut) {
|
||||
return getShortcutEffectivePrompt(matchedShortcut)
|
||||
}
|
||||
return shortcutData.prompt
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { $createParagraphNode, $getRoot, createEditor } from "lexical"
|
|||
import { nanoid } from "nanoid"
|
||||
import { useEffect, useMemo, useRef } from "react"
|
||||
|
||||
import { useAISettingValue } from "~/atoms/settings/ai"
|
||||
import { getShortcutEffectivePrompt, useAISettingValue } from "~/atoms/settings/ai"
|
||||
import { useGeneralSettingKey } from "~/atoms/settings/general"
|
||||
import { ROUTE_FEED_IN_FOLDER, ROUTE_FEED_PENDING } from "~/constants"
|
||||
import { useRouteParamsSelector } from "~/hooks/biz/useRouteParams"
|
||||
|
|
@ -105,10 +105,7 @@ export const useAutoTimelineSummaryShortcut = () => {
|
|||
const defaultShortcut = useMemo(() => {
|
||||
const shortcuts = aiSettings.shortcuts ?? []
|
||||
return shortcuts.find(
|
||||
(shortcut) =>
|
||||
shortcut.id === DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID &&
|
||||
shortcut.enabled &&
|
||||
shortcut.prompt,
|
||||
(shortcut) => shortcut.id === DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID && shortcut.enabled,
|
||||
)
|
||||
}, [aiSettings.shortcuts])
|
||||
|
||||
|
|
@ -217,10 +214,8 @@ export const useAutoTimelineSummaryShortcut = () => {
|
|||
|
||||
const run = async () => {
|
||||
try {
|
||||
const { prompt, id, name } = defaultShortcut
|
||||
if (!prompt) {
|
||||
return
|
||||
}
|
||||
const prompt = getShortcutEffectivePrompt(defaultShortcut)
|
||||
const { id, name } = defaultShortcut
|
||||
|
||||
const existingSession = await AIPersistService.findTimelineSummarySession({
|
||||
view,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,4 @@
|
|||
import { Button } from "@follow/components/ui/button/index.js"
|
||||
import {
|
||||
DEFAULT_SUMMARIZE_TIMELINE_PROMPT,
|
||||
DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID,
|
||||
defaultAISettings,
|
||||
} from "@follow/shared/settings/defaults"
|
||||
import { useEffect } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { toast } from "sonner"
|
||||
|
||||
|
|
@ -17,22 +11,6 @@ export const AIShortcutsSection = () => {
|
|||
const { t } = useTranslation("ai")
|
||||
const { shortcuts } = useAISettingValue()
|
||||
|
||||
useEffect(() => {
|
||||
if (!shortcuts.some((shortcut) => shortcut.id === DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID)) {
|
||||
const defaultSummarizeShortcut = defaultAISettings.shortcuts.find(
|
||||
(shortcut) => shortcut.id === DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID,
|
||||
) ?? {
|
||||
name: "Summarize",
|
||||
prompt: DEFAULT_SUMMARIZE_TIMELINE_PROMPT,
|
||||
enabled: true,
|
||||
displayTargets: ["list"],
|
||||
id: DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID,
|
||||
}
|
||||
|
||||
setAISetting("shortcuts", [...shortcuts, { ...defaultSummarizeShortcut }])
|
||||
}
|
||||
}, [shortcuts])
|
||||
|
||||
const handleAddShortcut = useCreateAIShortcutModal()
|
||||
const handleEditShortcut = useEditAIShortcutModal()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { KbdCombined } from "@follow/components/ui/kbd/Kbd.js"
|
||||
import { DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID } from "@follow/shared/settings/defaults"
|
||||
import type { AIShortcut, AIShortcutTarget } from "@follow/shared/settings/interface"
|
||||
import { DEFAULT_SHORTCUT_TARGETS } from "@follow/shared/settings/interface"
|
||||
import type { AIShortcut } from "@follow/shared/settings/interface"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import type { ActionButton } from "~/modules/ai-task/components/ai-item-actions"
|
||||
|
|
@ -16,11 +15,8 @@ interface ShortcutItemProps {
|
|||
|
||||
export const ShortcutItem = ({ shortcut, onDelete, onToggle, onEdit }: ShortcutItemProps) => {
|
||||
const { t } = useTranslation("ai")
|
||||
const isProtected = shortcut.id === DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID
|
||||
const targets =
|
||||
shortcut.displayTargets && shortcut.displayTargets.length > 0
|
||||
? (shortcut.displayTargets as AIShortcutTarget[])
|
||||
: DEFAULT_SHORTCUT_TARGETS
|
||||
const isProtected =
|
||||
shortcut.defaultPrompt || shortcut.id === DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID
|
||||
const actions: ActionButton[] = [
|
||||
{
|
||||
icon: "i-mgc-edit-cute-re",
|
||||
|
|
@ -50,14 +46,11 @@ export const ShortcutItem = ({ shortcut, onDelete, onToggle, onEdit }: ShortcutI
|
|||
</KbdCombined>
|
||||
)}
|
||||
</div>
|
||||
<p className="line-clamp-2 text-xs leading-relaxed text-text-secondary">
|
||||
{shortcut.prompt}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{targets.map((target) => (
|
||||
{shortcut.displayTargets?.map((target) => (
|
||||
<span
|
||||
key={target}
|
||||
className="inline-flex items-center rounded-full bg-material-thin px-2 py-0.5 text-[11px] font-medium uppercase tracking-wide text-text-tertiary"
|
||||
className="inline-flex items-center rounded-full bg-material-thin px-2 py-0.5 text-[11px] font-medium tracking-wide text-text-tertiary"
|
||||
>
|
||||
{t(`shortcuts.targets.${target}`)}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -9,16 +9,14 @@ import {
|
|||
PopoverTrigger,
|
||||
} from "@follow/components/ui/popover/index.js"
|
||||
import { Switch } from "@follow/components/ui/switch/index.jsx"
|
||||
import {
|
||||
DEFAULT_SUMMARIZE_TIMELINE_PROMPT,
|
||||
DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID,
|
||||
} from "@follow/shared/settings/defaults"
|
||||
import type { AIShortcut, AIShortcutTarget } from "@follow/shared/settings/interface"
|
||||
import { DEFAULT_SHORTCUT_TARGETS } from "@follow/shared/settings/interface"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { isServerShortcut } from "~/atoms/settings/ai"
|
||||
|
||||
interface ShortcutModalContentProps {
|
||||
shortcut?: AIShortcut | null
|
||||
onSave: (shortcut: Omit<AIShortcut, "id">) => void
|
||||
|
|
@ -28,14 +26,7 @@ interface ShortcutModalContentProps {
|
|||
export const ShortcutModalContent = ({ shortcut, onSave, onCancel }: ShortcutModalContentProps) => {
|
||||
const { t } = useTranslation("ai")
|
||||
const [name, setName] = useState(shortcut?.name || "")
|
||||
const isDefaultSummarize = shortcut?.id === DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID
|
||||
const resolvedPrompt = useMemo(() => {
|
||||
if (isDefaultSummarize) {
|
||||
return shortcut?.prompt?.trim() || DEFAULT_SUMMARIZE_TIMELINE_PROMPT
|
||||
}
|
||||
return shortcut?.prompt || ""
|
||||
}, [isDefaultSummarize, shortcut?.prompt])
|
||||
const [prompt, setPrompt] = useState(resolvedPrompt)
|
||||
const [prompt, setPrompt] = useState(shortcut?.prompt || "")
|
||||
const [enabled, setEnabled] = useState(shortcut?.enabled ?? true)
|
||||
const [icon, setIcon] = useState<string>(shortcut?.icon || "i-mgc-hotkey-cute-re")
|
||||
const initialTargets = useMemo<AIShortcutTarget[]>(() => {
|
||||
|
|
@ -77,15 +68,12 @@ export const ShortcutModalContent = ({ shortcut, onSave, onCancel }: ShortcutMod
|
|||
],
|
||||
[],
|
||||
)
|
||||
const isServer = shortcut && isServerShortcut(shortcut)
|
||||
|
||||
useEffect(() => {
|
||||
setDisplayTargets(initialTargets)
|
||||
}, [initialTargets])
|
||||
|
||||
useEffect(() => {
|
||||
setPrompt(resolvedPrompt)
|
||||
}, [resolvedPrompt])
|
||||
|
||||
const handleTargetChange = (target: AIShortcutTarget, checked: boolean) => {
|
||||
setDisplayTargets((prev) => {
|
||||
if (checked) {
|
||||
|
|
@ -101,11 +89,15 @@ export const ShortcutModalContent = ({ shortcut, onSave, onCancel }: ShortcutMod
|
|||
const handleSave = () => {
|
||||
const trimmedName = name.trim()
|
||||
const trimmedPrompt = prompt.trim()
|
||||
const finalPrompt =
|
||||
trimmedPrompt || (isDefaultSummarize ? DEFAULT_SUMMARIZE_TIMELINE_PROMPT : "")
|
||||
const effectivePrompt = trimmedPrompt || shortcut?.defaultPrompt
|
||||
|
||||
if (!trimmedName || !finalPrompt) {
|
||||
toast.error(t("shortcuts.validation.required"))
|
||||
if (!trimmedName) {
|
||||
toast.error(t("shortcuts.validation.name_required"))
|
||||
return
|
||||
}
|
||||
|
||||
if (!effectivePrompt) {
|
||||
toast.error(t("shortcuts.validation.prompt_required"))
|
||||
return
|
||||
}
|
||||
if (displayTargets.length === 0) {
|
||||
|
|
@ -115,7 +107,8 @@ export const ShortcutModalContent = ({ shortcut, onSave, onCancel }: ShortcutMod
|
|||
|
||||
onSave({
|
||||
name: trimmedName,
|
||||
prompt: finalPrompt,
|
||||
prompt: trimmedPrompt,
|
||||
defaultPrompt: shortcut?.defaultPrompt,
|
||||
enabled,
|
||||
icon,
|
||||
displayTargets,
|
||||
|
|
@ -168,14 +161,30 @@ export const ShortcutModalContent = ({ shortcut, onSave, onCancel }: ShortcutMod
|
|||
</Popover>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-text">{t("shortcuts.prompt")}</Label>
|
||||
{isServer ? (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs text-text">{t("shortcuts.default_prompt.label")}</Label>
|
||||
<div className="select-text whitespace-pre-wrap rounded-md border border-border bg-material-thin px-3 py-2 text-xs leading-relaxed text-text">
|
||||
{shortcut?.defaultPrompt}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs text-text">
|
||||
{t(isServer ? "shortcuts.custom_prompt.title" : "shortcuts.prompt")}
|
||||
</Label>
|
||||
<TextArea
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
placeholder={t("shortcuts.prompt_placeholder")}
|
||||
placeholder={t(
|
||||
isServer ? "shortcuts.custom_prompt_placeholder" : "shortcuts.prompt_placeholder",
|
||||
)}
|
||||
className="min-h-[120px] resize-none py-2 text-sm"
|
||||
/>
|
||||
{isServer && (
|
||||
<p className="text-xs text-text-tertiary">{t("shortcuts.custom_prompt.help")}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect } from "react"
|
||||
|
||||
import { setServerConfigs } from "~/atoms/server-configs"
|
||||
import { syncServerShortcuts } from "~/atoms/settings/ai"
|
||||
import { useServerConfigsQuery } from "~/queries/server-configs"
|
||||
|
||||
export const ServerConfigsProvider = () => {
|
||||
|
|
@ -9,6 +10,7 @@ export const ServerConfigsProvider = () => {
|
|||
useEffect(() => {
|
||||
if (!serverConfigs) return
|
||||
setServerConfigs(serverConfigs)
|
||||
syncServerShortcuts(serverConfigs.AI_SHORTCUTS)
|
||||
}, [serverConfigs])
|
||||
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -175,6 +175,10 @@
|
|||
"shortcuts.context_menu.empty.entry": "No entry shortcuts configured yet",
|
||||
"shortcuts.context_menu.empty.list": "No shortcuts available for the list view yet",
|
||||
"shortcuts.create_first": "Create your first shortcut",
|
||||
"shortcuts.custom_prompt.help": "Leave blank to use the default prompt provided by the server.",
|
||||
"shortcuts.custom_prompt.title": "Custom prompt",
|
||||
"shortcuts.custom_prompt_placeholder": "Override the default prompt...",
|
||||
"shortcuts.default_prompt.label": "Default prompt",
|
||||
"shortcuts.deleted": "Shortcut deleted successfully",
|
||||
"shortcuts.empty.description": "Create custom AI shortcuts to quickly perform common tasks and get instant AI assistance.",
|
||||
"shortcuts.empty.title": "No Shortcuts Yet",
|
||||
|
|
@ -185,6 +189,7 @@
|
|||
"shortcuts.name_placeholder": "e.g. Summarize Article",
|
||||
"shortcuts.prompt": "Prompt",
|
||||
"shortcuts.prompt_placeholder": "Write a clear instruction for the AI...",
|
||||
"shortcuts.server_delete_disabled": "Server-provided shortcuts cannot be deleted",
|
||||
"shortcuts.targets.ai-page": "AI Page",
|
||||
"shortcuts.targets.entry": "Entry Details",
|
||||
"shortcuts.targets.help": "Choose scenarios to control where this shortcut appears.",
|
||||
|
|
@ -192,6 +197,8 @@
|
|||
"shortcuts.targets.list": "Timeline",
|
||||
"shortcuts.title": "AI Shortcuts",
|
||||
"shortcuts.updated": "Shortcut updated successfully",
|
||||
"shortcuts.validation.name_required": "Name is required",
|
||||
"shortcuts.validation.prompt_required": "A prompt is required",
|
||||
"shortcuts.validation.required": "Name and prompt are required",
|
||||
"shortcuts.validation.targets_required": "Select at least one display location",
|
||||
"summary_not_available": "Summary not available",
|
||||
|
|
|
|||
|
|
@ -140,6 +140,10 @@
|
|||
"shortcuts.context_menu.empty.entry": "エントリ詳細で使えるショートカットはまだありません",
|
||||
"shortcuts.context_menu.empty.list": "リスト表示で使えるショートカットはまだありません",
|
||||
"shortcuts.create_first": "最初のショートカットを作成",
|
||||
"shortcuts.custom_prompt.help": "空のままにすると、サーバー既定のプロンプトが使用されます。",
|
||||
"shortcuts.custom_prompt.title": "カスタムプロンプト",
|
||||
"shortcuts.custom_prompt_placeholder": "既定の指示を上書きします...",
|
||||
"shortcuts.default_prompt.label": "既定のプロンプト",
|
||||
"shortcuts.deleted": "ショートカットが正常に削除されました",
|
||||
"shortcuts.empty.description": "一般的なタスクを迅速に実行し、即座にAIアシスタンスを得るためのカスタムAIショートカットを作成します。",
|
||||
"shortcuts.empty.title": "ショートカットはまだありません",
|
||||
|
|
@ -150,12 +154,15 @@
|
|||
"shortcuts.name_placeholder": "例:記事を要約",
|
||||
"shortcuts.prompt": "プロンプト",
|
||||
"shortcuts.prompt_placeholder": "AI に対する明確な指示を書いてください...",
|
||||
"shortcuts.server_delete_disabled": "サーバー提供のショートカットは削除できません",
|
||||
"shortcuts.targets.entry": "エントリ詳細",
|
||||
"shortcuts.targets.help": "ショートカットを表示するシナリオを選択してください。",
|
||||
"shortcuts.targets.label": "表示場所",
|
||||
"shortcuts.targets.list": "タイムライン",
|
||||
"shortcuts.title": "AI ショートカット",
|
||||
"shortcuts.updated": "ショートカットが正常に更新されました",
|
||||
"shortcuts.validation.name_required": "名前は必須です",
|
||||
"shortcuts.validation.prompt_required": "プロンプトは必須です",
|
||||
"shortcuts.validation.required": "名前とプロンプトは必須です",
|
||||
"shortcuts.validation.targets_required": "少なくとも1つの表示場所を選択してください",
|
||||
"summary_not_available": "要約は利用できません",
|
||||
|
|
|
|||
|
|
@ -168,6 +168,10 @@
|
|||
"shortcuts.context_menu.empty.entry": "暂无可用于条目详情的快捷方式",
|
||||
"shortcuts.context_menu.empty.list": "列表视图暂无可用的快捷方式",
|
||||
"shortcuts.create_first": "创建您的第一个快捷方式",
|
||||
"shortcuts.custom_prompt.help": "留空则使用服务端提供的默认提示。",
|
||||
"shortcuts.custom_prompt.title": "自定义提示",
|
||||
"shortcuts.custom_prompt_placeholder": "覆盖默认提示内容...",
|
||||
"shortcuts.default_prompt.label": "默认提示",
|
||||
"shortcuts.deleted": "快捷方式删除成功",
|
||||
"shortcuts.empty.description": "创建自定义AI快捷方式,快速执行常见任务并获得即时AI协助。",
|
||||
"shortcuts.empty.title": "暂无快捷方式",
|
||||
|
|
@ -178,6 +182,7 @@
|
|||
"shortcuts.name_placeholder": "例如:总结文章",
|
||||
"shortcuts.prompt": "提示",
|
||||
"shortcuts.prompt_placeholder": "为AI编写清晰的指令...",
|
||||
"shortcuts.server_delete_disabled": "该快捷方式由服务端提供,无法删除",
|
||||
"shortcuts.targets.ai-page": "AI 页面",
|
||||
"shortcuts.targets.entry": "条目详情",
|
||||
"shortcuts.targets.help": "选择适用场景以控制此快捷方式的展示位置。",
|
||||
|
|
@ -185,6 +190,8 @@
|
|||
"shortcuts.targets.list": "时间线",
|
||||
"shortcuts.title": "AI 快捷方式",
|
||||
"shortcuts.updated": "快捷方式更新成功",
|
||||
"shortcuts.validation.name_required": "名称为必填项",
|
||||
"shortcuts.validation.prompt_required": "提示为必填项",
|
||||
"shortcuts.validation.required": "名称和提示为必填项",
|
||||
"shortcuts.validation.targets_required": "至少选择一个展示位置",
|
||||
"summary_not_available": "摘要不可用",
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@ import type { AISettings, GeneralSettings, IntegrationSettings, UISettings } fro
|
|||
|
||||
export const DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID = "default-summarize-timeline"
|
||||
|
||||
export const DEFAULT_SUMMARIZE_TIMELINE_PROMPT = `Generate a concise timeline summary based on entries within the current timeline within 24 hours.
|
||||
Recap the day in a casual, conversational tone instead of a rigid report.
|
||||
Open with a few relaxed sentences or light bullets that call out standout topics or shifts.
|
||||
Wrap up by casually noting any other interesting threads; if nothing else stands out, say so naturally.`
|
||||
|
||||
export const defaultGeneralSettings: GeneralSettings = {
|
||||
// App
|
||||
appLaunchOnStartup: false,
|
||||
|
|
@ -165,23 +160,7 @@ export const defaultIntegrationSettings: IntegrationSettings = {
|
|||
|
||||
export const defaultAISettings: AISettings = {
|
||||
personalizePrompt: "",
|
||||
shortcuts: [
|
||||
{
|
||||
name: "Summarize",
|
||||
prompt: DEFAULT_SUMMARIZE_TIMELINE_PROMPT,
|
||||
enabled: true,
|
||||
displayTargets: ["list"],
|
||||
id: DEFAULT_SUMMARIZE_TIMELINE_SHORTCUT_ID,
|
||||
},
|
||||
{
|
||||
name: "Analyze",
|
||||
prompt:
|
||||
"Analyze this content, looking for bias, patterns, trends, connections. Consider the author, the source. Research to fact check, if it seems beneficial. Research the broader setting. Try and think about what someone would want to know here.\n\nIf no content has been provided, ask about the relevant subject matter.",
|
||||
enabled: true,
|
||||
displayTargets: ["entry"],
|
||||
id: "default-analyze",
|
||||
},
|
||||
],
|
||||
shortcuts: [],
|
||||
|
||||
// MCP Services
|
||||
mcpEnabled: false,
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ export interface AIShortcut {
|
|||
id: string
|
||||
name: string
|
||||
prompt: string
|
||||
defaultPrompt?: string
|
||||
enabled: boolean
|
||||
icon?: string
|
||||
hotkey?: string
|
||||
|
|
|
|||
Loading…
Reference in New Issue