瀏覽代碼

fix(client): share the shield contour and describe the menu glyphs by shape

The permission selector and IconShieldOutline16 now read one exported
SHIELD_OUTLINE_PATH; the icon-set header no longer claims every glyph is
filled; the three menu glyph docs name what they draw and where they are
used; the compact ring matches the set's extent; the icon pin asserts the
three names.
creatixchu 1 月之前
父節點
當前提交
c75e100893

+ 9 - 8
packages/client/ui-conversation/src/client/skeleton/PermissionSelect.tsx

@@ -2,7 +2,9 @@ import { useEffect, useState } from 'react'
 import type { ReactNode } from 'react'
 import clsx from 'clsx'
 import type { PermissionSelect as PermissionSelectValue } from '@deepseek-ai/dsh-permission-presets/client'
-import { IconChevronDownOutline14, Menu, RiskConfirmation } from '@deepseek-ai/dsh-client-ui-primitives'
+import {
+  IconChevronDownOutline14, Menu, RiskConfirmation, SHIELD_OUTLINE_PATH, SHIELD_OUTLINE_STROKE,
+} from '@deepseek-ai/dsh-client-ui-primitives'
 import type { MenuEntry } from '@deepseek-ai/dsh-client-ui-primitives'
 import type { ComposerBarProps } from '../contract/slots.ts'
 import { en } from '../locales.ts'
@@ -10,16 +12,15 @@ import css from './PermissionSelect.module.css'
 
 const FULL_ACCESS = 'danger-full-access'
 
