Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions packages/app-shell/src/views/metadata-admin/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,10 +632,7 @@ const ENGINE_STRINGS_EN: Record<string, string> = {
'engine.packages.create.title': 'New Package',
'engine.packages.create.description': 'Create a project-scoped package to author and publish your own metadata. The id should be reverse-domain (e.g. {example}).',
'engine.packages.create.id': 'Package ID',
'engine.packages.create.idInvalid': 'Use letters, numbers, dot, dash or underscore (2–255 chars).',
'engine.packages.create.namespace': 'Object namespace',
'engine.packages.create.namespaceHint': 'All object names will start with `{ns}_` (e.g. leave_request).',
'engine.packages.create.namespaceInvalid': 'Use 2–20 chars: a lowercase letter, then letters, digits or underscore.',
'engine.packages.create.name': 'Display name',
'engine.packages.create.version': 'Version',
'engine.packages.create.versionInvalid': 'Use semantic version, e.g. 0.1.0',
Expand Down Expand Up @@ -1045,7 +1042,6 @@ const ENGINE_STRINGS_EN: Record<string, string> = {
'engine.studio.landing.dupIdPlaceholder': 'Copy package ID',
'engine.studio.landing.dupGo': 'Duplicate and open the builder',
'engine.studio.landing.dupCreated': 'Duplicated into writable package “{name}”',
'engine.studio.landing.createGo': 'Create and start building',
'engine.studio.landing.installedHeading': 'Installed (read-only · browsable)',
'engine.studio.designer.search': 'Search…',
'engine.studio.designer.select': 'Select…',
Expand Down Expand Up @@ -1167,14 +1163,8 @@ const ENGINE_STRINGS_EN: Record<string, string> = {
'engine.studio.pkg.writable': 'Writable',
'engine.studio.pkg.heading': 'Packages (apps)',
'engine.studio.pkg.none': 'No app packages yet',
'engine.studio.pkg.namePlaceholder': 'Name (e.g. Repair Center)',
'engine.studio.pkg.idPlaceholder': 'Package ID (e.g. com.example.repairs)',
'engine.studio.pkg.namespacePlaceholder': 'Namespace (e.g. repairs)',
'engine.studio.pkg.namespaceHint': 'All object names will start with `{ns}_` (e.g. leave_request).',
'engine.studio.pkg.namespaceInvalid': 'Use 2–20 chars: a lowercase letter, then letters, digits or underscore.',
'engine.studio.pkg.idStrippedNotice': 'Unsupported characters were removed — allowed: lowercase letters, digits, dots, hyphens, underscores',
'engine.studio.pkg.idFormatHint': 'Use a reverse-domain identifier with at least one dot, e.g. com.example.myapp',
'engine.studio.pkg.idFromNameUnavailable': 'No identifier could be suggested from this name — type one below (e.g. com.example.myapp)',
'engine.studio.pkg.createWritable': 'Create writable package',
'engine.studio.pkg.new': 'New package (writable base)',
'engine.studio.pkg.created': 'Package {name} created (writable)',
Expand Down Expand Up @@ -1938,10 +1928,7 @@ const ENGINE_STRINGS_ZH: Record<string, string> = {
'engine.packages.create.title': '新建软件包',
'engine.packages.create.description': '创建项目范围的软件包,用来编写并发布自己的元数据。ID 建议使用反向域名格式(例如 {example})。',
'engine.packages.create.id': '软件包 ID',
'engine.packages.create.idInvalid': '只能使用字母、数字、点、短横线或下划线(2–255 个字符)。',
'engine.packages.create.namespace': '对象命名空间',
'engine.packages.create.namespaceHint': '所有对象名将以 `{ns}_` 开头(如 leave_request)。',
'engine.packages.create.namespaceInvalid': '请使用 2–20 个字符:以小写字母开头,其后为字母、数字或下划线。',
'engine.packages.create.name': '显示名称',
'engine.packages.create.version': '版本',
'engine.packages.create.versionInvalid': '请使用语义化版本号,例如 0.1.0',
Expand Down Expand Up @@ -2347,7 +2334,6 @@ const ENGINE_STRINGS_ZH: Record<string, string> = {
'engine.studio.landing.dupIdPlaceholder': '副本包 ID',
'engine.studio.landing.dupGo': '复制并进入构建器',
'engine.studio.landing.dupCreated': '已复制为可写软件包「{name}」',
'engine.studio.landing.createGo': '创建并开始构建',
'engine.studio.landing.installedHeading': '已安装(只读 · 可浏览)',
'engine.studio.designer.search': '搜索…',
'engine.studio.designer.select': '请选择…',
Expand Down Expand Up @@ -2469,14 +2455,8 @@ const ENGINE_STRINGS_ZH: Record<string, string> = {
'engine.studio.pkg.writable': '可写',
'engine.studio.pkg.heading': '软件包(应用)',
'engine.studio.pkg.none': '暂无应用软件包',
'engine.studio.pkg.namePlaceholder': '名称(如:维修中心)',
'engine.studio.pkg.idPlaceholder': '包 ID(如:com.example.repairs)',
'engine.studio.pkg.namespacePlaceholder': '命名空间(如:repairs)',
'engine.studio.pkg.namespaceHint': '所有对象名将以 `{ns}_` 开头(如 leave_request)。',
'engine.studio.pkg.namespaceInvalid': '请使用 2–20 个字符:以小写字母开头,其后为字母、数字或下划线。',
'engine.studio.pkg.idStrippedNotice': '已移除不支持的字符——仅允许小写字母、数字、点、连字符和下划线',
'engine.studio.pkg.idFormatHint': '请使用带至少一个点的反向域名标识,如 com.example.myapp',
'engine.studio.pkg.idFromNameUnavailable': '无法从该名称生成标识,请在下方手动输入(如 com.example.myapp)',
'engine.studio.pkg.createWritable': '创建可写软件包',
'engine.studio.pkg.new': '新建软件包(可写 base)',
'engine.studio.pkg.created': '软件包 {name} 已创建(可写)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as React from 'react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
import { sanitizePackageId, PACKAGE_ID_RE } from './packages-io';
import { PackageIdInput, PackageIdSuggestionHint } from './PackageIdInput';
import { PackageIdInput } from './PackageIdInput';

afterEach(cleanup);

Expand Down Expand Up @@ -66,12 +66,3 @@ describe('PackageIdInput', () => {
expect(screen.queryByTestId('pkg-id-stripped')).not.toBeInTheDocument();
});
});

describe('PackageIdSuggestionHint', () => {
it('renders only when shown (CJK name yielded no suggestion)', () => {
const { rerender } = render(<PackageIdSuggestionHint show={false} />);
expect(screen.queryByTestId('pkg-id-manual-hint')).not.toBeInTheDocument();
rerender(<PackageIdSuggestionHint show />);
expect(screen.getByTestId('pkg-id-manual-hint')).toBeInTheDocument();
});
});
27 changes: 4 additions & 23 deletions packages/app-shell/src/views/studio-design/PackageIdInput.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

/**
* Package-identifier input shared by the three package wizards (switcher
* create, landing create, landing duplicate). Fixes the dogfood wizard
* findings (framework#2615 P2): illegal characters are still normalized
* Package-identifier input used by the landing "duplicate package" form.
* (Package create/edit now runs through the spec-driven PackageFormDialog.)
* Fixes the dogfood wizard findings (framework#2615 P2): illegal characters
* are still normalized
* away, but no longer silently — a notice says so — and while the value
* doesn't parse as a package id yet, an inline hint spells out the
* reverse-domain format instead of leaving the user staring at a disabled
Expand Down Expand Up @@ -72,23 +73,3 @@ export function PackageIdInput({
</div>
);
}

/**
* Hint under the display-name input when the name yields no identifier
* suggestion (CJK-only names slug to nothing) — say the id must be typed
* manually instead of leaving the id box silently empty.
*/
export function PackageIdSuggestionHint({
show,
locale,
}: {
show: boolean;
locale?: string;
}): React.ReactElement | null {
if (!show) return null;
return (
<p className="text-[10px] text-muted-foreground" data-testid="pkg-id-manual-hint">
{t('engine.studio.pkg.idFromNameUnavailable', locale)}
</p>
);
}
22 changes: 0 additions & 22 deletions packages/app-shell/src/views/studio-design/packages-io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,28 +54,6 @@ export async function fetchPackages(): Promise<PkgEntry[]> {
return parsePackages(await res.json());
}

/**
* Create a writable base package (POST /packages {id, name, namespace?}).
*
* `namespace` is the object-name prefix (framework#2694). The framework
* back-derives it from the id when omitted, but sending it explicitly keeps the
* authored value (which the user may have edited) authoritative.
*/
export async function createBasePackage(id: string, name: string, namespace?: string): Promise<void> {
const res = await fetch('/api/v1/packages', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: JSON.stringify({ id, name, ...(namespace ? { namespace } : {}) }),
});
const payload = (await res.json().catch(() => null)) as
| { success?: boolean; error?: { message?: string } }
| null;
if (!res.ok || payload?.success === false) {
throw new Error(payload?.error?.message || `HTTP ${res.status}`);
}
}

/**
* Duplicate a package into a NEW writable base (ADR-0070 D4 — the Airtable
* "duplicate base" gesture; POST /packages/:id/duplicate). This is how a
Expand Down
Loading