@scalio-cloud / ui
Scalio应用程序(Nexus, Rosetta, Foundry, Citadel, Compass,后来的Pulse)的共享外观和感觉:
“控制室”的设计标志,一个顺风预设和字体,从应用程序本身提供。
规范: design/UISPEC.md 在 Scalio-Cloud/scalio-docs (单一副本)。
| 文件 | 它是什么? |
|---|---|
tokens.css | shadcn/ui CSS变量,亮起 :root,暗的 .dark (克莱因蓝 #002FA7,半径4px) |
fonts.css | @font-face 用于Chivo, IBM Plex Sans, JetBrains Mono (woff2,拉丁语,SIL OFL 1.1)。没有谷歌字体请求 |
base.css | 正文/标题/代码字体系列 |
shell.css | 应用标题、用户菜单、页面标题和版本(scalio-* 类) |
nav.css | 侧边栏,电话抽屉,切换器,上下文选择,支持抽屉,页面块 |
index.css | 所有这些都在一个导入中 |
react/ | React的共享应用头(见下文),除了React之外没有依赖项 |
tailwind-preset.js | 颜色,字体,半径,手风琴动画, brand 的颜色, h-switcher, h-header, w-sidebar |
tokens.js | 没有CSS的代码的原始值(图表、电子邮件、pdf) |
在应用程序中使用它(CRA/craco或Vite)
// tailwind.config.js
module.exports = {
presets: [require("@scalio-cloud/ui/tailwind-preset")],
content: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
plugins: [require("tailwindcss-animate")],
};
/* src/index.css, first line */
@import "@scalio-cloud/ui/index.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
然后删除应用程序自己的 :root { --... } / .dark { --... } 块和谷歌字体 @import / <link>.
类等 bg-primary, rounded-sm, font-display 保持工作不变。
应用头(React)
每个应用都在右侧显示相同的标题,总是按照这个顺序:(应用操作),状态,语言,主题,
支持,《阿凡达》。
部件只有在应用程序通过其处理程序时才会出现(Compass没有暗模式,所以没有) onThemeChange)。
import { AppHeader, PageHeader, VersionTag, manualUrl } from "@scalio-cloud/ui/react";
<AppHeader
brand={{ mark: "N", name: "Scalio Nexus", sub: "Dev" }} // or your own element
onMenu={() => setMobileNavOpen(true)} // hamburger below 768px
left={<TenantSelect />} // after the brand
actions={<LocalePopover />} // right side, before the fixed group
status={<ApiStatus />} // hidden below 768px
lang={lang} onLangChange={setLang} // one click, shows the other language
theme={theme} themeModes={["light", "dark", "system"]} onThemeChange={setTheme}
docsUrl={manualUrl("nexus", { stage, lang })} // book icon, opens the manual in a new tab
onSupport={() => setSupportOpen(true)}
user={{ name, email, role }} onLogout={logout}
menuItems={[{ label: "Profil", icon: User, onSelect: () => nav("/profile"), testId: "profile-link" }]}
testIds={{ logout: "logout-button" }} // keep an app's existing test ids
/>
<PageHeader eyebrow="Verwaltung" title="Benutzer" subtitle="..." actions={<Button>Neu</Button>} />
<VersionTag version="1.2.4" commit="3f2a1bc" /> // bottom of the sidebar
文本是内置的 lang de 和 en 并且可以被覆盖 labels。默认测试id
(language-toggle-btn, theme-toggle-btn, support-ticket-trigger, user-avatar-btn, logout-btn)都在
TEST_IDS。组件是普通的CommonJS React.createElement,所以CRA、Vite和Jest不需要额外的配置。
图标是内联的(Lucide, ISC,参见 licenses/)。
侧边栏,切换器和页面块(React)
每个应用程序都有相同的帧(UISPEC“导航”):切换器,标题与上下文选择在左边,然后 侧边栏搜索,最多一个主要操作,“Übersicht”,最多三个部分和“Verwaltung”最后 (Protokoll, Konten und rolen, einstein)配置文件和个人页面只存在于用户菜单中。应用程序 将其导航描述为数据;侧边栏、电话抽屉和(后来的)搜索读取相同的列表。
import { NavLink } from "react-router-dom";
import { AppShell, Sidebar, ProductSwitcher, ContextSelect, SupportDrawer, useScalioApps,
KpiTile, EmptyState, Loading, Notice, StatusBadge, LABELS } from "@scalio-cloud/ui/react";
const L = LABELS[lang];
const NAV = { sections: [
{ id: "main", items: [{ to: "/", label: L.overview, icon: LayoutGrid, end: true }] },
{ id: "compliance", label: t("Compliance"), items: [
{ to: "/drifts", label: t("Drifts"), icon: GitCompareArrows, badge: open || null, badgeTone: "error" },
] },
{ id: "admin", label: L.administration, items: [
{ to: "/audit", label: L.auditLog, icon: ScrollText },
{ to: "/accounts", label: L.accounts, icon: Users, hidden: !isAdmin }, // hidden items and empty sections disappear
{ to: "/settings", label: L.settings, icon: SlidersHorizontal },
] },
] };
const apps = useScalioApps(() => api.get("/apps").then((r) => r.data)); // once per page load
<AppShell
switcher={apps.features.switcher && <ProductSwitcher current="citadel" links={apps.links} stage={apps.stage} sso={sso} lang={lang} />}
header={<AppHeader ... onMenu={() => setDrawer(true)}
left={<ContextSelect label={L.tenant} value={tenantId} options={tenants} onChange={select} hint={domain} testId="tenant-switcher" />} />}
sidebar={<Sidebar nav={NAV} link={NavLink} lang={lang} version={VERSION} commit={COMMIT}
search={{ onOpen: openPalette, shortcut: "⌘K" }} primaryAction={{ label: t("Scan starten"), icon: Play, onSelect: scan }} />}
drawerOpen={drawer} onDrawerClose={() => setDrawer(false)} // phones: same sidebar in a drawer (test ids with "m-")
drawerTop={<ContextSelect ... testId="m-tenant-switcher" />} // below 640px the header hides the select, the drawer shows it
>
<Routes>...</Routes>
</AppShell>
<SupportDrawer open={supportOpen} onClose={...} lang={lang} client={supportApi(api)} page={location.pathname}
context={[["URL", location.pathname], [L.tenant, current?.name]]} />
与 client 抽屉里有“Neue Anfrage”标签(类别、主题、描述、技术细节)和
“我的票”(列表、回答、回复、评分);机票到Compass (scalio-docs ADR)的中央服务台
0022),后端路由来自scalio-connect scalio_connect.support.web. supportApi(api) 取应用程序的
axios实例。设置卡 <SupportConnection client={supportApi(api)} lang={lang} /> 连接
安装使用一次性代码(admins)。没有 client 抽屉里保存着旧的单张表格 onSubmit.
supportApi(api, { ideas: true }) 添加标签“Ideen”(Scalio Ideen, ADR 0050;后端路由器)
scalio_connect.support.ideas_web.ideas_router(scalio-connect 0.8.0):应用程序的想法与投票和评论和
“Idee vorschlagen”也有类似的想法;然后是“unsch”类别。
设置卡 <BeaconConnection client={peersApi(api)} lang={lang} /> 将应用程序与Scalio Beacon配对
(地址和一次性代码来自Beacon, Einstellungen -> Verbundene Apps);然后将调查结果交给Beacon公司。后端:
scalio-connect 0.8.0 peers.web.admin_router.
link 是路由器的 NavLink (就 to, end,一个 className 功能和 onClick); 没有它
工具条显示普通链接并将项目标记为 activePath。项目test id默认为 nav-<path>
(nav-overview 用于 /),或设置 testId。页面: KpiTile (语气 error =红色数字),
EmptyState (孩子=下一步), Loading, Notice (error 红色=费勒; warn/important 橙色=
wichtig, ok/success 绿色=融化; info/action 蓝色=行动; banner =全宽), StatusBadge
(音调 neutral, running, ok, warn, error), PageHeader 与 back 查看详细页面。
属于一个菜单项的页面保留自己的地址:该菜单项将它们列在其中 match
({ to: "/groups", label: "Gruppen", match: ["/teams", "/distribution-lists"] },通过 activePath 与
link),每页显示 <SubNav items={[{ to: "/groups", label: "Sicherheit", end: true }, ...]} link={NavLink} />
在上面。一个有两个工作区域(Nexus Identity和HR)的应用程序 <WorkspaceSwitcher options value onSelect />
进入侧边栏 top.
侧边栏中的页面操作 (从0.18.0开始,ispec第2节): <Sidebar pageActions ... /> 保持Übersicht (
第一部分的第一项(没有标题),并在它的正下方添加一个插槽。页面将其操作封装在
<PageActions>;从768px他们移动到槽,在手机上,他们留在页眉。槽位不接受
没有动作的页面空间;通过操作,它将菜单向下推。
<PageHeader title="Kontakte" actions={<PageActions><Button onClick={add}><Plus /> Kontakt</Button></PageActions>} />
<PageHeader title={doc.number} actions={<PageActions><Button variant="outline" asChild><Link to="/invoices"><ArrowLeft /> Zurück</Link></Button></PageActions>} />
只有开始或引导的操作才会出现在那里(新建,导入,扫描,扫描<e:1>),每页最多两个。
没有“新”/“新”的标签:+图标表示它(“+ Kontakt”,而不是“+ Neuer Kontakt”)。
过滤器,搜索,标签和specichern留在页面上。有自己侧边栏位置的应用程序 <ActionSlot /> 他们自己。
设置页面(React)
设置的每个区域都有自己的地址(/settings/<area>); 应用程序的路由器会选择该区域
布局绘制子导航,面包屑(“Einstellungen›Verbindungen”)和标题。组 general,
connections, notifications, security 和 system 得到他们的名字 LABELS 一直坐在这里
订单,应用程序自己的组(与 label)跟在“Allgemein”后面。隐藏区域和空组消失在下面
三个领域(minItems),没有子导航。搜索将查看标签、提示、组和 keywords;
search={false} 忽略它, search={<OwnSearch />} 将应用程序自己的搜索放在那里。
import { SettingsLayout, SettingsSection } from "@scalio-cloud/ui/react";
const GROUPS = [
{ id: "general", items: [{ id: "general", to: "/settings/general", label: "Instanz", hint: "Name, Sprache, Zeitzone" }] },
{ id: "flows", label: "Abläufe", items: [{ id: "onboarding", to: "/settings/onboarding", label: "Onboarding" }] },
{ id: "connections", items: [{ id: "sync", to: "/settings/sync", label: "Synchronisierung", keywords: ["Intervall"] }] },
{ id: "system", items: [{ id: "updates", to: "/settings/updates", label: "Updates", hidden: saas }] },
];
<SettingsLayout groups={GROUPS} active={area} link={NavLink} onSelect={(i) => navigate(i.to)} lang={lang}>
<SettingsSection id="cloud-sync" title="Cloud-Synchronisierung" description="Intervall und Umfang."
footer={<Button onClick={save}>Speichern</Button>}>
...form fields (shadcn)...
</SettingsSection>
</SettingsLayout>
登录页面和配置文件(React)
所有登录页面都使用相同的框架和顺序:Microsoft先,然后是“order mit E-Mail”,密码字段为 在它下面,是提交按钮。忘记密码,重置,验证电子邮件和两个步骤是 他们自己的页面在同一个框架里。只显示应用程序可以做什么(仅在Compass中注册,演示帐户仅限) 在演示模式下)。
import { AuthLayout, AuthHeader, MicrosoftButton, AuthDivider, AuthMessage, ProfileSection } from "@scalio-cloud/ui/react";
<AuthLayout
brand={{ mark: "N", name: "Scalio Nexus", claim: "Alle Konten aus einer Konsole verwalten.",
features: ["Onboarding", "Ändern", "Offboarding"] }}
lang={lang} onLangChange={setLang} // top right
theme={theme} onThemeChange={setTheme} // optional
footer={<VersionTag version={v} />} // optional
>
<AuthHeader eyebrow="Zugang" title="Anmelden" subtitle="..." />
<MicrosoftButton href="/api/auth/sso/login" lang={lang} disabled={!ssoReady} hint="..." />
<AuthDivider lang={lang} />
<form>...</form>
<AuthMessage kind="error">{error}</AuthMessage>
</AuthLayout>
// Profile page: the same sections in every app, leave out what an app does not need.
<ProfileSection id="account" lang={lang}>...</ProfileSection> // Konto
<ProfileSection id="appearance" lang={lang}>...</ProfileSection> // Darstellung
<ProfileSection id="language" lang={lang}>...</ProfileSection> // Sprache und Region
<ProfileSection id="security" lang={lang}>...</ProfileSection> // Sicherheit
<ProfileSection id="connections" lang={lang}>...</ProfileSection> // Verknüpfungen
章节内容(每个应用都一样,参考Nexus): ProfileIdentity 在Konto, ThemeChoice 在
Darstellung, LanguageChoice + TimezoneChoice 在Sprache und Region, PasswordChange + SecurityFactors 在
Sicherheit。应用程序存储时区(POST /auth/preferences {timezone}),电话 setUserTimezone() 与
的值 /auth/me 并将日期格式化为 timeZone: currentTz(). applyTheme(mode) 设置dark类
适用于光、暗、系统。
其他表单字段和按钮(例如name字段)保持应用程序自己的(shadcn),因此它们与应用程序的其余部分匹配。
用户管理(React)
Konten und rolen→Benutzer,每个应用程序都一样(ADR 0059)。后端遵循scala -docs
interfaces/app-users.md; usersApi(api, {base: "/admins"}) 连结。
import { UserAdmin, usersApi, MicrosoftConnection } from "@scalio-cloud/ui/react";
const users = usersApi(api); // once, outside the component
<UserAdmin client={users} me={user.id} lang={lang} roles={[{ value: "admin", label: t("Administrator") }, ...]}
defaultRole="operator" signInModes={["microsoft", "password"]} microsoftEnabled={ssoOn} canLock
accessColumn={{ label: t("Customers"), render: (u) => ... }} accessField={(value, onChange, role, u) => ...}
notify={(text, tone) => toast[tone](text)} confirm={confirm} formatError={fmt} />
// Profile -> Verknüpfungen
<MicrosoftConnection linked={user.microsoft} canUnlink={user.has_password} onLink={...} onUnlink={...} lang={lang} />
Microsoft Entra (RoleAdmin, RoleSourceSettings)
import { RoleAdmin, rolesApi, RoleSourceSettings } from "@scalio-cloud/ui/react";
const roles = rolesApi(api); // Nexus: rolesApi(api, { permissions: "/permissions" })
<RoleAdmin client={roles} lang={lang} notify={toast} confirm={confirm} onChange={reloadUsers} />
<RoleSourceSettings value={source} tenants onSave={({ source, tenants }) => api.put("/users/role-source", { source, tenant_ids: tenants })} lang={lang} />
后端是规模平台 roles_router (0.3.0)。内置角色是只读的;自己的角色永远得不到
应用程序为管理员保留的权限。
设置向导(React)
第一次登录后,在每个应用程序中都一样(ADR 0060, uspec第3节)。应用程序写道
只有它的脚步;后端遵循scala -docs interfaces/setup-wizard.md (GET/PUT /api/setup/wizard)。
import { SetupWizard, SetupWizardButton, useSetupWizard, setupWizardApi } from "@scalio-cloud/ui/react";
const wizardClient = setupWizardApi(api); // once, outside the component ({ base: "/wizard" } for another route)
function AppWizard() {
const w = useSetupWizard({ client: wizardClient, enabled: isAdmin }); // opens by itself while needed
const steps = [
{ id: "tenant", label: t("Tenant"), title: t("Connect Microsoft"), description: "...", content: <TenantsSection />,
canNext: connected, onNext: async () => { /* save; a rejected promise keeps the step open */ } },
{ id: "mail", label: t("Email"), title: t("Email (SMTP)"), content: <SmtpForm />, optional: true },
];
return <SetupWizard {...w.props} lang={lang} title={t("Set up Foundry")} subtitle={t("...")} steps={steps}
success={{ title: t("Setup complete"), text: t("...") }} />;
}
// Einstellungen: open it again (admins only)
<SetupWizardButton lang={lang} />
测试id: setup-wizard, wizard-stepper(-item), wizard-step-{id}, wizard-next, wizard-back, wizard-skip,
wizard-close, wizard-error, wizard-success, wizard-done, settings-open-wizard.
双因素卡和许可面板(React)
每个应用程序中都有相同的后端路由,因此应用程序只传递它们的axios实例和一些helper。
import { SecurityFactors, securityApi, LicensePanel, licenseApi } from "@scalio-cloud/ui/react";
import { QRCodeSVG } from "qrcode.react";
import { startRegistration, browserSupportsWebAuthn } from "@simplewebauthn/browser";
const security = securityApi(api, { startRegistration, browserSupportsWebAuthn }); // once, outside the component
const license = licenseApi(api);
// Profile -> Sicherheit: authenticator app, backup codes, passkeys. Hidden for accounts without a password.
<SecurityFactors client={security} lang={lang} product="Scalio Citadel" email={user.email}
qr={(uri) => <QRCodeSVG value={uri} size={160} />} notify={(text, tone) => toast[tone](text)}
confirm={confirm} onMfaChange={(on) => setUser({ ...user, mfa_enabled: on })} />
// Einstellungen -> System -> Lizenz (scalio-license backend: /license, /license/addons)
<LicensePanel client={license} lang={lang} product="Citadel" kindLabel={licenseKindLabel}
featureLabel={licenseFeatureLabel} noFeatures={t("Drift detection only")} notify={...} confirm={confirm} />
// noFeatures only when the app has a better text than «Nur die Grundfunktionen»
securityApi 读取snake_case和camelCase的答案。 notify(text, tone) 得到 "success" 或者 "error";没有它
这些块本身显示信息。 confirm 应用程序的对话框({title, action, danger} - >承诺),
window.confirm 否则。 formatError(err) 让应用程序短语API错误(例如许可证限制代码)。有其他路由的license后端传递自己的路由 client 和一个 map(license, {L})
中描述的视图模型 react/license.js. Nexus做到了这一点:它 map 添加试用徽章,座位提示,无效
附加组件和只读注释(演示模式),并将其试用通知传递为 statusExtra 提醒设置为 children.
从0.27.0 (ADR 0069)开始,面板在设置表单中显示三个区域(左边是标题和提示,右边是卡片)
Nutzung»,«Add-ons»,«lizenzschlssel»。 children 与 extraTitle (和 extraHint)成为第四个领域。面板通过其 lang 每一个 client 调用;
licenseApi 将其发送为 ?lang=因此,scalio-license 0.4.0以页面语言呈现警告和错误。
Gruppe系统:zur<s:1> cksetzen和helpers (React)
每个应用程序都以相同的顺序显示Einstellungen→System: Lizenz, Updates, Sicherung, zur<s:1> cksetzen ()settingsGroups
对组进行排序 system 这种方式)。重置块与scalio-platform对话 factory_reset
(GET/POST /api/admin/factory-reset):
import { FactoryReset, factoryResetApi, SettingsField, SettingsRow } from "@scalio-cloud/ui/react";
const reset = factoryResetApi(api); // once, outside the component
<FactoryReset client={reset} lang={lang} tokenKey="citadel_token" backupTo="/settings/backup" link={Link} />
SettingsField (标签,字段,帮助)和 SettingsRow (左边是标题和帮助,右边是开关或按钮; tone="danger")
将字段保持在 SettingsSection 每个应用程序都是一样的。
Benachrichtigungen: E-Mail-Versand and E-Mail-Vorlagen (React)
自0.29.0 (ADR 0069 W5)以来,每个应用程序都以相同的方式发送邮件。服务器端是scalio-platform 0.8.0 mail
(Mailer.router,安装于 /api/mail). 两个块都进入设置组«Benachrichtigungen»。
import { MailTransport, MailTemplates, mailApi } from "@scalio-cloud/ui/react";
import { RichTextEditor } from "@/components/RichTextEditor"; // optional, the app's own editor (Compass: react-quill-new)
const mail = mailApi(api); // once, outside the component; mailApi(api, "/admin/mail") for another prefix
<MailTransport client={mail} lang={lang} notify={toast} graphHint={t("...Mail.Send and Mail.ReadWrite...")}>
<InboxSettings /> {/* optional blocks after «Testmail» */}
</MailTransport>
<MailTemplates client={mail} lang={lang} editor={RichTextEditor} />
MailTransport 显示三个块:«E-Mail-Versand»(关闭,SMTP或Microsoft 365,发件人,语言;秘密回来
作为掩码,掩码原封不动地发送回来),«Testmail»(空收件人:to me)和«Letzte Mails»(outbox={false}
隐藏)。在测试实例中,后端响应 test_mailbox: true 服务器字段被锁定。
MailTemplates 选择邮件和语言,提供占位符作为按钮,显示真实呈现的预览
在沙盒框架中,并保存,测试(«Test an mich»)或重置为内置文本。一个可切换的邮件得到
复选框“已发送的邮件”。这个 editor 得到 {value, onChange, testId};如果没有它,则使用文本字段,因此
包不保留任何依赖项。使用编辑器,普通内置文本作为段落(textToHtml)。
协议:动作和工作(React)
一个菜单项«协议»有两个标签,如Nexus (scalio-docs ADR 0070)。服务器端是规模平台
audit_router 和 jobs_router (0.7.0)。
import { AuditNav, AuditLog, JobsPanel, auditApi, jobsApi } from "@scalio-cloud/ui/react";
const audit = auditApi(api); // GET /audit, /audit/export.csv
const jobs = jobsApi(api); // GET /jobs, /jobs/runs, /jobs/queue, POST /jobs/{key}/run, queue actions
<PageHeader title={L.auditLog} nav={<AuditNav link={NavLink} lang={lang} />} />
<AuditLog client={audit} lang={lang} notify={toast} actionLabel={(a, lang) => ACTIONS[lang][a]} tenantLabel="Kunde" />
<JobsPanel client={jobs} lang={lang} notify={toast} canRun={can("jobs.run")} link={Link} confirm={confirm} />
AuditLog:注意«unveränderlich»,芯片计数(从服务器的类别),搜索,过滤器的行动, 状态、租户和时间范围、可排序列、列选择器(人员、租户、IP)、具有相同过滤器的CSV、 “Ältere laden”(光标),用“alt→new”查看,手机上的卡片。配件:toolbar,rowActions(entry),renderDetail(entry)(Nexus KI, Citadel PDF, Nexus步骤)。JobsPanel:选项卡Warteschlange(仅当应用程序有一个),Läufe(按作业和状态过滤)和Hintergrundjobs (时间表,最后一次运行,最后的结果,«Jetzt ausf<s:1> hren»与canRun); 每15秒重新装填一次(pollMs). 配件:queueActions(item, reload),renderQueueDetail(item, reload).AuditNav:标签/audit和/jobs(base对于另一个前缀,jobs={false}隐藏工作)。
命令面板(React)
读取与侧边栏相同的导航数据。将其放入布局中;侧边栏搜索按钮打开它。
import { CommandPalette, openCommandPalette, paletteShortcut } from "@scalio-cloud/ui/react";
<Sidebar nav={nav} search={{ onOpen: openCommandPalette, shortcut: paletteShortcut(lang) }} ... />
<CommandPalette nav={nav} activePath={location.pathname} onNavigate={navigate} lang={lang}
pages={[{ to: "/profile", label: "Profil" }]}
groups={(query) => [{ id: "ai", label: "KI", order: 10, items: [{ id: "ask", label: `Fragen: ${query}`, onSelect: ask, hidden: !query }] }]} />
页面:每个可见的侧边栏项目加上 pages (每条路径一次),按标签搜索, keywords、截面和路径。
分组排序 order (默认60);最近的页面(50)只在查询为空时显示,页面排在最后(100)。
一个项目有 label, icon, to 或者 onSelect,可选 keywords, hint, tone (primary, danger),
testId 和 alwaysShow. 没有 open 面板处理⌘K / Ctrl+K和 openCommandPalette() 本身;
通过 open + onOpenChange 来控制它。 recentKey 设置localStorage键(false:最近没有)。
安装
在npmjs.com上公开,不需要令牌: yarn add @scalio-cloud/ui (固定准确的版本)。
包中只包含SIL OFL下的设计值和字体,没有任何机密。
许可证
设计令牌和预设:(c) Scalio Cloud LLC,可在Scalio应用程序中免费使用。字体:SIL开放字体许可证1.1,
看到 fonts/licenses/. 图标在 react/icons.jsLucide, ISC许可,参见 licenses/lucide-LICENSE.txt.
释放
- 更改文件和bump
version在package.json无标签;无标签v)。 - 合并
main,然后发布它:在Scalio Dev驾驶舱(在Scalio UI中,“Freigeben”与版本一起)或 在GitHub中(Actions -> publish ->在main上运行工作流,或者自己创建标签/发布)。这个publishWorkflow测试并通过可信发布(无令牌;NPM org)将该版本发布到npmjs.comscalio-cloud,可信发布者Scalio-Cloud / scalio-ui / publish。Yml)和,当由手或从 使用CHANGELOG行创建标签和GitHub发布。变更日志位于scalio-docs中changelog/scalio-ui/(ADR 0066):每次更改都会添加一个文件changelog.d/<name>.md在那里,在释放a之前 文档公关运行python3 tools/changelog.py --repo scalio-ui release <version>. - 应用程序更新
yarn upgrade @scalio-cloud/ui@<version>在他们自己的公关中。
npm test 检查:亮/暗变量匹配,预设只使用定义的变量,所有字体文件存在,
没有从网络中加载任何内容。