feat: simplify HiCAD project management
This commit is contained in:
parent
2c81486862
commit
43fd3d258a
@ -110,7 +110,11 @@ function replaceSource(relativePath, before, after) {
|
||||
if (!after) {
|
||||
return;
|
||||
}
|
||||
throw new Error(`Official source changed; patch failed for ${relativePath}`);
|
||||
throw new Error(
|
||||
`Official source changed; patch failed for ${relativePath}; expected ${JSON.stringify(
|
||||
before.slice(0, 160)
|
||||
)}`
|
||||
);
|
||||
}
|
||||
writeFileSync(filePath, source.replace(before, after));
|
||||
}
|
||||
@ -162,6 +166,10 @@ function patchOfficialSource() {
|
||||
path.join(customizationDir, "FitAllButton.tsx.template"),
|
||||
path.join(sourceDir, "src/components/FitAllButton.tsx")
|
||||
);
|
||||
copyFileSync(
|
||||
path.join(customizationDir, "HicadProjectExperience.tsx.template"),
|
||||
path.join(sourceDir, "src/components/HicadProjectExperience.tsx")
|
||||
);
|
||||
replaceSource(
|
||||
"index.html",
|
||||
' <meta name="description" content="An open-source CAD modeling tool from the future by Zoo." />',
|
||||
@ -307,6 +315,284 @@ function patchOfficialSource() {
|
||||
" {displayedName !== user.email && (",
|
||||
" {!isHiCadEmbedded && displayedName !== user.email && ("
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppHeader.tsx",
|
||||
"import ProjectSidebarMenu from '@src/components/ProjectSidebarMenu'\n",
|
||||
"import { HicadAutosaveStatus } from '@src/components/HicadProjectExperience'\nimport ProjectSidebarMenu from '@src/components/ProjectSidebarMenu'\n"
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppHeader.tsx",
|
||||
` <div className="flex items-center gap-2 py-1.5 ml-auto">
|
||||
{children ||`,
|
||||
` <div className="flex items-center gap-2 py-1.5 ml-auto">
|
||||
{project && <HicadAutosaveStatus />}
|
||||
{children ||`
|
||||
);
|
||||
replaceSource(
|
||||
"src/routes/Home.tsx",
|
||||
"import AppProjectCard from '@src/components/AppProjectCard/AppProjectCard'\n",
|
||||
"import AppProjectCard from '@src/components/AppProjectCard/AppProjectCard'\nimport { HicadProjectDashboardHeading } from '@src/components/HicadProjectExperience'\n"
|
||||
);
|
||||
replaceSource(
|
||||
"src/routes/Home.tsx",
|
||||
` projectSearchKeybinding={projectSearchKeybinding}
|
||||
className="col-start-2 -col-end-1"`,
|
||||
` projectSearchKeybinding={projectSearchKeybinding}
|
||||
projectCount={scopedHomeProjectEntries.length}
|
||||
className="col-start-2 -col-end-1"`
|
||||
);
|
||||
replaceSource(
|
||||
"src/routes/Home.tsx",
|
||||
` projectSearchKeybinding?: string
|
||||
}`,
|
||||
` projectSearchKeybinding?: string
|
||||
projectCount: number
|
||||
}`
|
||||
);
|
||||
replaceSource(
|
||||
"src/routes/Home.tsx",
|
||||
` projectSearchKeybinding,
|
||||
...rest`,
|
||||
` projectSearchKeybinding,
|
||||
projectCount,
|
||||
...rest`
|
||||
);
|
||||
replaceSource(
|
||||
"src/routes/Home.tsx",
|
||||
` <div className="flex gap-8 items-center">
|
||||
<div className="flex flex-col gap-1">
|
||||
{library && showLibraryBackLink && (
|
||||
<Link
|
||||
to={PATHS.HOME}
|
||||
className="text-sm text-chalkboard-70 underline underline-offset-2 dark:text-chalkboard-30"
|
||||
>
|
||||
All libraries
|
||||
</Link>
|
||||
)}
|
||||
<h1 className="text-3xl font-bold">{title}</h1>
|
||||
</div>
|
||||
</div>`,
|
||||
` <div className="flex min-w-0 flex-1 gap-8 items-center">
|
||||
{library && showLibraryBackLink && (
|
||||
<Link
|
||||
to={PATHS.HOME}
|
||||
className="text-sm text-chalkboard-70 underline underline-offset-2 dark:text-chalkboard-30"
|
||||
>
|
||||
返回全部项目
|
||||
</Link>
|
||||
)}
|
||||
<HicadProjectDashboardHeading projectCount={projectCount} />
|
||||
</div>`
|
||||
);
|
||||
replaceSource(
|
||||
"src/routes/Home.tsx",
|
||||
`<div className="flex flex-col md:flex-row gap-4 justify-between md:items-center select-none">`,
|
||||
`<div className="flex flex-col gap-5 justify-between select-none">`
|
||||
);
|
||||
replaceSource(
|
||||
"src/routes/Home.tsx",
|
||||
`<div className="flex flex-col sm:flex-row gap-2 sm:items-center">`,
|
||||
`<div className="flex w-full flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
"import { ContextMenu, ContextMenuItem } from '@src/components/ContextMenu'\n",
|
||||
"import { ContextMenu, ContextMenuItem } from '@src/components/ContextMenu'\nimport { CustomIcon } from '@src/components/CustomIcon'\n"
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
` {
|
||||
local: 'Local',
|
||||
'cloud-only': 'Cloud-only',
|
||||
syncing: 'Syncing',
|
||||
synced: 'Synced',
|
||||
conflicted: 'Conflicted',
|
||||
}`,
|
||||
` {
|
||||
local: '已保存',
|
||||
'cloud-only': '云端项目',
|
||||
syncing: '保存中',
|
||||
synced: '已保存',
|
||||
conflicted: '需要处理',
|
||||
}`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
`function getDisplayedTime(dateTimeMs: number) {
|
||||
const date = new Date(dateTimeMs)
|
||||
const startOfToday = new Date()
|
||||
startOfToday.setHours(0, 0, 0, 0)
|
||||
return date.getTime() < startOfToday.getTime()
|
||||
? date.toLocaleDateString()
|
||||
: date.toLocaleTimeString()
|
||||
}`,
|
||||
`function getDisplayedTime(dateTimeMs: number) {
|
||||
const date = new Date(dateTimeMs)
|
||||
const now = new Date()
|
||||
const isToday =
|
||||
date.getFullYear() === now.getFullYear() &&
|
||||
date.getMonth() === now.getMonth() &&
|
||||
date.getDate() === now.getDate()
|
||||
|
||||
if (isToday) {
|
||||
return \`今天 \${date.toLocaleTimeString('zh-CN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
})}\`
|
||||
}
|
||||
|
||||
return date.toLocaleDateString('zh-CN', {
|
||||
year: date.getFullYear() === now.getFullYear() ? undefined : 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})
|
||||
}`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
` {project.kclFileCount !== undefined && (
|
||||
<span className="px-2 text-chalkboard-60 text-xs">
|
||||
<span data-testid="project-file-count">{project.kclFileCount}</span>{' '}
|
||||
file{project.kclFileCount === 1 ? '' : 's'}{' '}
|
||||
{(project.directoryCount ?? 0) > 0 && (
|
||||
<>
|
||||
{'/ '}
|
||||
<span data-testid="project-folder-count">
|
||||
{project.directoryCount}
|
||||
</span>{' '}
|
||||
folder{project.directoryCount === 1 ? '' : 's'}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
<span className="px-2 text-chalkboard-60 text-xs">
|
||||
Edited{' '}
|
||||
<span data-testid="project-edit-date">
|
||||
{displayedProject.modified
|
||||
? getDisplayedTime(displayedProject.modified)
|
||||
: 'never'}
|
||||
</span>
|
||||
</span>`,
|
||||
` <div className="flex items-center justify-between gap-2 px-2 text-xs text-chalkboard-60">
|
||||
<span>
|
||||
{project.kclFileCount !== undefined ? (
|
||||
<>
|
||||
<span data-testid="project-file-count">{project.kclFileCount}</span>{' '}
|
||||
个模型文件
|
||||
</>
|
||||
) : (
|
||||
'CAD 项目'
|
||||
)}
|
||||
</span>
|
||||
<span className="truncate">
|
||||
最近编辑{' '}
|
||||
<span data-testid="project-edit-date">
|
||||
{displayedProject.modified
|
||||
? getDisplayedTime(displayedProject.modified)
|
||||
: '暂无记录'}
|
||||
</span>
|
||||
</span>
|
||||
</div>`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
` title="Delete Project"`,
|
||||
` title="删除项目"`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
` <p className="my-4 text-wrap break-words">
|
||||
This will permanently delete "{projectName || 'this file'}
|
||||
".
|
||||
</p>
|
||||
<p className="my-4 text-wrap break-words">
|
||||
Are you sure you want to delete "{projectName || 'this file'}
|
||||
"? This action cannot be undone.
|
||||
</p>`,
|
||||
` <p className="my-4 text-wrap break-words">
|
||||
确定删除“{projectName || '此项目'}”吗?
|
||||
</p>
|
||||
<p className="my-4 text-wrap break-words text-sm text-chalkboard-70 dark:text-chalkboard-30">
|
||||
项目及其全部模型文件将被永久删除,此操作无法撤销。
|
||||
</p>`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
` title={projectName}
|
||||
titleText={projectName}`,
|
||||
` title={
|
||||
<span className="flex min-w-0 items-center justify-between gap-2">
|
||||
<span className="truncate font-semibold">{projectName}</span>
|
||||
{canOpen && (
|
||||
<span className="flex-none text-xs font-medium text-primary opacity-0 transition-opacity group-hover:opacity-100">
|
||||
继续设计
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
titleText={projectName}`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
` thumbnailUrl={imageUrl}
|
||||
badges={badges}`,
|
||||
` thumbnailUrl={imageUrl}
|
||||
thumbnailAlt={projectName ? \`\${projectName} 的模型预览\` : 'CAD 项目预览'}
|
||||
thumbnailFallback={
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-2 text-chalkboard-50 dark:text-chalkboard-50">
|
||||
<CustomIcon name="model" className="h-8 w-8" />
|
||||
<span className="text-xs">暂无模型预览</span>
|
||||
</div>
|
||||
}
|
||||
badges={badges}`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
` Rename project`,
|
||||
` 重命名`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/AppProjectCard/AppProjectCard.tsx",
|
||||
` Delete project`,
|
||||
` 删除项目`
|
||||
);
|
||||
replaceSource(
|
||||
"src/lib/commandBarConfigs/projectsCommandConfig.ts",
|
||||
` displayName: \`Create project\`,
|
||||
description: 'Create a project',`,
|
||||
` displayName: \`新建 CAD 项目\`,
|
||||
description: '输入项目名称即可开始,设计内容会自动保存。',`
|
||||
);
|
||||
replaceSource(
|
||||
"src/lib/commandBarConfigs/projectsCommandConfig.ts",
|
||||
` displayName: 'Title',
|
||||
required: true,`,
|
||||
` displayName: '项目名称',
|
||||
description: '使用容易识别的名称,例如“五金连接安装板”。',
|
||||
required: true,`
|
||||
);
|
||||
replaceSource(
|
||||
"src/lib/commandBarConfigs/projectsCommandConfig.ts",
|
||||
` displayName: 'Library',
|
||||
required: () => createProjectLibraryOptions().length > 1,`,
|
||||
` displayName: '保存位置',
|
||||
required: () => createProjectLibraryOptions().length > 1,`
|
||||
);
|
||||
replaceSource(
|
||||
"src/lib/commandBarConfigs/projectsCommandConfig.ts",
|
||||
` defaultValue: defaultProjectFolderNameSnapshot,`,
|
||||
` defaultValue: () => '',`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/CommandBar/CommandBarBasicInput.tsx",
|
||||
` placeholder="Enter a value"`,
|
||||
` placeholder={
|
||||
commandBarState.context.selectedCommand?.name === 'Create project'
|
||||
? '例如:五金连接安装板'
|
||||
: '请输入'
|
||||
}`
|
||||
);
|
||||
replaceSource(
|
||||
"src/components/environment/Environment.tsx",
|
||||
" new URL(env().VITE_KITTYCAD_WEBSOCKET_URL ?? ''),",
|
||||
|
||||
53
tests/routes/hicad-project-experience.test.ts
Normal file
53
tests/routes/hicad-project-experience.test.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
|
||||
const projectRoot = process.cwd()
|
||||
|
||||
test.describe('HiCAD project management experience', () => {
|
||||
test('vendors the project dashboard and autosave status', () => {
|
||||
const script = fs.readFileSync(
|
||||
path.join(projectRoot, 'scripts/vendor-zoo-modeling-app.mjs'),
|
||||
'utf8'
|
||||
)
|
||||
const experience = fs.readFileSync(
|
||||
path.join(
|
||||
projectRoot,
|
||||
'vendor/zoo-modeling-app/HicadProjectExperience.tsx.template'
|
||||
),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
expect(script).toContain('HicadProjectExperience.tsx.template')
|
||||
expect(script).toContain('projectCount={scopedHomeProjectEntries.length}')
|
||||
expect(script).toContain('thumbnailFallback')
|
||||
expect(script).toContain('最近编辑')
|
||||
expect(experience).toContain('我的 CAD 项目')
|
||||
expect(experience).toContain('编辑内容会自动保存')
|
||||
expect(experience).toContain('自动保存已开启')
|
||||
expect(experience).toContain('data-testid="hicad-create-project"')
|
||||
expect(experience).toContain('data-testid="hicad-create-from-sample"')
|
||||
})
|
||||
|
||||
test('keeps new project creation simple and understandable', () => {
|
||||
const script = fs.readFileSync(
|
||||
path.join(projectRoot, 'scripts/vendor-zoo-modeling-app.mjs'),
|
||||
'utf8'
|
||||
)
|
||||
const localization = fs.readFileSync(
|
||||
path.join(projectRoot, 'vendor/zoo-modeling-app/localization.ts'),
|
||||
'utf8'
|
||||
)
|
||||
|
||||
expect(script).toContain('新建 CAD 项目')
|
||||
expect(script).toContain("displayName: '项目名称'")
|
||||
expect(script).toContain('例如:五金连接安装板')
|
||||
expect(script).toContain("defaultValue: () => ''")
|
||||
expect(localization).toContain("['Submit', '确认']")
|
||||
expect(localization).toContain("['Continue', '下一步']")
|
||||
expect(localization).toContain("['Step back', '返回上一步']")
|
||||
expect(localization).toContain('还没有项目,点击“新建项目”开始设计。')
|
||||
expect(localization).toContain("[data-testid='home-sidebar']")
|
||||
})
|
||||
})
|
||||
68
vendor/zoo-modeling-app/HicadProjectExperience.tsx.template
vendored
Normal file
68
vendor/zoo-modeling-app/HicadProjectExperience.tsx.template
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
import { CustomIcon } from '@src/components/CustomIcon'
|
||||
|
||||
export function HicadProjectDashboardHeading({
|
||||
projectCount,
|
||||
}: {
|
||||
projectCount: number
|
||||
}) {
|
||||
const startCreatingProject = () => {
|
||||
document
|
||||
.querySelector<HTMLButtonElement>('[data-testid="home-new-file"]')
|
||||
?.click()
|
||||
}
|
||||
const startFromSample = () => {
|
||||
document
|
||||
.querySelector<HTMLButtonElement>(
|
||||
'[data-testid="home-create-from-sample"]'
|
||||
)
|
||||
?.click()
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex min-w-0 flex-1 flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"
|
||||
data-testid="hicad-project-dashboard-heading"
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className="mb-1 text-xs font-medium text-primary">HiCAD 项目空间</p>
|
||||
<h1 className="text-3xl font-bold">我的 CAD 项目</h1>
|
||||
<p className="mt-1 text-sm text-chalkboard-70 dark:text-chalkboard-30">
|
||||
共 {projectCount} 个项目,编辑内容会自动保存
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-none items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={startFromSample}
|
||||
className="inline-flex h-10 items-center justify-center gap-2 rounded-sm border border-chalkboard-30 bg-chalkboard-10 px-3 text-sm font-medium text-chalkboard-90 transition-colors hover:border-primary/50 hover:bg-primary/5 dark:border-chalkboard-70 dark:bg-chalkboard-90 dark:text-chalkboard-20"
|
||||
data-testid="hicad-create-from-sample"
|
||||
>
|
||||
<CustomIcon name="file" className="h-4 w-4" />
|
||||
从示例开始
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={startCreatingProject}
|
||||
className="inline-flex h-10 items-center justify-center gap-2 rounded-sm border border-primary bg-primary px-4 text-sm font-semibold text-white transition-colors hover:brightness-110 focus-visible:outline-appForeground"
|
||||
data-testid="hicad-create-project"
|
||||
>
|
||||
<CustomIcon name="plus" className="h-5 w-5" />
|
||||
新建项目
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function HicadAutosaveStatus() {
|
||||
return (
|
||||
<span
|
||||
className="mr-1 inline-flex h-7 items-center gap-1.5 rounded-md border border-chalkboard-30 bg-chalkboard-10/80 px-2 text-xs text-chalkboard-70 dark:border-chalkboard-70 dark:bg-chalkboard-100/50 dark:text-chalkboard-30"
|
||||
data-testid="hicad-autosave-status"
|
||||
title="项目内容会自动保存到当前浏览器"
|
||||
>
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-succeed-60" />
|
||||
自动保存已开启
|
||||
</span>
|
||||
)
|
||||
}
|
||||
58
vendor/zoo-modeling-app/localization.ts
vendored
58
vendor/zoo-modeling-app/localization.ts
vendored
@ -23,6 +23,8 @@ const translations = new Map<string, string>([
|
||||
['Connected', '已连接'],
|
||||
['Connecting and setting up scene...', '正在连接并初始化场景...'],
|
||||
['Create a new project', '新建项目'],
|
||||
['Create project', '新建项目'],
|
||||
['Create from a sample', '从示例开始'],
|
||||
[
|
||||
'Create a gear with 10 teeth and use sensible defaults for everything else...',
|
||||
'创建一个 10 齿齿轮,其余参数使用合理默认值...',
|
||||
@ -31,6 +33,7 @@ const translations = new Map<string, string>([
|
||||
['Design using traditional point-and-click', '使用传统点击方式设计'],
|
||||
['Dismiss', '关闭'],
|
||||
['Edit', '编辑'],
|
||||
['Edited', '最近编辑'],
|
||||
['Export', '导出'],
|
||||
['Export part', '导出零件'],
|
||||
['Extrude', '拉伸'],
|
||||
@ -67,9 +70,28 @@ const translations = new Map<string, string>([
|
||||
['No selection.', '未选择。'],
|
||||
['Offset Plane', '偏移基准面'],
|
||||
['Open project', '打开项目'],
|
||||
['Project Libraries', '我的 CAD 项目'],
|
||||
['Projects', '项目'],
|
||||
['Project Files', '项目文件'],
|
||||
['Publish', '发布'],
|
||||
['Redo', '重做'],
|
||||
['Rename project', '重命名'],
|
||||
['Delete project', '删除项目'],
|
||||
['Search projects', '搜索项目'],
|
||||
['Sort by', '排序'],
|
||||
['Name', '名称'],
|
||||
['Last Modified', '最近编辑'],
|
||||
['No projects found', '还没有项目'],
|
||||
[', ready to make your first one?', ',点击“新建项目”开始设计。'],
|
||||
[
|
||||
'No projects found, ready to make your first one?',
|
||||
'还没有项目,点击“新建项目”开始设计。',
|
||||
],
|
||||
['Submit', '确认'],
|
||||
['Continue', '下一步'],
|
||||
['Step back', '返回上一步'],
|
||||
['Enter a value', '请输入'],
|
||||
['Cancel', '取消'],
|
||||
['Revolve', '旋转'],
|
||||
['Settings', '设置'],
|
||||
['Shell', '抽壳'],
|
||||
@ -158,7 +180,11 @@ function translateElement(element: Element) {
|
||||
for (const attribute of translatableAttributes) {
|
||||
const value = normalizeLookupKey(element.getAttribute(attribute) || '')
|
||||
if (!value) continue
|
||||
const translated = translations.get(value)
|
||||
const translated =
|
||||
translations.get(value) ||
|
||||
(attribute === 'placeholder' && value.startsWith('Search projects')
|
||||
? '搜索项目'
|
||||
: undefined)
|
||||
if (translated) element.setAttribute(attribute, translated)
|
||||
}
|
||||
}
|
||||
@ -203,6 +229,36 @@ export function installHiCadLocalization() {
|
||||
html[data-hicad-embedded='true'] [data-hicad-official-account-link] {
|
||||
display: none !important;
|
||||
}
|
||||
html[data-hicad-embedded='true'] [data-testid='home-sidebar'],
|
||||
html[data-hicad-embedded='true'] [data-testid='home-new-file'],
|
||||
html[data-hicad-embedded='true'] [data-testid='environment-subdomain-bar'],
|
||||
html[data-hicad-embedded='true'] [data-testid='cloud-sync-status'] {
|
||||
display: none !important;
|
||||
}
|
||||
html[data-hicad-embedded='true'] .home-layout {
|
||||
display: block !important;
|
||||
column-gap: 2rem;
|
||||
margin-top: clamp(1.5rem, 5vh, 4rem);
|
||||
}
|
||||
html[data-hicad-embedded='true'] [data-testid='home-header'] > p:has(
|
||||
[data-testid='project-directory-settings-link']
|
||||
) {
|
||||
display: none !important;
|
||||
}
|
||||
html[data-hicad-embedded='true'] [data-testid='home-section'] {
|
||||
margin-top: 1.5rem;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
html[data-hicad-embedded='true'] [data-testid='home-section'] > ul {
|
||||
align-content: start;
|
||||
}
|
||||
html[data-hicad-embedded='true'] [data-testid='project-status-badge'] {
|
||||
box-shadow: 0 1px 2px rgb(0 0 0 / 8%);
|
||||
}
|
||||
html[data-hicad-embedded='true'] [data-testid='project-directory-settings-link'] {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user