Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qjclaw-dmg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qjclaw-dmg
Commits
bd61e8e6
Commit
bd61e8e6
authored
May 22, 2026
by
edy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(ui): add colorful expert icons
parent
763794d2
Pipeline
#18480
failed
Changes
4
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
92 additions
and
28 deletions
+92
-28
.gitlab-ci.yml
.gitlab-ci.yml
+1
-0
package.json
apps/ui/package.json
+1
-0
AppIcons.tsx
apps/ui/src/components/icons/AppIcons.tsx
+37
-28
expertIconSource.test.ts
apps/ui/test/expertIconSource.test.ts
+53
-0
No files found.
.gitlab-ci.yml
View file @
bd61e8e6
...
@@ -31,6 +31,7 @@ typecheck_windows:
...
@@ -31,6 +31,7 @@ typecheck_windows:
-
node --version
-
node --version
-
corepack --version
-
corepack --version
-
corepack pnpm install --frozen-lockfile --store-dir "$env:PNPM_STORE_DIR"
-
corepack pnpm install --frozen-lockfile --store-dir "$env:PNPM_STORE_DIR"
-
corepack pnpm --filter @qjclaw/ui run test:icons
-
corepack pnpm typecheck
-
corepack pnpm typecheck
package_windows_installer
:
package_windows_installer
:
...
...
apps/ui/package.json
View file @
bd61e8e6
...
@@ -8,6 +8,7 @@
...
@@ -8,6 +8,7 @@
"clean"
:
"rimraf dist"
,
"clean"
:
"rimraf dist"
,
"dev"
:
"vite"
,
"dev"
:
"vite"
,
"lint"
:
"tsc --noEmit"
,
"lint"
:
"tsc --noEmit"
,
"test:icons"
:
"node --test test/expertIconSource.test.ts"
,
"typecheck"
:
"tsc --noEmit"
"typecheck"
:
"tsc --noEmit"
},
},
"dependencies"
:
{
"dependencies"
:
{
...
...
apps/ui/src/components/icons/AppIcons.tsx
View file @
bd61e8e6
...
@@ -93,10 +93,11 @@ export function DouyinNoteIcon() {
...
@@ -93,10 +93,11 @@ export function DouyinNoteIcon() {
function
BrowserExpertIcon
()
{
function
BrowserExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M4 8.25A2.25 2.25 0 0 1 6.25 6h11.5A2.25 2.25 0 0 1 20 8.25v7.5A2.25 2.25 0 0 1 17.75 18H6.25A2.25 2.25 0 0 1 4 15.75v-7.5Z"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.8"
/>
<
path
d=
"M4 8.25A2.25 2.25 0 0 1 6.25 6h11.5A2.25 2.25 0 0 1 20 8.25v7.5A2.25 2.25 0 0 1 17.75 18H6.25A2.25 2.25 0 0 1 4 15.75v-7.5Z"
fill=
"#EFF6FF"
stroke=
"#3B82F6"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.6"
/>
<
path
d=
"M4.75 9.25h14.5"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeWidth=
"1.6"
/>
<
path
d=
"M4.75 9.25h14.5"
fill=
"none"
stroke=
"#60A5FA"
strokeLinecap=
"round"
strokeWidth=
"1.45"
/>
<
circle
cx=
"7.4"
cy=
"7.7"
r=
"0.75"
fill=
"currentColor"
/>
<
circle
cx=
"7.4"
cy=
"7.7"
r=
"0.75"
fill=
"#EF4444"
/>
<
circle
cx=
"10.1"
cy=
"7.7"
r=
"0.75"
fill=
"currentColor"
opacity=
"0.78"
/>
<
circle
cx=
"10.1"
cy=
"7.7"
r=
"0.75"
fill=
"#F59E0B"
/>
<
path
d=
"M8 13.5h4.1M8 15.55h7.7"
fill=
"none"
stroke=
"#22C55E"
strokeLinecap=
"round"
strokeWidth=
"1.35"
/>
</
svg
>
</
svg
>
);
);
}
}
...
@@ -104,9 +105,11 @@ function BrowserExpertIcon() {
...
@@ -104,9 +105,11 @@ function BrowserExpertIcon() {
function
PlannerExpertIcon
()
{
function
PlannerExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M5 6.25A2.25 2.25 0 0 1 7.25 4h9.5A2.25 2.25 0 0 1 19 6.25v11.5A2.25 2.25 0 0 1 16.75 20h-9.5A2.25 2.25 0 0 1 5 17.75V6.25Z"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.8"
/>
<
path
d=
"M5 6.25A2.25 2.25 0 0 1 7.25 4h9.5A2.25 2.25 0 0 1 19 6.25v11.5A2.25 2.25 0 0 1 16.75 20h-9.5A2.25 2.25 0 0 1 5 17.75V6.25Z"
fill=
"#FFF7ED"
stroke=
"#F97316"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.6"
/>
<
path
d=
"M8 8.25h8M8 12h5.5M8 15.75h3.5"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeWidth=
"1.7"
/>
<
path
d=
"M8.2 8.1h5.7M8.2 11.1h4.5M8.2 16.1h3.1"
fill=
"none"
stroke=
"#2563EB"
strokeLinecap=
"round"
strokeWidth=
"1.45"
/>
<
circle
cx=
"16.9"
cy=
"15.8"
r=
"1.6"
fill=
"currentColor"
opacity=
"0.88"
/>
<
circle
cx=
"15.8"
cy=
"15.6"
r=
"2.2"
fill=
"#DBEAFE"
stroke=
"#2563EB"
strokeWidth=
"1.2"
/>
<
path
d=
"M15.8 13.4v2.2l1.7 1"
fill=
"none"
stroke=
"#F97316"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.15"
/>
<
path
d=
"M7.8 4v2.3M16.2 4v2.3"
fill=
"none"
stroke=
"#FB923C"
strokeLinecap=
"round"
strokeWidth=
"1.5"
/>
</
svg
>
</
svg
>
);
);
}
}
...
@@ -114,8 +117,9 @@ function PlannerExpertIcon() {
...
@@ -114,8 +117,9 @@ function PlannerExpertIcon() {
function
ZhihuExpertIcon
()
{
function
ZhihuExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M5.25 6.25A2.25 2.25 0 0 1 7.5 4h9A2.25 2.25 0 0 1 18.75 6.25v8.1a2.25 2.25 0 0 1-2.25 2.25h-3.4l-2.95 2.45c-.48.4-1.2.06-1.2-.57V16.6H7.5a2.25 2.25 0 0 1-2.25-2.25v-8.1Z"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.8"
/>
<
path
d=
"M5.25 6.25A2.25 2.25 0 0 1 7.5 4h9A2.25 2.25 0 0 1 18.75 6.25v8.1a2.25 2.25 0 0 1-2.25 2.25h-3.4l-2.95 2.45c-.48.4-1.2.06-1.2-.57V16.6H7.5a2.25 2.25 0 0 1-2.25-2.25v-8.1Z"
fill=
"#1769FF"
stroke=
"#0F4FD8"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.35"
/>
<
path
d=
"M8.2 9.1h7.6M8.2 12.2h5.4"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeWidth=
"1.7"
/>
<
path
d=
"M8 8.35h5.2M8 11.15h7.9M8 13.95h4.6"
fill=
"none"
stroke=
"#ffffff"
strokeLinecap=
"round"
strokeWidth=
"1.45"
/>
<
circle
cx=
"15.65"
cy=
"8.35"
r=
"1.15"
fill=
"#ffffff"
opacity=
"0.92"
/>
</
svg
>
</
svg
>
);
);
}
}
...
@@ -123,8 +127,8 @@ function ZhihuExpertIcon() {
...
@@ -123,8 +127,8 @@ function ZhihuExpertIcon() {
function
WechatExpertIcon
()
{
function
WechatExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M10 5C6.69 5 4 7.27 4 10.08c0 1.52.8 2.88 2.07 3.8L5.4 16.5l2.7-1.35c.6.14 1.23.21 1.9.21 3.32 0 6-2.27 6-5.08S13.32 5 10 5Z"
fill=
"
currentColor"
opacity=
"0.92
"
/>
<
path
d=
"M10 5C6.69 5 4 7.27 4 10.08c0 1.52.8 2.88 2.07 3.8L5.4 16.5l2.7-1.35c.6.14 1.23.21 1.9.21 3.32 0 6-2.27 6-5.08S13.32 5 10 5Z"
fill=
"
#22C55E
"
/>
<
path
d=
"M15.3 9.3c2.6 0 4.7 1.8 4.7 4.02 0 1.2-.62 2.27-1.62 3l.54 2.08-2.13-1.06c-.47.11-.97.17-1.5.17-2.6 0-4.7-1.8-4.7-4.02S12.7 9.3 15.3 9.3Z"
fill=
"
currentColor"
opacity=
"0.68
"
/>
<
path
d=
"M15.3 9.3c2.6 0 4.7 1.8 4.7 4.02 0 1.2-.62 2.27-1.62 3l.54 2.08-2.13-1.06c-.47.11-.97.17-1.5.17-2.6 0-4.7-1.8-4.7-4.02S12.7 9.3 15.3 9.3Z"
fill=
"
#16A34A
"
/>
<
circle
cx=
"8.35"
cy=
"9.95"
r=
"0.9"
fill=
"#ffffff"
/>
<
circle
cx=
"8.35"
cy=
"9.95"
r=
"0.9"
fill=
"#ffffff"
/>
<
circle
cx=
"11.55"
cy=
"9.95"
r=
"0.9"
fill=
"#ffffff"
/>
<
circle
cx=
"11.55"
cy=
"9.95"
r=
"0.9"
fill=
"#ffffff"
/>
<
circle
cx=
"14.05"
cy=
"13.25"
r=
"0.8"
fill=
"#ffffff"
/>
<
circle
cx=
"14.05"
cy=
"13.25"
r=
"0.8"
fill=
"#ffffff"
/>
...
@@ -136,7 +140,8 @@ function WechatExpertIcon() {
...
@@ -136,7 +140,8 @@ function WechatExpertIcon() {
function
XPlatformExpertIcon
()
{
function
XPlatformExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M6 5.25h3.4l2.86 4.2 3.53-4.2H18l-4.72 5.61L18.5 18h-3.4l-3.11-4.55L8.1 18H6l5.02-5.97L6 5.25Z"
fill=
"currentColor"
/>
<
rect
x=
"4.25"
y=
"4.25"
width=
"15.5"
height=
"15.5"
rx=
"4"
fill=
"#050505"
/>
<
path
d=
"M7 6.9h3.35l2.34 3.24 2.84-3.24h1.5l-3.65 4.17L17.25 17h-3.3l-2.58-3.66L8.17 17H6.65l3.99-4.57L7 6.9Zm1.9 1.25 5.68 7.58h.76L9.69 8.15H8.9Z"
fill=
"#ffffff"
/>
</
svg
>
</
svg
>
);
);
}
}
...
@@ -144,8 +149,9 @@ function XPlatformExpertIcon() {
...
@@ -144,8 +149,9 @@ function XPlatformExpertIcon() {
function
TikTokExpertIcon
()
{
function
TikTokExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M13.75 4.25v8.5a3.75 3.75 0 1 1-2-3.3V7.1c1.42.05 2.63-.33 3.78-1.18.25-.19.62-.01.62.3v1.35c0 1.15.63 2.2 1.64 2.73.35.19.72.33 1.11.43"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"2"
/>
<
path
d=
"M12.85 4.35v8.65a3.85 3.85 0 1 1-2.05-3.39V7.23c1.43.05 2.68-.34 3.84-1.2.25-.19.62-.01.62.31v1.37c0 1.17.64 2.23 1.66 2.77.36.19.74.34 1.14.44"
fill=
"none"
stroke=
"#25F4EE"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"2.25"
/>
<
circle
cx=
"8.1"
cy=
"15.7"
r=
"2"
fill=
"currentColor"
opacity=
"0.24"
/>
<
path
d=
"M14.25 4.35v8.65a3.85 3.85 0 1 1-2.05-3.39V7.23c1.43.05 2.68-.34 3.84-1.2.25-.19.62-.01.62.31v1.37c0 1.17.64 2.23 1.66 2.77.36.19.74.34 1.14.44"
fill=
"none"
stroke=
"#FE2C55"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"2.25"
/>
<
path
d=
"M13.55 4.35v8.65a3.85 3.85 0 1 1-2.05-3.39V7.23c1.43.05 2.68-.34 3.84-1.2.25-.19.62-.01.62.31v1.37c0 1.17.64 2.23 1.66 2.77.36.19.74.34 1.14.44"
fill=
"none"
stroke=
"#111827"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.8"
/>
</
svg
>
</
svg
>
);
);
}
}
...
@@ -153,9 +159,10 @@ function TikTokExpertIcon() {
...
@@ -153,9 +159,10 @@ function TikTokExpertIcon() {
function
PosterExpertIcon
()
{
function
PosterExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M6 4.75A1.75 1.75 0 0 1 7.75 3h8.5A1.75 1.75 0 0 1 18 4.75v14.5A1.75 1.75 0 0 1 16.25 21h-8.5A1.75 1.75 0 0 1 6 19.25V4.75Z"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.8"
/>
<
path
d=
"M6 4.75A1.75 1.75 0 0 1 7.75 3h8.5A1.75 1.75 0 0 1 18 4.75v14.5A1.75 1.75 0 0 1 16.25 21h-8.5A1.75 1.75 0 0 1 6 19.25V4.75Z"
fill=
"#FDF2F8"
stroke=
"#EC4899"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.55"
/>
<
path
d=
"M8.6 8.2h6.8M8.6 11.4h6.8M8.6 14.6h4.4"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeWidth=
"1.7"
/>
<
path
d=
"M8.4 7.4h7.2v4.1H8.4z"
fill=
"#F59E0B"
opacity=
"0.9"
/>
<
circle
cx=
"14.9"
cy=
"14.7"
r=
"1.1"
fill=
"currentColor"
opacity=
"0.88"
/>
<
path
d=
"M8.5 14.1h4.8M8.5 16.55h3.25"
fill=
"none"
stroke=
"#DB2777"
strokeLinecap=
"round"
strokeWidth=
"1.35"
/>
<
circle
cx=
"15.1"
cy=
"15.65"
r=
"1.35"
fill=
"#2563EB"
/>
</
svg
>
</
svg
>
);
);
}
}
...
@@ -163,9 +170,10 @@ function PosterExpertIcon() {
...
@@ -163,9 +170,10 @@ function PosterExpertIcon() {
function
GeoExpertIcon
()
{
function
GeoExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
circle
cx=
"12"
cy=
"12"
r=
"7.25"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"1.8"
/>
<
circle
cx=
"11"
cy=
"11"
r=
"6.7"
fill=
"#DBEAFE"
stroke=
"#2563EB"
strokeWidth=
"1.55"
/>
<
path
d=
"M12 4.75c2.05 1.95 3.25 4.55 3.25 7.25S14.05 17.3 12 19.25C9.95 17.3 8.75 14.7 8.75 12S9.95 6.7 12 4.75Z"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"1.7"
/>
<
path
d=
"M11 4.3c1.9 1.8 3 4.2 3 6.7s-1.1 4.9-3 6.7C9.1 15.9 8 13.5 8 11s1.1-4.9 3-6.7Z"
fill=
"#D1FAE5"
stroke=
"#10B981"
strokeWidth=
"1.25"
/>
<
path
d=
"M5 12h14"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeWidth=
"1.6"
/>
<
path
d=
"M4.7 11h12.6"
fill=
"none"
stroke=
"#2563EB"
strokeLinecap=
"round"
strokeWidth=
"1.35"
/>
<
path
d=
"m15.8 15.8 3.1 3.1"
fill=
"none"
stroke=
"#059669"
strokeLinecap=
"round"
strokeWidth=
"1.9"
/>
</
svg
>
</
svg
>
);
);
}
}
...
@@ -173,10 +181,11 @@ function GeoExpertIcon() {
...
@@ -173,10 +181,11 @@ function GeoExpertIcon() {
function
LeadsExpertIcon
()
{
function
LeadsExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
circle
cx=
"12"
cy=
"12"
r=
"6.9"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"1.8"
/>
<
circle
cx=
"12"
cy=
"12"
r=
"7"
fill=
"#FEF2F2"
stroke=
"#EF4444"
strokeWidth=
"1.65"
/>
<
circle
cx=
"12"
cy=
"12"
r=
"3.6"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"1.7"
opacity=
"0.86"
/>
<
circle
cx=
"12"
cy=
"12"
r=
"4"
fill=
"#FFFBEB"
stroke=
"#F59E0B"
strokeWidth=
"1.45"
/>
<
circle
cx=
"12"
cy=
"12"
r=
"1.4"
fill=
"currentColor"
/>
<
circle
cx=
"12"
cy=
"12"
r=
"1.45"
fill=
"#EF4444"
/>
<
path
d=
"M12 3.8v2.1M20.2 12h-2.1M12 20.2v-2.1M3.8 12h2.1"
fill=
"none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeWidth=
"1.5"
/>
<
path
d=
"M12 3.8v2M20.2 12h-2M12 20.2v-2M3.8 12h2"
fill=
"none"
stroke=
"#DC2626"
strokeLinecap=
"round"
strokeWidth=
"1.35"
/>
<
path
d=
"m15.7 7.7 2.15-1.25M16.3 8.9h2.45"
fill=
"none"
stroke=
"#2563EB"
strokeLinecap=
"round"
strokeWidth=
"1.25"
/>
</
svg
>
</
svg
>
);
);
}
}
...
@@ -184,10 +193,10 @@ function LeadsExpertIcon() {
...
@@ -184,10 +193,10 @@ function LeadsExpertIcon() {
function
SalesChampionExpertIcon
()
{
function
SalesChampionExpertIcon
()
{
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M8 5.25h8v2.1a4 4 0 0 1-3 3.88v2.02h2.1a1.4 1.4 0 0 1 1.4 1.4V16H7.5v-1.35a1.4 1.4 0 0 1 1.4-1.4H11v-2.02a4 4 0 0 1-3-3.88v-2.1Z"
fill=
"
none"
stroke=
"currentColor"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.8
"
/>
<
path
d=
"M8 5.25h8v2.1a4 4 0 0 1-3 3.88v2.02h2.1a1.4 1.4 0 0 1 1.4 1.4V16H7.5v-1.35a1.4 1.4 0 0 1 1.4-1.4H11v-2.02a4 4 0 0 1-3-3.88v-2.1Z"
fill=
"
#FDE68A"
stroke=
"#F59E0B"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.55
"
/>
<
path
d=
"M8 6.2H5.9A1.65 1.65 0 0 0 4.25 7.85c0 1.74 1.41 3.15 3.15 3.15H8m8-4.8h2.1a1.65 1.65 0 0 1 1.65 1.65c0 1.74-1.41 3.15-3.15 3.15H16"
fill=
"none"
stroke=
"
currentColor"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.6
"
/>
<
path
d=
"M8 6.2H5.9A1.65 1.65 0 0 0 4.25 7.85c0 1.74 1.41 3.15 3.15 3.15H8m8-4.8h2.1a1.65 1.65 0 0 1 1.65 1.65c0 1.74-1.41 3.15-3.15 3.15H16"
fill=
"none"
stroke=
"
#D97706"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.45
"
/>
<
path
d=
"M9.8 19h4.4M8.9 16h6.2v3H8.9z"
fill=
"
currentColor"
opacity=
"0.18
"
/>
<
path
d=
"M9.8 19h4.4M8.9 16h6.2v3H8.9z"
fill=
"
#92400E"
opacity=
"0.24
"
/>
<
path
d=
"m12 7.55.68 1.38 1.52.22-1.1 1.07.26 1.51L12 11.03l-1.36.7.26-1.51-1.1-1.07 1.52-.22L12 7.55Z"
fill=
"
currentColor
"
/>
<
path
d=
"m12 7.55.68 1.38 1.52.22-1.1 1.07.26 1.51L12 11.03l-1.36.7.26-1.51-1.1-1.07 1.52-.22L12 7.55Z"
fill=
"
#F59E0B
"
/>
</
svg
>
</
svg
>
);
);
}
}
...
...
apps/ui/test/expertIconSource.test.ts
0 → 100644
View file @
bd61e8e6
import
test
from
"node:test"
import
assert
from
"node:assert/strict"
import
{
readFileSync
}
from
"node:fs"
const
uiPackageSource
=
readFileSync
(
new
URL
(
"../package.json"
,
import
.
meta
.
url
),
"utf8"
)
const
iconSource
=
readFileSync
(
new
URL
(
"../src/components/icons/AppIcons.tsx"
,
import
.
meta
.
url
),
"utf8"
)
function
functionBlock
(
name
:
string
):
string
{
const
start
=
iconSource
.
indexOf
(
`function
${
name
}
()`
)
assert
.
notEqual
(
start
,
-
1
,
`Missing icon function:
${
name
}
`
)
const
nextFunction
=
iconSource
.
indexOf
(
"
\n
function "
,
start
+
1
)
const
renderFunction
=
iconSource
.
indexOf
(
"
\n
export function renderExpertIcon"
,
start
+
1
)
const
candidates
=
[
nextFunction
,
renderFunction
].
filter
((
index
)
=>
index
>
start
)
const
end
=
Math
.
min
(...
candidates
)
assert
.
notEqual
(
end
,
Infinity
,
`Missing block end for icon function:
${
name
}
`
)
return
iconSource
.
slice
(
start
,
end
)
}
test
(
"expert icon source test is exposed as a package script"
,
()
=>
{
const
uiPackage
=
JSON
.
parse
(
uiPackageSource
)
as
{
scripts
?:
Record
<
string
,
string
>
}
assert
.
equal
(
uiPackage
.
scripts
?.[
"test:icons"
],
"node --test test/expertIconSource.test.ts"
)
})
test
(
"browser expert icon avoids reusable svg ids"
,
()
=>
{
const
block
=
functionBlock
(
"BrowserExpertIcon"
)
assert
.
doesNotMatch
(
block
,
/<defs|id=|url
\(
#/
,
"BrowserExpertIcon should avoid duplicate SVG ids across repeated renders"
)
})
test
(
"non-redbook and non-douyin expert icons use fixed brand colors"
,
()
=>
{
const
expectedColorsByIcon
=
new
Map
([
[
"BrowserExpertIcon"
,
[
"#3B82F6"
,
"#22C55E"
]],
[
"PlannerExpertIcon"
,
[
"#F97316"
,
"#2563EB"
]],
[
"ZhihuExpertIcon"
,
[
"#1769FF"
,
"#ffffff"
]],
[
"WechatExpertIcon"
,
[
"#22C55E"
,
"#16A34A"
]],
[
"XPlatformExpertIcon"
,
[
"#050505"
,
"#ffffff"
]],
[
"TikTokExpertIcon"
,
[
"#25F4EE"
,
"#FE2C55"
]],
[
"PosterExpertIcon"
,
[
"#EC4899"
,
"#F59E0B"
]],
[
"GeoExpertIcon"
,
[
"#2563EB"
,
"#10B981"
]],
[
"LeadsExpertIcon"
,
[
"#EF4444"
,
"#F59E0B"
]],
[
"SalesChampionExpertIcon"
,
[
"#F59E0B"
,
"#FDE68A"
]]
])
for
(
const
[
iconName
,
colors
]
of
expectedColorsByIcon
)
{
const
block
=
functionBlock
(
iconName
)
assert
.
doesNotMatch
(
block
,
/currentColor/
,
`
${
iconName
}
should not inherit a monochrome text color`
)
for
(
const
color
of
colors
)
{
assert
.
match
(
block
,
new
RegExp
(
color
,
"i"
),
`
${
iconName
}
should include
${
color
}
`
)
}
}
})
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment