Skip to content

Commit

Permalink
finished ai settings page
Browse files Browse the repository at this point in the history
  • Loading branch information
eric2788 committed Oct 19, 2024
1 parent c912040 commit 9c81a72
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 3 deletions.
1 change: 0 additions & 1 deletion src/options/components/Hints.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ function Hints(props: HintsProps): JSX.Element {
<Fragment>
<Typography
variant="small"

className="mt-2 flex items-center gap-1 font-normal dark:text-gray-400"
>
<svg
Expand Down
3 changes: 2 additions & 1 deletion src/options/components/Selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type SelectorProps<T> = {
label: string
disabled?: boolean
options: SelectorOption<T>[]
className?: string
}


Expand All @@ -32,7 +33,7 @@ function Selector<T = any>(props: SelectorProps<T>): JSX.Element {
}

return (
<section className={`relative block text-left`} data-testid={props['data-testid']}>
<section className={`relative block text-left ${props.className || ''}`} data-testid={props['data-testid']}>
<label className="text-sm ml-1 font-medium text-gray-900 dark:text-white">{props.label}</label>
<div ref={dropdownRef} className={`mt-2 ${props.disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`} onClick={() => !props.disabled && setOpen(!isOpen)}>
<div className={`inline-flex justify-between h-full w-full rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 text-sm font-medium text-gray-700 dark:text-white ${props.disabled ? 'opacity-50 bg-transparent' : 'bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-900'} focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 dark:focus:ring-gray-500`}>
Expand Down
136 changes: 136 additions & 0 deletions src/options/features/jimaku/components/AIFragment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { Button, Input, List, Tooltip, Typography } from "@material-tailwind/react"
import { type ChangeEvent, Fragment, useState } from "react"
import { toast } from "sonner/dist"
import type { StateProxy } from "~hooks/binding"
import type { LLMProviders, LLMTypes } from "~llms"
import createLLMProvider from "~llms"
import ExperienmentFeatureIcon from "~options/components/ExperientmentFeatureIcon"
import Selector from "~options/components/Selector"
import SwitchListItem from "~options/components/SwitchListItem"



export type AISchema = {
enabled: boolean
provider: LLMTypes

// cloudflare settings
accountId?: string
apiToken?: string
}


export const aiDefaultSettings: Readonly<AISchema> = {
enabled: false,
provider: 'worker'
}


function AIFragment({ state, useHandler }: StateProxy<AISchema>): JSX.Element {

const [validating, setValidating] = useState(false)

const handler = useHandler<ChangeEvent<HTMLInputElement>, string>((e) => e.target.value)
const checker = useHandler<ChangeEvent<HTMLInputElement>, boolean>((e) => e.target.checked)

const onValidate = async () => {
setValidating(true)
try {
let provider: LLMProviders;
if (state.provider === 'qwen') {
provider = await createLLMProvider(state.provider, state.accountId, state.apiToken)
} else {
provider = await createLLMProvider(state.provider)
}
await provider.validate()
toast.success('配置可用!')
} catch (e) {
toast.error('配置不可用: ' + e.message)
} finally {
setValidating(false)
}
}

return (
<Fragment>
<List className="col-span-2 border border-[#808080] rounded-md">
<SwitchListItem
data-testid="ai-enabled"
label="启用同传字幕AI总结"
hint="此功能将采用通义大模型对同传字幕进行总结"
value={state.enabled}
onChange={checker('enabled')}
marker={<ExperienmentFeatureIcon />}
/>
</List>
{state.enabled && (
<Fragment>
<Selector<typeof state.provider>
className="col-span-2"
data-testid="ai-provider"
label="AI 提供商"
value={state.provider}
onChange={e => state.provider = e}
options={[
{ label: 'Cloudflare AI', value: 'qwen' },
{ label: '有限度服务器', value: 'worker' },
{ label: 'Chrome 浏览器内置 AI', value: 'nano' }
]}
/>
{state.provider === 'qwen' && (
<Fragment>
<Typography
className="flex items-center gap-1 font-normal dark:text-gray-200 col-span-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
className="-mt-px h-6 w-6"
>
<path
fillRule="evenodd"
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 01.67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 11-.671-1.34l.041-.022zM12 9a.75.75 0 100-1.5.75.75 0 000 1.5z"
clipRule="evenodd"
/>
</svg>
<Typography className="underline" as="a" href="https://linux.do/t/topic/34037" target="_blank">点击此处</Typography>
查看如何获得 Cloudflare API Token 和 Account ID
</Typography>
<Input
data-testid="cf-account-id"
crossOrigin="anonymous"
variant="static"
required
label="Cloudflare Account ID"
value={state.accountId}
onChange={handler('accountId')}
/>
<Input
data-testid="cf-api-token"
crossOrigin="anonymous"
variant="static"
required
label="Cloudflare API Token"
value={state.apiToken}
onChange={handler('apiToken')}
/>
</Fragment>
)}
</Fragment>
)}
<div className="col-span-2">
<Button disabled={validating} onClick={onValidate} color="blue" size="lg" className="group flex items-center justify-center gap-3 text-[1rem] hover:shadow-lg">
验证是否可用
<Tooltip content="检查你目前的配置是否可用。若不可用,则无法启用AI总结功能。" placement="top-end">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-6">
<path strokeLinecap="round" strokeLinejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
</svg>
</Tooltip>
</Button>
</div>
</Fragment>
)
}

export default AIFragment
10 changes: 9 additions & 1 deletion src/options/features/jimaku/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ButtonFragment, { buttonDefaultSettings, type ButtonSchema } from "./comp
import DanmakuZone, { danmakuDefaultSettings, type DanmakuSchema } from "./components/DanmakuFragment"
import JimakuZone, { jimakuDefaultSettings, type JimakuSchema } from "./components/JimakuFragment"
import ListingFragment, { listingDefaultSettings, type ListingSchema } from "./components/ListingFragment"
import AIFragment, { aiDefaultSettings, type AISchema } from "./components/AIFragment"


export const title: string = '同传弹幕过滤'
Expand All @@ -28,6 +29,7 @@ export type FeatureSettingSchema = {
danmakuZone: DanmakuSchema,
buttonZone: ButtonSchema,
listingZone: ListingSchema
aiZone: AISchema
}

export const defaultSettings: Readonly<FeatureSettingSchema> = {
Expand All @@ -39,7 +41,8 @@ export const defaultSettings: Readonly<FeatureSettingSchema> = {
jimakuZone: jimakuDefaultSettings,
danmakuZone: danmakuDefaultSettings,
buttonZone: buttonDefaultSettings,
listingZone: listingDefaultSettings
listingZone: listingDefaultSettings,
aiZone: aiDefaultSettings
}

const zones: {
Expand All @@ -66,6 +69,11 @@ const zones: {
Zone: ListingFragment,
title: '同传名单设定',
key: 'listingZone'
},
{
Zone: AIFragment,
title: 'AI 设定',
key: 'aiZone'
}
]

Expand Down

0 comments on commit 9c81a72

Please sign in to comment.