dsh-ai-courses 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +77 -15
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -680,6 +680,27 @@ const AIC_COURSES_CORE = (function () {
|
|
|
680
680
|
return DEFAULT_LOCALE
|
|
681
681
|
}
|
|
682
682
|
|
|
683
|
+
/**
|
|
684
|
+
* 动态 `t(key, vars)`:**每次调用现读**宿主语言。
|
|
685
|
+
*
|
|
686
|
+
* 为什么不能在 apply 时 `createT(detectLocale(...))` 一次定死:
|
|
687
|
+
* DSH 的 locale 插件在启动后才把 `<html lang>` 从服务端默认的 `en` 改成用户设置
|
|
688
|
+
* (profile patch 里 `locale.preference: zh` 的情况)。apply 时读到的还是 `en`,
|
|
689
|
+
* 于是整个面板一直显示英文。这里按 locale 缓存翻译器,语言变了下一次渲染就切过来。
|
|
690
|
+
*/
|
|
691
|
+
function createLiveT (doc, nav) {
|
|
692
|
+
const cache = new Map()
|
|
693
|
+
return function t (key, vars) {
|
|
694
|
+
const locale = detectLocale(doc, nav)
|
|
695
|
+
let tr = cache.get(locale)
|
|
696
|
+
if (!tr) {
|
|
697
|
+
tr = createT(locale)
|
|
698
|
+
cache.set(locale, tr)
|
|
699
|
+
}
|
|
700
|
+
return tr(key, vars)
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
683
704
|
/* --------------------------------------------------- 编号(绝不解析 id) */
|
|
684
705
|
/** 章节的人类编号:由 `order` 派生(05 §4 / 06 §4.2)。 */
|
|
685
706
|
function chapterNo (ch) {
|
|
@@ -1172,6 +1193,7 @@ const AIC_COURSES_CORE = (function () {
|
|
|
1172
1193
|
LOCALES,
|
|
1173
1194
|
DEFAULT_LOCALE,
|
|
1174
1195
|
createT,
|
|
1196
|
+
createLiveT,
|
|
1175
1197
|
detectLocale,
|
|
1176
1198
|
genSpec,
|
|
1177
1199
|
learnSpec,
|
|
@@ -4458,9 +4480,28 @@ function PageState (props) {
|
|
|
4458
4480
|
)
|
|
4459
4481
|
}
|
|
4460
4482
|
|
|
4483
|
+
/**
|
|
4484
|
+
* `<html lang>` 变化时强制重渲染。
|
|
4485
|
+
*
|
|
4486
|
+
* DSH 的 locale 插件在启动后才把服务端默认的 `en` 改成用户设置;`t` 本身是
|
|
4487
|
+
* `createLiveT`(每次调用现读),但组件需要一次 re-render 才会重新取词。
|
|
4488
|
+
*/
|
|
4489
|
+
function useLocaleRefresh () {
|
|
4490
|
+
const [, setTick] = React.useState(0)
|
|
4491
|
+
React.useEffect(() => {
|
|
4492
|
+
if (typeof document === 'undefined' || typeof MutationObserver !== 'function') return undefined
|
|
4493
|
+
const el = document.documentElement
|
|
4494
|
+
if (!el || typeof el.getAttribute !== 'function') return undefined
|
|
4495
|
+
const obs = new MutationObserver(() => setTick((n) => n + 1))
|
|
4496
|
+
obs.observe(el, { attributes: true, attributeFilter: ['lang'] })
|
|
4497
|
+
return () => obs.disconnect()
|
|
4498
|
+
}, [])
|
|
4499
|
+
}
|
|
4500
|
+
|
|
4461
4501
|
function AicCoursesApp (props) {
|
|
4462
4502
|
const t = props.t
|
|
4463
4503
|
const core = AIC_COURSES_CORE
|
|
4504
|
+
useLocaleRefresh()
|
|
4464
4505
|
const [view, setView] = React.useState('library')
|
|
4465
4506
|
const [courseId, setCourseId] = React.useState(null)
|
|
4466
4507
|
const [kpId, setKpId] = React.useState(null)
|
|
@@ -4697,7 +4738,9 @@ function aicCoursesApply (ctx) {
|
|
|
4697
4738
|
|
|
4698
4739
|
const doc = typeof document === 'undefined' ? null : document
|
|
4699
4740
|
const nav = typeof navigator === 'undefined' ? null : navigator
|
|
4700
|
-
|
|
4741
|
+
/* 动态语言:`<html lang>` 由 DSH locale 插件在启动后改成用户设置,
|
|
4742
|
+
不能在这里一次性 detectLocale 后锁死(否则中文设置下一直显示英文)。 */
|
|
4743
|
+
const t = AIC_COURSES_CORE.createLiveT(doc, nav)
|
|
4701
4744
|
|
|
4702
4745
|
/* 样式:构建期内联(宿主不会自动加载包内 lib/styles.css —— 见 doc/03 §6 的定案),
|
|
4703
4746
|
挂一个 <style> 并在停用/卸载时移除;不用 innerHTML。 */
|
|
@@ -4715,19 +4758,38 @@ function aicCoursesApply (ctx) {
|
|
|
4715
4758
|
|
|
4716
4759
|
const disposers = []
|
|
4717
4760
|
|
|
4718
|
-
/*
|
|
4719
|
-
`
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4761
|
+
/* 侧边栏面板图标 / 主面板:**等槽声明后再注册**。
|
|
4762
|
+
`sidebar.panellist` 由 `@deepseek-ai/dsh-client-ui-sidebar` 声明;在 `dsh web` 里这个声明
|
|
4763
|
+
发生在 sidebar 首次渲染时,比插件 apply 晚 —— 直接 `slots.register` 会抛
|
|
4764
|
+
「slot "sidebar.panellist" is not declared」,整个 client entry 被标 failed(真机踩到)。
|
|
4765
|
+
用 `slots.inject` 等声明;老宿主没有 inject 时退回直接注册。 */
|
|
4766
|
+
if (slots && typeof slots.inject === 'function') {
|
|
4767
|
+
/* `id` 必须与 `main` 的 `key` 是同一字符串(宿主按 list id 寻址 main 面板,E-8);
|
|
4768
|
+
`label` 必须是函数。 */
|
|
4769
|
+
disposers.push(
|
|
4770
|
+
slots.inject('sidebar.panellist', () => slots.register(
|
|
4771
|
+
{ name: 'sidebar.panellist', id: 'ai-courses', order: 30, label: () => t('panel.title') },
|
|
4772
|
+
(props) => h(CoursePanelIcon, props),
|
|
4773
|
+
)),
|
|
4774
|
+
)
|
|
4775
|
+
/* 主面板。key = 'ai-courses'(**不能**用 'course':那是 dsh-course-plugin 的格位) */
|
|
4776
|
+
disposers.push(
|
|
4777
|
+
slots.inject('main', () => slots.register(
|
|
4778
|
+
{ name: 'main', key: 'ai-courses' },
|
|
4779
|
+
() => h(AicCoursesApp, { t: t, ctx: ctx }),
|
|
4780
|
+
)),
|
|
4781
|
+
)
|
|
4782
|
+
} else {
|
|
4783
|
+
disposers.push(
|
|
4784
|
+
slots.register(
|
|
4785
|
+
{ name: 'sidebar.panellist', id: 'ai-courses', order: 30, label: () => t('panel.title') },
|
|
4786
|
+
(props) => h(CoursePanelIcon, props),
|
|
4787
|
+
),
|
|
4788
|
+
)
|
|
4789
|
+
disposers.push(
|
|
4790
|
+
slots.register({ name: 'main', key: 'ai-courses' }, () => h(AicCoursesApp, { t: t, ctx: ctx })),
|
|
4791
|
+
)
|
|
4792
|
+
}
|
|
4731
4793
|
|
|
4732
4794
|
/* 主会话里的 `/ai-course` 实时状态卡片(conversation.chat.commandview,见 55-status-card.js):
|
|
4733
4795
|
宿主 ui-chat 声明该槽,用 inject 等声明出现再注册(直接 register 会抛「未声明」)。 */
|
|
@@ -4748,7 +4810,7 @@ function aicCoursesApply (ctx) {
|
|
|
4748
4810
|
if (styleEl && styleEl.parentNode) styleEl.parentNode.removeChild(styleEl)
|
|
4749
4811
|
})
|
|
4750
4812
|
|
|
4751
|
-
console.log('[ai-courses] 客户端已就绪(sidebar
|
|
4813
|
+
console.log('[ai-courses] 客户端已就绪(sidebar/main 等槽声明后注册;主会话槽已注册)')
|
|
4752
4814
|
}
|
|
4753
4815
|
|
|
4754
4816
|
/* ==================== src/client/99-shell-tail.js ==================== */
|