@huaqiu/dsh-tool-schematic-gen 0.3.13 → 0.3.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huaqiu/dsh-tool-schematic-gen",
3
- "version": "0.3.13",
3
+ "version": "0.3.14",
4
4
  "type": "module",
5
5
  "main": "./lib/index.mjs",
6
6
  "types": "./lib/index.d.mts",
@@ -30,8 +30,8 @@
30
30
  "@deepseek-ai/cordis": "^4.0.1",
31
31
  "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.0",
32
32
  "@deepseek-ai/dsh-tools": "^0.1.0-rc.0",
33
- "@huaqiu/dsh-auth": "^0.3.13",
34
- "@huaqiu/dsh-artifacts": "^0.3.13",
33
+ "@huaqiu/dsh-auth": "^0.3.14",
34
+ "@huaqiu/dsh-artifacts": "^0.3.14",
35
35
  "react": "^18"
36
36
  },
37
37
  "devDependencies": {
@@ -39,9 +39,9 @@
39
39
  "@huaqiu/kicad-sexpr-parser": "^0.1.1",
40
40
  "@types/react": "^18.3.31",
41
41
  "react": "^18.3.1",
42
- "@huaqiu/dsh-artifacts": "0.3.13",
43
- "@huaqiu/dsh-tool-uncollapse": "0.3.13",
44
- "@huaqiu/dsh-auth": "0.3.13"
42
+ "@huaqiu/dsh-auth": "0.3.14",
43
+ "@huaqiu/dsh-tool-uncollapse": "0.3.14",
44
+ "@huaqiu/dsh-artifacts": "0.3.14"
45
45
  },
46
46
  "files": [
47
47
  "lib",
@@ -52,7 +52,7 @@
52
52
  "access": "public"
53
53
  },
54
54
  "dependencies": {
55
- "@huaqiu/dsh-plugin-log": "0.3.13"
55
+ "@huaqiu/dsh-plugin-log": "0.3.14"
56
56
  },
