@huaweidevtools/aishell-dsh-brand 0.0.4 → 0.0.6
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 +998 -2
- package/lib/types/client/SuggestionCards.d.ts +94 -0
- package/lib/types/client/autoplay.d.ts +67 -0
- package/lib/types/client/carousel.d.ts +96 -0
- package/lib/types/client/hero-content.d.ts +70 -0
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/locales.d.ts +66 -0
- package/lib/types/client/suggestion-icons.d.ts +45 -0
- package/lib/types/client/suggestions.d.ts +118 -0
- package/package.json +10 -6
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example-prompt cards for the New-Session hero.
|
|
3
|
+
*
|
|
4
|
+
* ## Where this renders, and why it takes a portal to get there
|
|
5
|
+
*
|
|
6
|
+
* The entry occupies `conversation.input.dock`, the band between the hero
|
|
7
|
+
* workspace row and the input card. That is where the strip is MOUNTED — but
|
|
8
|
+
* not where it is DRAWN: the product wants the cards directly under the hero
|
|
9
|
+
* headline, and the workspace row sits between the two.
|
|
10
|
+
*
|
|
11
|
+
* Measured on the live page (1280×900):
|
|
12
|
+
*
|
|
13
|
+
* hero (headline) y=288 h=74
|
|
14
|
+
* hero workspace row y=374 h=28
|
|
15
|
+
* conversation.input.dock y=411 h=80
|
|
16
|
+
* the input card y=491
|
|
17
|
+
*
|
|
18
|
+
* The row cannot be crossed with a margin — it would be covered — and its DOM
|
|
19
|
+
* is a sibling subtree, unreachable from the dock. So the strip renders
|
|
20
|
+
* through `createPortal` into the hero's own content div, the empty `_body`
|
|
21
|
+
* the official shell leaves for exactly this. See `hero-content.ts` for how
|
|
22
|
+
* that mount is found and why it fails closed.
|
|
23
|
+
*
|
|
24
|
+
* The dock entry is still what supplies the slot props (`useSession`,
|
|
25
|
+
* `inputActions`, `t`), so the registration is unchanged; the portal only
|
|
26
|
+
* relocates the output. What the dock band itself renders is nothing.
|
|
27
|
+
*
|
|
28
|
+
* ## When it renders
|
|
29
|
+
*
|
|
30
|
+
* The dock band renders in BOTH the hero and the docked composer (it only
|
|
31
|
+
* needs a live input zone), so the gate is `session.blank` — the same
|
|
32
|
+
* condition the account chip next door uses. `blank` is true from the moment a
|
|
33
|
+
* New Session opens until the first turn is accepted: exactly the window the
|
|
34
|
+
* cards exist for. Once the first message is sent the strip renders nothing
|
|
35
|
+
* and the composer takes over.
|
|
36
|
+
*
|
|
37
|
+
* ## Why it does not look like the input card
|
|
38
|
+
*
|
|
39
|
+
* The first version read as a second input box, and the reason was measurable
|
|
40
|
+
* rather than a matter of taste. Both surfaces were built from the same two
|
|
41
|
+
* ingredients the shell reserves for a composer:
|
|
42
|
+
*
|
|
43
|
+
* - the same neutral one step apart — the card was `bg-module-platform`
|
|
44
|
+
* (`#353638` in dark) and the composer `specific-input-major` (`#2c2c2e`),
|
|
45
|
+
* both rungs of one ramp, so the two read as siblings;
|
|
46
|
+
* - the same box-shadow token. `--dsw-elevation-soft` expands to
|
|
47
|
+
* `var(--dsw-elevation-stroke), …`, and that stroke is a
|
|
48
|
+
* `0 0 0 .5px` ring — so "no border" had not removed the outline at all,
|
|
49
|
+
* it had only moved it into the shadow. Both surfaces carry a hairline.
|
|
50
|
+
*
|
|
51
|
+
* The fix is to use the shell's vocabulary for a CHOICE rather than for a
|
|
52
|
+
* FIELD. Every pickable surface in the product — the option list in the
|
|
53
|
+
* question card (`border-radius: var(--dsw-radius-md)`, `background: 0 0`),
|
|
54
|
+
* the command and trigger lists, the portalled menu — draws itself the same
|
|
55
|
+
* way: a transparent ground, a `radius-md` corner, and a fill that appears
|
|
56
|
+
* only on hover or selection. A field, by contrast, is an opaque well inset
|
|
57
|
+
* into the page.
|
|
58
|
+
*
|
|
59
|
+
* So a card here is:
|
|
60
|
+
*
|
|
61
|
+
* - TRANSLUCENT, not opaque and not empty. The shell explains this exact
|
|
62
|
+
* choice for `SegmentedControl`: a translucent fill "reads as a place
|
|
63
|
+
* rather than a second button". Opaque is what made the card a well;
|
|
64
|
+
* fully transparent is invisible on a white page, where the light theme
|
|
65
|
+
* would leave nothing but floating text. The tint is the shell's own
|
|
66
|
+
* interactive pair — `interactive-bg-hover` at rest, `interactive-bg-active`
|
|
67
|
+
* on hover — so hovering reads as the same material intensifying;
|
|
68
|
+
* - `radius-md` (12px), the choice radius, against the composer's 28px
|
|
69
|
+
* `radius-panel`;
|
|
70
|
+
* - no shadow at rest. The shell gives an elevated surface a shadow only
|
|
71
|
+
* when it FLOATS over other content (a menu, a hovercard, a modal); a
|
|
72
|
+
* card sitting inline in the page gets none, so neither does this.
|
|
73
|
+
*
|
|
74
|
+
* The icon tile keeps an accent tint — `Tag`'s info tone and the plan
|
|
75
|
+
* package's chip both paint `color-mix(… business-primary 10%, transparent)`
|
|
76
|
+
* — on a square with `radius-sm`, matching those chips' geometry.
|
|
77
|
+
*
|
|
78
|
+
* ## Auto-rotation
|
|
79
|
+
*
|
|
80
|
+
* The strip shows three of four cards, so the fourth would otherwise be
|
|
81
|
+
* invisible to anyone who does not touch the arrows. It advances one card
|
|
82
|
+
* every {@link AUTOPLAY_INTERVAL_MS}, pausing on hover, on keyboard focus, in
|
|
83
|
+
* a background tab, and under `prefers-reduced-motion`. The policy itself
|
|
84
|
+
* lives in `autoplay.ts`, kept pure so the list of reasons to stop is testable
|
|
85
|
+
* rather than buried in an effect.
|
|
86
|
+
*
|
|
87
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/SuggestionCards
|
|
88
|
+
*/
|
|
89
|
+
import { type ReactNode } from 'react';
|
|
90
|
+
import type { PropsRuntime, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
91
|
+
/** Full component props: the dock band's owner share + the session kit + `t`. */
|
|
92
|
+
export type SuggestionCardsProps = PropsRuntime<'conversation.input.dock'> & PropsLocale<'aishell-brand'>;
|
|
93
|
+
export declare function SuggestionCards(props: SuggestionCardsProps): ReactNode;
|
|
94
|
+
//# sourceMappingURL=SuggestionCards.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-rotation policy for the example-prompt strip.
|
|
3
|
+
*
|
|
4
|
+
* Kept apart from the component and free of DOM, for the same reason
|
|
5
|
+
* `carousel.ts` is: the DECISION is what needs pinning down, and a decision
|
|
6
|
+
* buried in an effect can only be checked by watching a browser.
|
|
7
|
+
*
|
|
8
|
+
* ## Why the strip advances on its own
|
|
9
|
+
*
|
|
10
|
+
* The strip shows three of four cards, so a card is always off screen and a
|
|
11
|
+
* reader who never touches the arrows never learns it exists. Rotation is what
|
|
12
|
+
* makes the set self-evident. The cost is that content moves without being
|
|
13
|
+
* asked to, which is why every reason to stop is collected here rather than
|
|
14
|
+
* decided piecemeal at the call site.
|
|
15
|
+
*
|
|
16
|
+
* ## Every reason to stop
|
|
17
|
+
*
|
|
18
|
+
* - `engaged` — the pointer is over the strip or focus is inside it. Someone
|
|
19
|
+
* reaching for a card must not have it slide out from under the cursor;
|
|
20
|
+
* this is the pause the feature was asked for, and `focus` is the half of
|
|
21
|
+
* it that covers a reader who never uses a pointer.
|
|
22
|
+
* - `reducedMotion` — the reader asked the platform for less movement. An
|
|
23
|
+
* element that moves by itself for more than five seconds is exactly what
|
|
24
|
+
* `prefers-reduced-motion` exists to suppress, and a hover pause cannot help
|
|
25
|
+
* the reader who cannot hover. Honouring it is what keeps the feature from
|
|
26
|
+
* being motion no one consented to.
|
|
27
|
+
* - `visible` — the tab is hidden. A background tab still fires timers, so
|
|
28
|
+
* without this the strip would churn through slides nobody can see and land
|
|
29
|
+
* somewhere arbitrary on return.
|
|
30
|
+
* - `ready` — the ring has not been measured yet. Advancing before the step
|
|
31
|
+
* is known would move the track by a wrong distance; see the measurement
|
|
32
|
+
* note in `SuggestionCards.tsx`.
|
|
33
|
+
* - `ring` — three cards or fewer all fit, so there is nowhere to advance to.
|
|
34
|
+
* A strip with nothing to scroll must not appear to scroll.
|
|
35
|
+
*
|
|
36
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/autoplay
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* How long each card is shown before the strip advances.
|
|
40
|
+
*
|
|
41
|
+
* Fixed rather than configurable: this is a hint at what the product can do,
|
|
42
|
+
* not a reading surface, and a setting nobody asked for is a setting nobody
|
|
43
|
+
* finds.
|
|
44
|
+
*/
|
|
45
|
+
export declare const AUTOPLAY_INTERVAL_MS = 1500;
|
|
46
|
+
/** Everything that can hold the strip still. */
|
|
47
|
+
export interface AutoplayInputs {
|
|
48
|
+
/** More cards than fit, so there is somewhere to advance to. */
|
|
49
|
+
readonly ring: boolean;
|
|
50
|
+
/** The ring has been measured and the track can be moved a known distance. */
|
|
51
|
+
readonly ready: boolean;
|
|
52
|
+
/** The pointer is over the strip, or focus is inside it. */
|
|
53
|
+
readonly engaged: boolean;
|
|
54
|
+
/** The reader asked for reduced motion. */
|
|
55
|
+
readonly reducedMotion: boolean;
|
|
56
|
+
/** The tab is on screen. */
|
|
57
|
+
readonly visible: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether the strip should be advancing on its own right now.
|
|
61
|
+
*
|
|
62
|
+
* Expressed as one predicate rather than a chain of early returns inside the
|
|
63
|
+
* timer, so that adding a reason to stop is a change to this list and its
|
|
64
|
+
* test — not a change to the effect that schedules the ticks.
|
|
65
|
+
*/
|
|
66
|
+
export declare function autoplayActive(inputs: AutoplayInputs): boolean;
|
|
67
|
+
//# sourceMappingURL=autoplay.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carousel position maths, kept pure so it can be tested without a DOM.
|
|
3
|
+
*
|
|
4
|
+
* The strip is a RING, not a scroll range: the cards form a circle, and an
|
|
5
|
+
* arrow steps one card around it forever. Clicking next from the last
|
|
6
|
+
* position lands on the first, and clicking prev from the first lands on the
|
|
7
|
+
* last — and crucially, every intermediate position exists. A four-card ring
|
|
8
|
+
* showing three at a time has FOUR positions, not two:
|
|
9
|
+
*
|
|
10
|
+
* cards 1 2 3 4, three visible
|
|
11
|
+
* pos 0 → [1 2 3]
|
|
12
|
+
* pos 1 → [2 3 4]
|
|
13
|
+
* pos 2 → [3 4 1] ← the wrap is visible, not a jump back
|
|
14
|
+
* pos 3 → [4 1 2]
|
|
15
|
+
* pos 4 → [1 2 3] same as pos 0
|
|
16
|
+
*
|
|
17
|
+
* A plain scroll container cannot express that: its range is only
|
|
18
|
+
* `content - viewport` (one step, for four cards), and positions 2 and 3 have
|
|
19
|
+
* no place to live. So the card list is rendered {@link COPIES} times over
|
|
20
|
+
* and the window travels through the middle copy, where both neighbours
|
|
21
|
+
* always have content.
|
|
22
|
+
*
|
|
23
|
+
* ## Why the copies, and how the seam stays invisible
|
|
24
|
+
*
|
|
25
|
+
* With the list rendered three times, a window at physical position `p` shows
|
|
26
|
+
* `cards[p .. p+VISIBLE-1]`, and position `2n` shows exactly what position `n`
|
|
27
|
+
* shows. That equivalence is the seam: once a slide settles at `2n`, the
|
|
28
|
+
* track can be placed at `n` in the same frame with transitions off, and
|
|
29
|
+
* nothing on screen changes. The same holds at the other end, where `n-1` and
|
|
30
|
+
* `2n-1` are the same window.
|
|
31
|
+
*
|
|
32
|
+
* The rewind happens only AFTER the slide finishes — never before. Jumping
|
|
33
|
+
* first would put the destination on screen instantly and then animate away
|
|
34
|
+
* from it, so a click at the seam would appear to skip a card. This is the
|
|
35
|
+
* one law in this module that a reader is likely to get backwards.
|
|
36
|
+
*
|
|
37
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/carousel
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* How many times the card list is rendered.
|
|
41
|
+
*
|
|
42
|
+
* Three is the minimum that works. The window lives in the middle copy, so it
|
|
43
|
+
* needs one full copy behind it (to show the cards before the start) and one
|
|
44
|
+
* ahead (to show the cards past the end while a slide is finishing at the
|
|
45
|
+
* seam, where the position may briefly reach `2n` and read up to
|
|
46
|
+
* `2n + VISIBLE - 1`).
|
|
47
|
+
*/
|
|
48
|
+
export declare const COPIES = 3;
|
|
49
|
+
/** How many cards to render for a ring of `n`. */
|
|
50
|
+
export declare function ringLength(n: number): number;
|
|
51
|
+
/**
|
|
52
|
+
* The physical position the ring starts at — the first card of the middle
|
|
53
|
+
* copy. Starting here rather than at 0 is what makes the very first prev
|
|
54
|
+
* click land on a real card instead of an empty track.
|
|
55
|
+
*/
|
|
56
|
+
export declare function ringStart(n: number): number;
|
|
57
|
+
/**
|
|
58
|
+
* Where a settled position should be silently rewound to, or `null` when it
|
|
59
|
+
* is already inside the working band.
|
|
60
|
+
*
|
|
61
|
+
* The band is `[n, 2n-1]`. A slide may finish one past either end — `2n`
|
|
62
|
+
* going forward, `n-1` going backward — and both have an exact twin inside
|
|
63
|
+
* the band. See the module note for why this runs after the slide, not
|
|
64
|
+
* before.
|
|
65
|
+
*/
|
|
66
|
+
export declare function rewindTo(pos: number, n: number): number | null;
|
|
67
|
+
/** One arrow press, resolved into the physical positions involved. */
|
|
68
|
+
export interface RingStep {
|
|
69
|
+
/**
|
|
70
|
+
* Where the track must be placed with transitions OFF before the slide.
|
|
71
|
+
* Equal to the incoming position when no rewind is due; when it differs,
|
|
72
|
+
* the jump is invisible (both positions show the same window).
|
|
73
|
+
*/
|
|
74
|
+
readonly from: number;
|
|
75
|
+
/**
|
|
76
|
+
* Where the slide ends. May sit one past the band's end, in which case the
|
|
77
|
+
* next {@link rewindTo} brings it back.
|
|
78
|
+
*/
|
|
79
|
+
readonly to: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve one arrow press on a ring.
|
|
83
|
+
*
|
|
84
|
+
* Any rewind still owed from the previous press is settled first, so a person
|
|
85
|
+
* clicking faster than the slide animation cannot walk the window off the
|
|
86
|
+
* rendered copies — the band always absorbs one more step.
|
|
87
|
+
*/
|
|
88
|
+
export declare function ringStep(pos: number, delta: number, n: number): RingStep;
|
|
89
|
+
/**
|
|
90
|
+
* One arrow press on a plain strip (fewer cards than fit): step by one and
|
|
91
|
+
* stay inside the scrollable range.
|
|
92
|
+
*
|
|
93
|
+
* @param maxPos - the last meaningful position, `0` when nothing scrolls.
|
|
94
|
+
*/
|
|
95
|
+
export declare function clampStep(pos: number, delta: number, maxPos: number): number;
|
|
96
|
+
//# sourceMappingURL=carousel.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mount point the suggestion strip renders into, inside the hero.
|
|
3
|
+
*
|
|
4
|
+
* ## Why a portal, and not CSS placement
|
|
5
|
+
*
|
|
6
|
+
* The strip is a `conversation.input.dock` entry, and that band renders BELOW
|
|
7
|
+
* the hero workspace row in the composer stack. Measured on the live page
|
|
8
|
+
* (1280×900, DSH 0.2.x):
|
|
9
|
+
*
|
|
10
|
+
* hero (headline) y=288 h=74
|
|
11
|
+
* hero workspace row y=374 h=28
|
|
12
|
+
* conversation.input.dock y=411 h=80
|
|
13
|
+
* the input card y=491
|
|
14
|
+
*
|
|
15
|
+
* The product wants the cards directly under the headline, and the workspace
|
|
16
|
+
* row sits between. A negative margin cannot cross it — the row would be
|
|
17
|
+
* covered — and the row's own subtree is not reachable from the dock, since
|
|
18
|
+
* the two are separate children of the composer stack.
|
|
19
|
+
*
|
|
20
|
+
* The hero, however, already renders an empty content div for exactly this
|
|
21
|
+
* purpose. Its stack is: headline → (the "Powered by" label) → `_body`, where
|
|
22
|
+
* `_body` is `flex-direction: column` and left empty by the official shell.
|
|
23
|
+
* That is the hero's content area, and putting the cards there is using the
|
|
24
|
+
* slot as intended rather than fighting the layout.
|
|
25
|
+
*
|
|
26
|
+
* ## How the mount is found
|
|
27
|
+
*
|
|
28
|
+
* By the hero's own class suffix, from the headline outward — `[class*="_body"]`
|
|
29
|
+
* alone is ambiguous (the conversation root has a `_body` too, and the live
|
|
30
|
+
* page has two). So the search starts at a `[class*="_headline"]`, steps to its
|
|
31
|
+
* parent (the hero stack), and takes that stack's own `_body` child. The
|
|
32
|
+
* headline is unique on the page and is what makes the walk unambiguous.
|
|
33
|
+
*
|
|
34
|
+
* Children are walked directly rather than with `:scope >`. The two are
|
|
35
|
+
* equivalent here, and the direct walk keeps these helpers working against the
|
|
36
|
+
* hand-rolled DOM stub the specs use (which has no selector engine) instead of
|
|
37
|
+
* leaving the module's real logic untested.
|
|
38
|
+
*
|
|
39
|
+
* The classes are CSS-module hashes (`pXSMma_body`), hence the suffix match.
|
|
40
|
+
* A base bump that renames them fails closed: {@link findHeroContent} returns
|
|
41
|
+
* null, the portal renders nothing, and the arrows and cards simply do not
|
|
42
|
+
* appear — the strip is never left in the wrong parent.
|
|
43
|
+
*
|
|
44
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/hero-content
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* The hero's content area, or `null` when the hero is not on screen.
|
|
48
|
+
*
|
|
49
|
+
* Returns null rather than throwing: a New Session that has not painted its
|
|
50
|
+
* hero yet is a normal state (and the strip retries on the next frame), not an
|
|
51
|
+
* error.
|
|
52
|
+
*/
|
|
53
|
+
export declare function findHeroContent(target: Document): HTMLElement | null;
|
|
54
|
+
/**
|
|
55
|
+
* A container for the strip inside the hero content area.
|
|
56
|
+
*
|
|
57
|
+
* The hero body is `display: block` and zero-height, so the strip cannot rely
|
|
58
|
+
* on it for layout and must not inherit that block context. The wrapper is
|
|
59
|
+
* created here rather than rendered by React into the official div, because
|
|
60
|
+
* React does not own that node — it owns only what the portal puts inside.
|
|
61
|
+
*/
|
|
62
|
+
export declare function ensureHeroMount(content: HTMLElement): HTMLElement;
|
|
63
|
+
/**
|
|
64
|
+
* Remove the mount this module created, if it is still there.
|
|
65
|
+
*
|
|
66
|
+
* Only the mount is removed — never the host div, which belongs to the shell
|
|
67
|
+
* and is reused on the next New Session.
|
|
68
|
+
*/
|
|
69
|
+
export declare function removeHeroMount(content: HTMLElement): void;
|
|
70
|
+
//# sourceMappingURL=hero-content.d.ts.map
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @module @huaweidevtools/aishell-dsh-brand/client
|
|
9
9
|
*/
|
|
10
10
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
11
|
-
/** Required
|
|
11
|
+
/** Required services: the UI slot registry and the locale registry. */
|
|
12
12
|
export declare const inject: string[];
|
|
13
13
|
export declare function apply(ctx: ClientContext): void;
|
|
14
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aishell brand locale dictionaries.
|
|
3
|
+
*
|
|
4
|
+
* The brand plugin owns its own namespace rather than reusing `aishell`
|
|
5
|
+
* (which `aishell-credentials` registers): a namespace's texts have exactly
|
|
6
|
+
* one owner, and a duplicate `(ns, locale)` registration throws.
|
|
7
|
+
*
|
|
8
|
+
* ## Why content lives in a dictionary
|
|
9
|
+
*
|
|
10
|
+
* The suggestion cards are the only strings in this plugin a person reads,
|
|
11
|
+
* and they are CONTENT rather than interface chrome — a card title, its
|
|
12
|
+
* one-line gloss, and the prompt a click inserts. They still belong here:
|
|
13
|
+
* a card that says「云资源查询」to an English reader is worse than no card,
|
|
14
|
+
* and the prompt it inserts would start an English conversation in Chinese.
|
|
15
|
+
*
|
|
16
|
+
* Chinese is the key-set source of truth; English is complete against it and
|
|
17
|
+
* is also the terminal fallback, so every locale other than `zh` reads
|
|
18
|
+
* English. That is the framework's own chain (`LOCALE_IDS = ["zh", "en"]`,
|
|
19
|
+
* with English carrying no fallback) — there is no third dictionary to write.
|
|
20
|
+
*
|
|
21
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/locales
|
|
22
|
+
*/
|
|
23
|
+
/** Dictionary namespace owned by this plugin. */
|
|
24
|
+
export declare const NS: "aishell-brand";
|
|
25
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
26
|
+
export declare const zh: {
|
|
27
|
+
readonly 'suggestion.previous': "上一个示例";
|
|
28
|
+
readonly 'suggestion.next': "下一个示例";
|
|
29
|
+
readonly 'suggestion.inventory.title': "云资源查询";
|
|
30
|
+
readonly 'suggestion.inventory.gloss': "列出当前账号下的云资源";
|
|
31
|
+
readonly 'suggestion.inventory.prompt': "请列出我的华为云资源清单,按服务分类(计算、存储、网络、数据库等),标出每类资源的数量和所在区域。";
|
|
32
|
+
readonly 'suggestion.deploy.title': "部署方案推荐";
|
|
33
|
+
readonly 'suggestion.deploy.gloss': "按访问量和预算给出可落地的部署路径";
|
|
34
|
+
readonly 'suggestion.deploy.prompt': "我要部署一个轻量级 Web 应用,请对比几种可行的部署方案,说明各自的适用规模、月度成本量级和运维复杂度,并给出你的推荐及理由。";
|
|
35
|
+
readonly 'suggestion.enterprise.title': "企业上云";
|
|
36
|
+
readonly 'suggestion.enterprise.gloss': "多账号、网络分区与权限边界的整体设计";
|
|
37
|
+
readonly 'suggestion.enterprise.prompt': "请为我规划一套云上 Landing Zone 架构,覆盖账号组织结构、网络分区、权限边界与审计要求,并说明各部分的设计取舍。";
|
|
38
|
+
readonly 'suggestion.billing.title': "账单分析";
|
|
39
|
+
readonly 'suggestion.billing.gloss': "分析上个月的云资源支出";
|
|
40
|
+
readonly 'suggestion.billing.prompt': "请分析我上个月的云资源支出构成,找出占比最高的几项服务,指出其中可以优化或降配的部分,并估算调整后能节省多少。";
|
|
41
|
+
};
|
|
42
|
+
/** English dictionary. Complete against {@link zh}; also the terminal fallback. */
|
|
43
|
+
export declare const en: {
|
|
44
|
+
readonly 'suggestion.previous': "Previous example";
|
|
45
|
+
readonly 'suggestion.next': "Next example";
|
|
46
|
+
readonly 'suggestion.inventory.title': "Cloud inventory";
|
|
47
|
+
readonly 'suggestion.inventory.gloss': "List the resources in this account";
|
|
48
|
+
readonly 'suggestion.inventory.prompt': string;
|
|
49
|
+
readonly 'suggestion.deploy.title': "Deployment options";
|
|
50
|
+
readonly 'suggestion.deploy.gloss': "A workable path for your traffic and budget";
|
|
51
|
+
readonly 'suggestion.deploy.prompt': string;
|
|
52
|
+
readonly 'suggestion.enterprise.title': "Landing zone";
|
|
53
|
+
readonly 'suggestion.enterprise.gloss': "Multi-account structure, network and permission boundaries";
|
|
54
|
+
readonly 'suggestion.enterprise.prompt': string;
|
|
55
|
+
readonly 'suggestion.billing.title': "Billing analysis";
|
|
56
|
+
readonly 'suggestion.billing.gloss': "Break down last month's cloud spend";
|
|
57
|
+
readonly 'suggestion.billing.prompt': string;
|
|
58
|
+
};
|
|
59
|
+
/** Every key, derived from the zh dictionary (the source of truth). */
|
|
60
|
+
export type AishellBrandLocaleKey = keyof typeof zh;
|
|
61
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
62
|
+
interface LocaleNamespaceMap {
|
|
63
|
+
'aishell-brand': AishellBrandLocaleKey;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline SVG artwork for the suggestion cards.
|
|
3
|
+
*
|
|
4
|
+
* Inline rather than an icon dependency: the strip draws four shapes, and a
|
|
5
|
+
* dependency would add a package, a version to track, and a bundle face to
|
|
6
|
+
* keep in step with the host.
|
|
7
|
+
*
|
|
8
|
+
* Every path is drawn on a 24×24 viewBox with `currentColor`, so a card
|
|
9
|
+
* decides the colour by setting `color` and nothing here hard-codes a theme
|
|
10
|
+
* value.
|
|
11
|
+
*
|
|
12
|
+
* ## Shape rules, learned by getting them wrong
|
|
13
|
+
*
|
|
14
|
+
* The first draft drew "deploy" as a box with an arrow leaving it — which
|
|
15
|
+
* reads as SHARE or export, the one meaning this card must not carry. A
|
|
16
|
+
* glyph in a row of four is read at a glance and out of context, so each one
|
|
17
|
+
* has to be recognisable on its own:
|
|
18
|
+
*
|
|
19
|
+
* - nothing may resemble the share glyph (box + outward arrow) or the
|
|
20
|
+
* external-link glyph (box + arrow through a corner);
|
|
21
|
+
* - every shape is closed and filled-ish in silhouette, because outlines
|
|
22
|
+
* at 20px in a row of four blur into each other;
|
|
23
|
+
* - no glyph carries more than three strokes, so they stay distinguishable
|
|
24
|
+
* at the size they are actually drawn.
|
|
25
|
+
*
|
|
26
|
+
* These rules are not machine-checked: rendering the glyphs needs
|
|
27
|
+
* `react-dom/server`, which this repo does not depend on, and vitest here
|
|
28
|
+
* collects only `.spec.ts` (not `.tsx`). The browser harness in
|
|
29
|
+
* `scripts/render-check/` is what actually puts them on screen.
|
|
30
|
+
*
|
|
31
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/suggestion-icons
|
|
32
|
+
*/
|
|
33
|
+
import type { ReactNode } from 'react';
|
|
34
|
+
import type { SuggestionIcon } from './suggestions.ts';
|
|
35
|
+
/** Props shared by every glyph. */
|
|
36
|
+
interface IconProps {
|
|
37
|
+
/** Rendered size in px; the cards use 20. */
|
|
38
|
+
readonly size?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Resolve one icon key to its glyph. */
|
|
41
|
+
export declare function SuggestionIconArt({ icon, size, }: IconProps & {
|
|
42
|
+
readonly icon: SuggestionIcon;
|
|
43
|
+
}): ReactNode;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=suggestion-icons.d.ts.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The New-Session example cards, and the shapes they need.
|
|
3
|
+
*
|
|
4
|
+
* One array drives the whole strip: add or reorder a card by editing
|
|
5
|
+
* `SUGGESTIONS` and adding its three keys to `locales.ts`, and nothing else.
|
|
6
|
+
* The row renders whatever it is given — three or fewer display side by side
|
|
7
|
+
* with no arrows, more than three become a carousel.
|
|
8
|
+
*
|
|
9
|
+
* ## This file holds no copy
|
|
10
|
+
*
|
|
11
|
+
* Every string a person reads — the card title, its one-line gloss, and the
|
|
12
|
+
* prompt a click inserts — lives in `locales.ts`, addressed by the keys here.
|
|
13
|
+
* A card whose text were inlined would be readable in exactly one language,
|
|
14
|
+
* and the prompt it inserted would start a conversation in that language no
|
|
15
|
+
* matter what the reader's locale was.
|
|
16
|
+
*
|
|
17
|
+
* The three keys per card are separate rather than one blob because the
|
|
18
|
+
* surfaces differ: the card shows `title` and `gloss` (both clamped — one
|
|
19
|
+
* line and two lines — so a long string ellipsizes instead of breaking the
|
|
20
|
+
* row), while `prompt` goes to the composer and is never displayed on the
|
|
21
|
+
* card. A prompt that only restates its title makes the click pointless;
|
|
22
|
+
* written as a pair, the prompt carries the context and the expected output
|
|
23
|
+
* that make the first message worth sending without editing.
|
|
24
|
+
*
|
|
25
|
+
* ## Sizing, when editing copy
|
|
26
|
+
*
|
|
27
|
+
* Three cards share one row, which at the default column leaves roughly 150px
|
|
28
|
+
* per title — about seven CJK glyphs, or noticeably more Latin ones. Measured
|
|
29
|
+
* against the shipped set: every title fits at 560px and above, and all four
|
|
30
|
+
* clip below ~460px. Keep them within a couple of characters of each other —
|
|
31
|
+
* the longest one is otherwise the only card that ever shows an ellipsis,
|
|
32
|
+
* which reads as a bug rather than as a long name.
|
|
33
|
+
*
|
|
34
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/suggestions
|
|
35
|
+
*/
|
|
36
|
+
import type { AishellBrandLocaleKey } from './locales.ts';
|
|
37
|
+
/** One example card: what it looks like, and what a click inserts. */
|
|
38
|
+
export interface Suggestion {
|
|
39
|
+
/**
|
|
40
|
+
* Stable identity for React keys and for the carousel's position maths.
|
|
41
|
+
* Not shown to anyone — it names the dictionary key prefix, so renaming it
|
|
42
|
+
* means renaming three keys in `locales.ts`.
|
|
43
|
+
*/
|
|
44
|
+
readonly id: string;
|
|
45
|
+
/** Icon key resolved by {@link SuggestionIconArt}. */
|
|
46
|
+
readonly icon: SuggestionIcon;
|
|
47
|
+
/** The tile colour, measured to work in both themes. See {@link SuggestionHue}. */
|
|
48
|
+
readonly hue: SuggestionHue;
|
|
49
|
+
/** Dictionary key for the card's one-line title. */
|
|
50
|
+
readonly titleKey: AishellBrandLocaleKey;
|
|
51
|
+
/** Dictionary key for the line under the title. */
|
|
52
|
+
readonly glossKey: AishellBrandLocaleKey;
|
|
53
|
+
/** Dictionary key for the text inserted into the composer. */
|
|
54
|
+
readonly promptKey: AishellBrandLocaleKey;
|
|
55
|
+
}
|
|
56
|
+
/** The icon set this strip can draw. Keys are local to this plugin. */
|
|
57
|
+
export type SuggestionIcon = 'cloud' | 'deploy' | 'architecture' | 'cost';
|
|
58
|
+
/**
|
|
59
|
+
* The tile colour for one card, as the shell's own token names.
|
|
60
|
+
*
|
|
61
|
+
* ## Why each card carries its own hue
|
|
62
|
+
*
|
|
63
|
+
* The first version drew every icon in one accent, on the reasoning that four
|
|
64
|
+
* hues beside the brand mark would read as noise. That was a guess, and the
|
|
65
|
+
* shell contradicts it: `FileTypeIcon` gives every item in a row its own hue
|
|
66
|
+
* (`code` blue, `excel` green, `folder` amber, `image` violet), which is the
|
|
67
|
+
* same situation — a row of small glyphs read at a glance — and it chooses
|
|
68
|
+
* per-item colour. Colour is what makes such a row scannable rather than a
|
|
69
|
+
* wall of identical tiles.
|
|
70
|
+
*
|
|
71
|
+
* ## These are STATIC tokens, not the semantic ones
|
|
72
|
+
*
|
|
73
|
+
* `--dsw-alias-state-success-primary` and friends mean "the operation
|
|
74
|
+
* succeeded" / "this needs attention". A card about cost is not a warning, so
|
|
75
|
+
* borrowing the warning colour would be a lie the palette tells. The `static`
|
|
76
|
+
* family is the one intended for decorative identity, which is exactly what a
|
|
77
|
+
* card's hue is — and `FileTypeIcon` reaches for the same family.
|
|
78
|
+
*
|
|
79
|
+
* ## The values were chosen by measurement, in both themes
|
|
80
|
+
*
|
|
81
|
+
* Violet is the exception: the design platform ships NO violet token, so
|
|
82
|
+
* `FileTypeIcon` defines one locally (`rgb(139, 118, 246)`) for its own
|
|
83
|
+
* artwork. The same value is reused here rather than inventing a second one,
|
|
84
|
+
* and it is the only literal colour in this plugin.
|
|
85
|
+
*
|
|
86
|
+
* The four were picked to clear three bars at once, in both themes:
|
|
87
|
+
*
|
|
88
|
+
* - glyph on its tile >= 4.5 — measured 5.42 at worst (violet);
|
|
89
|
+
* - tile against the card >= 1.6 — measured 2.05 at worst;
|
|
90
|
+
* - worst pairwise colour distance >= 30 (CIE76 dE) — measured 38.7.
|
|
91
|
+
*
|
|
92
|
+
* The last bar is the one that matters for "four colours, not one blur": an
|
|
93
|
+
* obvious-looking pick like blue + violet measures only 17.5 apart, which is
|
|
94
|
+
* why this is measured rather than eyeballed.
|
|
95
|
+
*/
|
|
96
|
+
export interface SuggestionHue {
|
|
97
|
+
/**
|
|
98
|
+
* The tile fill. A SOLID hue, with the glyph in `--dsw-alias-label-primary-
|
|
99
|
+
* foreground` on top.
|
|
100
|
+
*
|
|
101
|
+
* A tint of the hue was the first attempt and it measured badly: a glyph
|
|
102
|
+
* drawn in a hue on top of a tint of that same hue has almost nothing to
|
|
103
|
+
* separate them, so the contrast was 1.8–2.8 — and RAISING the tint made it
|
|
104
|
+
* worse, since more of the hue behind the glyph means less difference from
|
|
105
|
+
* it. A solid tile puts the glyph on full-strength colour instead, which
|
|
106
|
+
* measured 5.4–9.9 for the same hues.
|
|
107
|
+
*/
|
|
108
|
+
readonly tile: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The cards, in display order.
|
|
112
|
+
*
|
|
113
|
+
* Order matters only for the ring's starting window: the strip opens on the
|
|
114
|
+
* first {@link VISIBLE} cards, so the first entries are the ones a person
|
|
115
|
+
* sees without pressing an arrow.
|
|
116
|
+
*/
|
|
117
|
+
export declare const SUGGESTIONS: readonly Suggestion[];
|
|
118
|
+
//# sourceMappingURL=suggestions.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huaweidevtools/aishell-dsh-brand",
|
|
3
3
|
"description": "AI Shell brand reskin for DSH web — replaces sidebar and hero brand marks with AI Shell SVG",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.6",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"inject": [
|
|
31
31
|
"@deepseek-ai/dsh-client-ui-renderer",
|
|
32
32
|
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
33
|
-
"@deepseek-ai/dsh-client-ui-conversation"
|
|
33
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
34
|
+
"@deepseek-ai/dsh-client-locale"
|
|
34
35
|
],
|
|
35
36
|
"platform": "web"
|
|
36
37
|
},
|
|
@@ -44,12 +45,15 @@
|
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
47
|
-
"@deepseek-ai/dsh-client-
|
|
48
|
-
"@deepseek-ai/dsh-client-ui-
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-
|
|
48
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.1 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1",
|
|
51
53
|
"@types/react": "~18.3.1",
|
|
54
|
+
"@types/react-dom": "~18.3.1",
|
|
52
55
|
"react": "^18.2.0",
|
|
56
|
+
"react-dom": "^18.2.0",
|
|
53
57
|
"tsdown": "^0.10.0",
|
|
54
58
|
"typescript": "^5.7.0"
|
|
55
59
|
},
|