-/* Shield glyphs (design set 1556): check = read-only, pencil = workspace
-   write, exclamation = full access. currentColor so the trigger and menu
-   rows tint them with their own text color. */
-
-const shieldOutline = 'M8.20554 0.899994L14.7901 3.36857V7.01026C14.7901 12 11.0466 14.2103 8.20554 15.3C5.36446 14.2103 1.62012 12 1.62012 7.01026V3.36857L8.20554 0.899994Z'
+/* Shield glyphs (design set 1556) over the ui-primitives shield contour:
+   check = read-only, pencil = workspace write, exclamation = full access.
+   currentColor so the trigger and menu rows tint them with their own text
+   color. */
 
 const permissionGlyphs = new Map<string, ReactNode>([
   ['read-only', (
     <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden>
-      <path d={shieldOutline} stroke="currentColor" strokeWidth="1.31831" strokeLinejoin="round" />
+      <path d={SHIELD_OUTLINE_PATH} stroke="currentColor" strokeWidth={SHIELD_OUTLINE_STROKE} strokeLinejoin="round" />
       <path d="M12.1654 5.7552L8.9447 9.41475C8.73044 9.65816 8.53628 9.8804 8.35774 10.0423C8.1713 10.2114 7.94235 10.3717 7.64016 10.4254C7.48207 10.4535 7.32 10.4552 7.16151 10.4294C6.85843 10.3801 6.62728 10.2223 6.43836 10.0559C6.25752 9.89653 6.06037 9.67732 5.84264 9.43705L4.72925 8.20897L5.63557 7.38707L6.74897 8.61594C6.98603 8.87755 7.12974 9.03533 7.24673 9.13839C7.31033 9.19443 7.34485 9.21476 7.35823 9.22122C7.38068 9.22484 7.40352 9.22515 7.42593 9.22122C7.40522 9.22502 7.42893 9.23294 7.53583 9.136C7.65132 9.03126 7.79316 8.87139 8.02643 8.60638L11.2479 4.94763L12.1654 5.7552Z" fill="currentColor" />
     </svg>
   )],
@@ -34,7 +35,7 @@ const permissionGlyphs = new Map<string, ReactNode>([
   )],
   [FULL_ACCESS, (
     <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden>
-      <path d={shieldOutline} stroke="currentColor" strokeWidth="1.31831" strokeLinejoin="round" />
+      <path d={SHIELD_OUTLINE_PATH} stroke="currentColor" strokeWidth={SHIELD_OUTLINE_STROKE} strokeLinejoin="round" />
       <path d="M9.10094 4.5V8.75939H7.59888V4.5H9.10094Z" fill="currentColor" />
       <path d="M9.10094 9.8114V11.5H7.59888V9.8114H9.10094Z" fill="currentColor" />
     </svg>

+ 30 - 14
packages/client/ui-primitives/src/icons/index.tsx

@@ -1,7 +1,8 @@
 /**
- * ic_ds_* icon set for the dsh web UI. All glyphs render fill="currentColor"
- * and take {size, className}. Batch A mirrors the deepsuite icon library
- * (same figma source); batch B glyphs are harness-only figma extracts.
+ * ic_ds_* icon set for the dsh web UI. All glyphs ride currentColor (filled
+ * or stroked) and take {size, className}. Batch A mirrors the deepsuite icon
+ * library (same figma source); batch B glyphs are harness-only figma
+ * extracts; the product glyphs at the end are hand-authored.
  */
 import type { IconProps } from './props.ts'
 
@@ -935,7 +936,12 @@ export const IconArchiveOutline20 = ({ size = 20, className }: IconProps) => (
   </svg>
 )
 
-/** plan_outline_14 (design doc for #3567: the composer menu's Plan row glyph; drawn on a 14 grid) */
+/**
+ * Plan row glyph of the composer menu (#3567): a document with three ruled
+ * lines and a pencil over its corner. Drawn on a 14 grid whose content spans
+ * 0.5..13.5, so at the menu's 16px render it fills the same extent as the
+ * 16-grid glyphs beside it.
+ */
 export const IconPlanOutline14 = ({ size = 14, className }: IconProps) => (
   <svg width={size} height={size} className={className} viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
     <path d="M9.56143 3.14672V4.24774H3.94716V3.14672H9.56143Z" fill="currentColor" />
@@ -953,22 +959,32 @@ export const IconPlanOutline14 = ({ size = 14, className }: IconProps) => (
   </svg>
 )
 
-/** compact_outline_16 (the composer's context-usage ring as a static glyph: a quiet track with one filled arc) */
+/**
+ * Compact row glyph of the composer menu: the composer's context-usage ring
+ * (ContextMeter) frozen at its resting look — a quiet track with one filled
+ * quarter arc. A restyle of the live ring revisits this copy.
+ */
 export const IconCompactOutline16 = ({ size = 16, className }: IconProps) => (
   <svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <circle cx="8" cy="8" r="6" stroke="currentColor" strokeWidth="1.6" opacity="0.35" />
-    <path d="M8 2A6 6 0 0 1 14 8" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
+    <circle cx="8" cy="8" r="6.4" stroke="currentColor" strokeWidth="1.6" opacity="0.35" />
+    <path d="M8 1.6A6.4 6.4 0 0 1 14.4 8" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
   </svg>
 )
 
-/** shield_outline_16 (design set 1556: the permission shield contour without a mode mark) */
+/**
+ * The permission shield contour on the 16 grid (design set 1556), stroked at
+ * {@link SHIELD_OUTLINE_STROKE}. The composer's permission selector composes
+ * its mode marks (check, pencil, exclamation) over this same path inside one
+ * svg, so the geometry lives here once.
+ */
+export const SHIELD_OUTLINE_PATH = 'M8.20554 0.899994L14.7901 3.36857V7.01026C14.7901 12 11.0466 14.2103 8.20554 15.3C5.36446 14.2103 1.62012 12 1.62012 7.01026V3.36857L8.20554 0.899994Z'
+
+/** Stroke width of {@link SHIELD_OUTLINE_PATH}. */
+export const SHIELD_OUTLINE_STROKE = '1.31831'
+
+/** Permission row glyph of the composer menu: the shield contour alone, without a mode mark. */
 export const IconShieldOutline16 = ({ size = 16, className }: IconProps) => (
   <svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path
-      d="M8.20554 0.899994L14.7901 3.36857V7.01026C14.7901 12 11.0466 14.2103 8.20554 15.3C5.36446 14.2103 1.62012 12 1.62012 7.01026V3.36857L8.20554 0.899994Z"
-      stroke="currentColor"
-      strokeWidth="1.31831"
-      strokeLinejoin="round"
-    />
+    <path d={SHIELD_OUTLINE_PATH} stroke="currentColor" strokeWidth={SHIELD_OUTLINE_STROKE} strokeLinejoin="round" />
   </svg>
 )

+ 8 - 0
packages/client/ui-primitives/tests/icons.client.spec.tsx

@@ -19,6 +19,14 @@ const iconNames = Object.keys(icons)
 describe('ic_ds_ icon set', () => {
   it('exports the full icon set (46 deepsuite + 21 figma extracts + eleven product glyphs outside those sets)', () => {
     expect(iconNames.length).toBe(78)
+    // The composer menu's own glyphs, pinned by name.
+    expect(iconNames).toEqual(expect.arrayContaining(['IconPlanOutline14', 'IconCompactOutline16', 'IconShieldOutline16']))
+  })
+
+  it('the permission selector composes its marks over the shield contour exported here', () => {
+    const { container } = render(<primitives.IconShieldOutline16 />)
+    expect(container.querySelector('path')?.getAttribute('d')).toBe(primitives.SHIELD_OUTLINE_PATH)
+    expect(container.querySelector('path')?.getAttribute('stroke-width')).toBe(primitives.SHIELD_OUTLINE_STROKE)
   })
 
   it.each(iconNames)('%s renders an svg with currentColor fills and no hardcoded palette', (name) => {