57
57
  "scripts": {
58
58
  "typecheck": "tsc --noEmit",
@@ -33,6 +33,22 @@ export interface AuthStateLike {
33
33
  nickname?: string
34
34
  }
35
35
 
36
+ /**
37
+ * Structural view of the `huaqiuAuth` CLIENT service (declared structurally,
38
+ * never imported — each package must remain independently installable).
39
+ * `login()` in HQ Edge host mode triggers the EDA login dialog through
40
+ * hq-edge (`POST /api/v1/auth/login` → EDA `TriggerLoginDialog`) instead of
41
+ * the auth.eda.cn iframe; `isHostMode()` tells the card which surface to show.
42
+ */
43
+ export interface AuthClientLike {
44
+ auth?: {
45
+ isAuthenticated(): boolean
46
+ isHostMode?(): boolean
47
+ login?(options?: { lang?: string; theme?: string }): Promise<void>
48
+ onAuthStateChanged(listener: (info: { nickname?: string } | null) => void): () => void
49
+ }
50
+ }
51
+
36
52
  export type PromptSender = (sessionId: string | undefined, message: string) => Promise<unknown>
37
53
 
38
54
  const TOOL_SCHEMATIC = 'generate_schematic_from_description'
@@ -53,6 +69,13 @@ export interface GenHitProps {
53
69
  inspect?: () => void
54
70
  authState?: AuthStateLike
55
71
  sendPrompt?: PromptSender
72
+ /**
73
+ * Lazy accessor for the `huaqiuAuth` client service (auth plugin browser
74
+ * half). Absent/undefined in a broken install — the needs_auth card then
75
+ * falls back to the embedded auth.eda.cn iframe. Lazy (not captured once)
76
+ * so plugin load order resolves correctly at click time.
77
+ */
78
+ getAuth?: () => AuthClientLike | undefined
56
79
  /**
57
80
  * Lazy accessor for the host's `hqEdge` service (edge-bridge browser half).
58
81
  * Absent/undefined in standalone DSH — the Place button is then hidden.
@@ -186,9 +209,16 @@ function PreviewStage({ payload, t }: { payload: PreviewPayload; t: Translate })
186
209
 
187
210
  // ── needs_auth login card ───────────────────────────────────────────────────
188
211
 
189
- function LoginCard({ toolName, authState, t }: { toolName: string; authState?: AuthStateLike; t: Translate }): ReactElement {
212
+ function LoginCard({ toolName, authState, t, getAuth }: { toolName: string; authState?: AuthStateLike; t: Translate; getAuth?: () => AuthClientLike | undefined }): ReactElement {
190
213
  const dark = useTheme()
191
214
  const locale = useLocale()
215
+ const authClient = getAuth?.()?.auth
216
+ // HQ Edge host mode: EDA owns the credential — login must ask EDA to open
217
+ // its own TriggerLoginDialog (hq-edge POST /api/v1/auth/login), not the
218
+ // auth.eda.cn iframe (a browser-pushed token is ignored by the STRICT host
219
+ // resolver). Standalone DSH keeps the inline iframe.
220
+ const hostMode = authClient?.isHostMode?.() ?? false
221
+
192
222
  // FILL mode (`fill=full`): this card IS the surface, so let the embed paint
193
223
  // it edge-to-edge with its own `bg-background`. Without it the embed's
194
224
  // `grid-rows-[20px_1fr_20px]` wrapper leaves two transparent strips above
@@ -202,6 +232,41 @@ function LoginCard({ toolName, authState, t }: { toolName: string; authState?: A
202
232
  // its URL params once on mount), silently ignoring the new `fill`/`theme`.
203
233
  const remountKey = `${locale}|${dark ? 'd' : 'l'}`
204
234
 
235
+ const statusLine = (
236
+ <p className="hq-sch__login-status" style={{ color: authState?.authenticated ? '#1677ff' : '#d4380d' }}>
237
+ {authState?.authenticated
238
+ ? t('card.auth.loggedIn', {
239
+ nickname: authState.nickname ? t('card.nicknameSep', { nickname: authState.nickname }) : '',
240
+ })
241
+ : t('card.auth.loggedOut')}
242
+ </p>
243
+ )
244
+
245
+ if (hostMode) {
246
+ return (
247
+ <div className="hq-sch">
248
+ <div className="hq-sch__header">
249
+ <span className="hq-sch__icon">⇶</span>
250
+ <span className="hq-sch__title">{t('card.auth.title')}</span>
251
+ </div>
252
+ <div className="hq-sch__login">
253
+ <p className="hq-sch__login-desc">{t('card.auth.descHost', { tool: toolName })}</p>
254
+ {statusLine}
255
+ <button
256
+ type="button"
257
+ className="hq-sch__login-btn"
258
+ onClick={() => {
259
+ void authClient?.login?.({ lang: locale, theme: dark ? 'dark' : 'light' })
260
+ .catch(() => { /* login cancelled / dialog failed — card keeps showing the button */ })
261
+ }}
262
+ >
263
+ {t('card.auth.loginBtn')}
264
+ </button>
265
+ </div>
266
+ </div>
267
+ )
268
+ }
269
+
205
270
  return (
206
271
  <div className="hq-sch">
207
272
  <div className="hq-sch__header">
@@ -210,13 +275,7 @@ function LoginCard({ toolName, authState, t }: { toolName: string; authState?: A
210
275
  </div>
211
276
  <div className="hq-sch__login">
212
277
  <p className="hq-sch__login-desc">{t('card.auth.desc', { tool: toolName })}</p>
213
- <p className="hq-sch__login-status" style={{ color: authState?.authenticated ? '#1677ff' : '#d4380d' }}>
214
- {authState?.authenticated
215
- ? t('card.auth.loggedIn', {
216
- nickname: authState.nickname ? t('card.nicknameSep', { nickname: authState.nickname }) : '',
217
- })
218
- : t('card.auth.loggedOut')}
219
- </p>
278
+ {statusLine}
220
279
  <iframe
221
280
  key={remountKey}
222
281
  src={src}
@@ -364,7 +423,7 @@ export const GenHit = memo(function GenHit(props: GenHitProps): ReactElement {
364
423
 
365
424
  // needs_auth
366
425
  if (state.phase === 'needs_auth') {
367
- return <LoginCard toolName={props.toolName} authState={props.authState} t={t} />
426
+ return <LoginCard toolName={props.toolName} authState={props.authState} t={t} getAuth={props.getAuth} />
368
427
  }
369
428
 
370
429
  const headerKind = result?.kind ?? kindOf(props.toolName)
@@ -59,6 +59,8 @@ const ZH = {
59
59
  'card.auth.desc': '工具「{tool}」需要登录华秋 EDA AI 账号才能继续。请在下方的登录框完成登录(或点击左侧「华秋EDA AI登录」按钮);登录完成后,回复助手「已登录,请重试」,助手会自动重新调用该工具。',
60
60
  'card.auth.loggedIn': '✓ 已登录{nickname}—— 现在可以回复助手「已登录,请重试」,助手会重新调用工具。',
61
61
  'card.auth.loggedOut': '未登录 —— 请在上方登录华秋 EDA AI(eda.cn)账号,或点击左侧「华秋EDA AI 登录」按钮;登录完成后让助手重试。',
62
+ 'card.auth.descHost': '工具「{tool}」需要登录华秋 EDA AI 账号才能继续。点击下方按钮后,EDA(KiCad)将弹出登录窗口,请在弹出的窗口中完成登录;登录完成后回复助手「已登录,请重试」,助手会自动重新调用该工具。',
63
+ 'card.auth.loginBtn': '登录华秋 EDA AI',
62
64
  // Substituted into `{nickname}` by `card.auth.loggedIn`. zh uses a
63
65
  // full-width colon, en a half-width one plus a space — hardcoding ':'
64
66
  // made the English card read "Logged in:John".
@@ -116,6 +118,8 @@ const EN: Record<CopyKey, string> = {
116
118
  'card.auth.desc': 'Tool "{tool}" requires a Huaqiu EDA AI login. Complete the login below (or use the 华秋EDA AI sidebar button); then reply "I have logged in, please retry" so the assistant can retry the tool.',
117
119
  'card.auth.loggedIn': '✓ Logged in{nickname} — reply "I have logged in, please retry" and the assistant will retry.',
118
120
  'card.auth.loggedOut': 'Not logged in — complete the login above, or use the 华秋EDA AI sidebar button.',
121
+ 'card.auth.descHost': 'Tool "{tool}" requires a Huaqiu EDA AI account. Click the button below — EDA (KiCad) will open its login dialog. Complete the login there, then reply "I have logged in, please retry" so the assistant can retry the tool.',
122
+ 'card.auth.loginBtn': 'Sign in to Huaqiu EDA AI',
119
123
  'card.nicknameSep': ': {nickname}',
120
124
  }
121
125
 
@@ -159,6 +159,7 @@ export function apply(ctx: ClientContext): () => void {
159
159
  sendPrompt,
160
160
  authState: useAuthState(),
161
161
  getHqEdge,
162
+ getAuth: () => authService,
162
163
  })
163
164
 
164
165
  for (const toolName of TOOLVIEW_KEYS) {
@@ -181,6 +181,8 @@ const CSS = `
181
181
  .hq-sch__login-desc { margin: 0 0 10px; font: var(--dsw-font-xxs-12, 12px/1.4 system-ui, sans-serif); color: var(--dsw-alias-label-secondary, currentColor); line-height: 1.5; }
182
182
  .hq-sch__login-status { margin: 0 0 10px; font: var(--dsw-font-xxs-12, 12px/1.4 system-ui, sans-serif); line-height: 1.5; }
183
183
  .hq-sch__login-iframe { width: 100%; height: ${LOGIN_IFRAME_HEIGHT}px; border: 0; border-radius: 8px; display: block; }
184
+ .hq-sch__login-btn { display: inline-flex; align-items: center; gap: 4px; border: 1px solid var(--dsw-alias-border-l1, currentColor); border-radius: 6px; padding: 6px 16px; background: var(--dsw-alias-interactive-bg-hover, rgba(127,127,127,0.08)); color: var(--dsw-alias-label-primary, currentColor); font: var(--dsw-font-xxs-12, 12px/1.4 system-ui, sans-serif); cursor: pointer; }
185
+ .hq-sch__login-btn:hover { filter: brightness(1.08); }
184
186
 
185
187
  /* ── live call stack (long-running generations) ───────────────────────────── */
186
188
  .hq-sch__progress { display: flex; align-items: center; gap: 8px; padding: 0 12px 8px; font: var(--dsw-font-xxs-12, 12px/1.4 system-ui, sans-serif); color: var(--dsw-alias-label-secondary, currentColor); }