openyida 2026.10.14-2 → 2026.10.15

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.
Files changed (42) hide show
  1. package/README.md +3 -0
  2. package/bin/yida.js +5 -0
  3. package/lib/app/app-entry.js +16 -3
  4. package/lib/app/update-form-config.js +5 -2
  5. package/lib/core/command-manifest.js +55 -10
  6. package/lib/core/locales/en.js +15 -1
  7. package/lib/core/locales/zh.js +15 -1
  8. package/lib/core/theme-widget.js +14 -1
  9. package/lib/design-plan/entry-navigation.js +3 -1
  10. package/lib/design-plan/init.js +2 -1
  11. package/lib/design-plan/visual-policy.js +9 -4
  12. package/lib/style/data-manage-presets.json +146 -0
  13. package/lib/style/data-manage-style.js +141 -0
  14. package/lib/style/native-data-manage.js +300 -0
  15. package/package.json +1 -1
  16. package/yida-skills/SKILL.md +2 -1
  17. package/yida-skills/references/field-and-url-reference.md +9 -8
  18. package/yida-skills/skills/yida-app/SKILL.md +6 -0
  19. package/yida-skills/skills/yida-app/references/entry-navigation.md +31 -5
  20. package/yida-skills/skills/yida-app/workflow/step-2-design.md +5 -0
  21. package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +6 -1
  22. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +14 -6
  23. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +1 -1
  24. package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +3 -1
  25. package/yida-skills/skills/yida-data-manage-ui/SKILL.md +70 -0
  26. package/yida-skills/skills/yida-data-manage-ui/references/column-emphasis.md +20 -0
  27. package/yida-skills/skills/yida-data-manage-ui/references/custom-layouts.md +23 -0
  28. package/yida-skills/skills/yida-data-manage-ui/references/style-decisions.md +32 -0
  29. package/yida-skills/skills/yida-data-manage-ui/references/token-and-css.md +77 -0
  30. package/yida-skills/skills/yida-data-management/SKILL.md +2 -0
  31. package/yida-skills/skills/yida-design/SKILL.md +3 -0
  32. package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +28 -0
  33. package/yida-skills/skills/yida-design/references/theme-selection.md +12 -10
  34. package/yida-skills/skills/yida-design/sub_skill/ui-improve/SKILL.md +2 -0
  35. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/SKILL.md +3 -1
  36. package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/visual-theme-selection.md +1 -1
  37. package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +2 -0
  38. package/yida-skills/skills/yida-nav-shell/SKILL.md +2 -2
  39. package/yida-skills/skills/yida-page-config/SKILL.md +2 -2
  40. package/yida-skills/skills/yida-publish-page/SKILL.md +1 -1
  41. package/yida-skills/skills/yida-requirement-analysis/references/handoff.md +2 -0
  42. package/yida-skills/skills-index.json +42 -0
package/README.md CHANGED
@@ -443,6 +443,9 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
443
443
  | `openyida design-plan materialize <build-plan.json> [--from-preview \| --business-file <json> --visual-file <json>] [--output-dir <dir>] [--rebase-parts] [--check] [--json]` | Generate or update plan documents and theme with consistency checks |
444
444
  | `openyida design-plan patch <build-plan.json> --set <path=value> [--set <path=value> ...] [--materialize] [--output-dir <dir>] [--json]` | Patch plan fields and optionally sync documents and theme |
445
445
  | `openyida update-app <appType> [--name "..."] [--desc "..."] [--icon <name>] [--icon-color <color>] [--colour <key>] [--theme-color <color>] [--theme-file <css>] [--confirm-legacy-app-style] [--disable-custom-theme] [--nav-theme light\|dark\|white\|gray] [--logo-source appIcon\|customImage] [--layout side\|top\|l_shape] [--hide-app-nav\|--show-app-nav]` | Update app info |
446
+ | `openyida data-manage-style guide [--json]` | Inspect data-management UI directions, tokens and scoped CSS guidance |
447
+ | `openyida data-manage-style list [--json]` | List optional data-management recipe examples |
448
+ | `openyida data-manage-style generate --app <appType> --form <formUuid> --style <id> --base-css <theme.css> --output <new.css> [--focus balanced\|actions] [--field-id <actualId>]... [--emphasize-field <actualId>]... [--tag-field <selectFieldId>]... [--follow-shell] [--json]` | Generate data-management CSS from an optional recipe (local only) |
446
449
  | `openyida app-theme snapshot <appType> --output-dir <new-dir> [--json]` | Archive application theme settings and CSS (read-only) |
447
450
  | `openyida app-theme restore <appType> --snapshot <before.json> --expected-current <after.json> [--check \| --confirm --output-dir <new-dir>] [--json]` | Check or restore an application theme snapshot (supported exact values only) |
448
451
  | `openyida upgrade-app-theme <appType> --explicit-request [--confirm \| --prepare --output-dir <dir>] [--json]` | Upgrade an AI-built legacy app theme (explicit request and ask_human confirmation required) |
package/bin/yida.js CHANGED
@@ -942,6 +942,11 @@ async function main() {
942
942
  break;
943
943
  }
944
944
 
945
+ case 'data-manage-style': {
946
+ await require('../lib/style/data-manage-style').run(args);
947
+ break;
948
+ }
949
+
945
950
  case 'app-theme': {
946
951
  await require('../lib/app/app-theme').run(args);
947
952
  break;
@@ -4,6 +4,8 @@ const { createAuthRef, createYidaClient, unwrapYidaResponse } = require('../core
4
4
  const { t } = require('../core/i18n');
5
5
  const { throwUsage } = require('../core/command-errors');
6
6
  const { fetchFormPageList } = require('./form-navigation');
7
+ const { fetchFormSchemaInfo } = require('./get-form-config');
8
+ const { buildDisplayPageEntryUrls } = require('./application-entry-urls');
7
9
 
8
10
  /** Keep complete runtime URLs; the server verifies tenant and resource ownership. */
9
11
  function normalizeUrl(value, appType) {
@@ -54,7 +56,16 @@ async function resolveEntryUrl(authRef, appType, key, formUuid) {
54
56
  if (key === 'frontend') {
55
57
  if (String(form.formType).toLowerCase() !== 'display') { throw new Error(t('app_entry.frontend_display_required', formUuid)); }
56
58
  if (!form.pathName) { throw new Error(t('app_entry.page_offline', formUuid)); }
57
- return normalizeUrl(`${authRef.baseUrl}/${appType}/custom/${formUuid}`, appType);
59
+ const response = await fetchFormSchemaInfo(authRef, appType, formUuid);
60
+ if (!response || response.success === false || response.__needLogin || response.__csrfExpired) {
61
+ throw new Error(t('app_entry.invalid_response'));
62
+ }
63
+ const config = response.content || response.result || response.data;
64
+ const entry = buildDisplayPageEntryUrls({ appType, formUuid, baseUrl: authRef.baseUrl, config, formType: 'display' });
65
+ if (!entry.standaloneUrl || entry.navigationVerification?.renderNav !== false) {
66
+ throw new Error(t('app_entry.frontend_nav_required', formUuid));
67
+ }
68
+ return normalizeUrl(entry.standaloneUrl, appType);
58
69
  }
59
70
  return normalizeUrl(`${authRef.baseUrl}/${appType}/workbench/${formUuid}`, appType);
60
71
  }
@@ -137,8 +148,10 @@ async function run(args = []) {
137
148
  urls[key] = normalizeUrl(result.accessEntries[key].url, appType);
138
149
  }
139
150
  }
140
- console.log(JSON.stringify({ appType, ...result, urls }, null, 2));
141
- return result;
151
+ const output = { ...result, appType, urls,
152
+ verification: { configurationReadback: true, runtimeVerified: false } };
153
+ console.log(JSON.stringify(output, null, 2));
154
+ return output;
142
155
  }
143
156
 
144
157
  module.exports = { run, parseArgs, normalizeUrl, resolveEntryUrl };
@@ -5,6 +5,7 @@ const { t } = require('../core/i18n');
5
5
  const { buildYidaTitleI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
6
6
  const { banner, step, label, success, fail, warn, info, error, result, usage } = require('../core/chalk');
7
7
  const { throwCommandError, throwUsage } = require('../core/command-errors');
8
+ const { readRenderNav } = require('./application-entry-urls');
8
9
 
9
10
  function parseArgs(inputArgs = process.argv.slice(2)) {
10
11
  const args = [...inputArgs];
@@ -158,12 +159,14 @@ async function run(args = process.argv.slice(2)) {
158
159
  success(t('update_form_config.read_ok'));
159
160
 
160
161
  // A keep request must not silently switch navigation when the read is incomplete.
161
- if (keepNav && ![true, false, 'true', 'false'].includes(currentConfig.isRenderNav)) {
162
+ const currentRenderNav = readRenderNav(currentConfig);
163
+ if (keepNav && currentRenderNav === null) {
162
164
  throwCommandError(t('get_form_config.read_failed', t('common.request_failed_label')), {
163
165
  code: 'CONFIG_READ_FAILED',
164
166
  });
165
167
  }
166
- const effectiveNav = keepNav ? String(currentConfig.isRenderNav) : isRenderNav;
168
+ // The read API exposes renderNav; the write API still accepts isRenderNav.
169
+ const effectiveNav = keepNav ? String(currentRenderNav) : isRenderNav;
167
170
 
168
171
  step(3, t('update_form_config.step_update'));
169
172
  info(t('update_form_config.sending_request'));
@@ -369,6 +369,9 @@ function findCommandSuggestion(argv = []) {
369
369
  }
370
370
 
371
371
  const COMMAND_SIDE_EFFECTS = new Map([
372
+ ['data-manage-style.guide', sideEffect('local_read')],
373
+ ['data-manage-style.list', sideEffect('local_read')],
374
+ ['data-manage-style.generate', sideEffect('local_write')],
372
375
  ['app-theme.snapshot', sideEffect('remote_read', { mutates_local: true })],
373
376
  ['app-theme.restore', sideEffect('mixed', { read_actions: ['--check', 'default'], mutating_actions: ['--confirm'] })],
374
377
  ['upgrade-app-theme', sideEffect('mixed', { mutates_yida: true, mutates_local: true,
@@ -761,6 +764,8 @@ const COMMAND_PERMISSIONS = new Map([
761
764
  reason: ALLOW_EXTERNAL_REASON,
762
765
  })),
763
766
 
767
+ ...permissionEntries(['data-manage-style.guide', 'data-manage-style.list'], permission('allow', 'read')),
768
+ ...permissionEntries(['data-manage-style.generate'], permission('allow', 'write', { reason: 'Generate a new local CSS file; no remote mutations.' })),
764
769
  ...permissionEntries(['app-theme.snapshot'], permission('allow', 'read', {
765
770
  reason: 'Read application theme configuration and archive CSS into a new local directory.',
766
771
  })),
@@ -1102,7 +1107,7 @@ const COMMAND_GROUPS = [
1102
1107
  { name: 'clearManagement', type: 'boolean', source: 'option', builder_options: ['--clear-management'] },
1103
1108
  { name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] },
1104
1109
  ],
1105
- notes: ['Complete URLs, including workbench page IDs, are preserved. --frontend-page requires a published display page. --management-page verifies ownership and builds /workbench/<formUuid>. Generated URLs need runtime verification; public entries require the verified complete URL. Omitted entries remain unchanged.'],
1110
+ notes: ['Complete URLs, including workbench page IDs, are preserved. --frontend-page requires a published display page plus a live hidden-navigation readback, using the same standalone URL resolver as publish; missing/mismatched config or visible navigation prevents writing. Results return urls.frontend/management and configurationReadback separately from runtimeVerified. --management-page verifies ownership and builds /workbench/<formUuid>. Generated URLs need runtime verification; public entries require the verified complete URL. --frontend-page is a standalone shortcut only; an explicitly planned platform-shell frontend uses --frontend with its verified workbenchUrl. This command does not hide navigation, publish pages or change permissions. Entry registration accepts only platform-supported routes and query parameters; do not strip required per-open parameters from a rejected URL to claim an equivalent entry. Omitted entries remain unchanged.'],
1106
1111
  }),
1107
1112
  command('app-list', ['app-list'], 'app-list [--type managed|created] [--page N] [--size N]', 'help.cmd_app_list', { managedRun: {'scope':'denied','reason':'scope_not_audited'},
1108
1113
  notes: ['Successful results include appUrl/workbenchUrl for the business workspace and adminUrl for developer administration. Complete application delivery retains adminUrl regardless of auth runtime; route construction does not verify recipient access. Preserve the frontend URL from its own page result.'],
@@ -1194,6 +1199,36 @@ const COMMAND_GROUPS = [
1194
1199
  'Platform preset keys are used independently of --theme-file and --theme-color. Choose --hide-app-nav or --show-app-nav to set navigation visibility.',
1195
1200
  ],
1196
1201
  }),
1202
+ command('data-manage-style.guide', ['data-manage-style', 'guide'], 'data-manage-style guide [--json]', 'help.cmd_data_manage_style_guide', {
1203
+ managedRun: { scope: 'local', allowedFlags: ['--json'] }, requiresLogin: false, output: 'json',
1204
+ args: [{ name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] }],
1205
+ notes: ['Default authoring entry: composable change dimensions, existing token consumers, scoped CSS targets and state checks. No style ID, login or file writes. Inspect the actual DOM before use. This guide does not generate or validate a page; list/generate recipes are optional examples, not the capability boundary.'],
1206
+ }),
1207
+ command('data-manage-style.list', ['data-manage-style', 'list'], 'data-manage-style list [--json]', 'help.cmd_data_manage_style_list', {
1208
+ managedRun: { scope: 'local', allowedFlags: ['--json'] }, requiresLogin: false, output: 'text|json',
1209
+ args: [{ name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] }],
1210
+ notes: ['Optional legacy recipe examples, column emphasis options and layout references. Start with guide for freeform token/CSS authoring; this catalog is not exhaustive and is not required. Custom directions are guidance, not native generate style IDs.'],
1211
+ }),
1212
+ command('data-manage-style.generate', ['data-manage-style', 'generate'], 'data-manage-style generate --app <appType> --form <formUuid> --style <id> --base-css <theme.css> --output <new.css> [--focus balanced|actions] [--field-id <actualId>]... [--emphasize-field <actualId>]... [--tag-field <selectFieldId>]... [--follow-shell] [--json]', 'help.cmd_data_manage_style_generate', {
1213
+ // Value-bearing and repeatable options are validated by the command parser.
1214
+ // allowedFlags is reserved for distinct, flag-only local invocations.
1215
+ managedRun: { scope: 'local' },
1216
+ requiresLogin: false, output: 'text|json',
1217
+ args: [
1218
+ { name: 'appType', type: 'string', required: true, source: 'option', builder_options: ['--app'] },
1219
+ { name: 'formUuid', type: 'string', required: true, source: 'option', builder_options: ['--form'] },
1220
+ { name: 'style', type: 'string', required: true, source: 'option', builder_options: ['--style'], choices: require('../style/data-manage-presets.json').map(item => item.id) },
1221
+ { name: 'baseCss', type: 'file', required: true, source: 'option', builder_options: ['--base-css'] },
1222
+ { name: 'output', type: 'file', required: true, source: 'option', builder_options: ['--output'] },
1223
+ { name: 'focus', type: 'string', source: 'option', builder_options: ['--focus'], choices: ['balanced', 'actions'], default: 'balanced' },
1224
+ { name: 'fieldId', type: 'string', source: 'option', builder_options: ['--field-id'], repeatable: true },
1225
+ { name: 'emphasizeField', type: 'string', source: 'option', builder_options: ['--emphasize-field'], repeatable: true },
1226
+ { name: 'tagField', type: 'string', source: 'option', builder_options: ['--tag-field'], repeatable: true },
1227
+ { name: 'followShell', type: 'boolean', source: 'option', builder_options: ['--follow-shell'] },
1228
+ { name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] },
1229
+ ],
1230
+ notes: ['Local-only generation from existing application CSS into a new file. Replaces only the owned app/form block. Preserves business configuration and virtual row geometry. Uses current native navigation selection; repeat actual field IDs for nav-free native views. Shell changes are opt-in. Repeat --emphasize-field for themed column fill, header and weight without an accent line; --tag-field renders uniform SelectField labels, not conditional colors. Targets also supply nav-free field evidence. Regeneration without the flags removes these owned enhancements. Back up before publishing through update-app, read back and verify the actual runtime; generated CSS is not publication or acceptance.'],
1231
+ }),
1197
1232
  command('app-theme.snapshot', ['app-theme', 'snapshot'], 'app-theme snapshot <appType> --output-dir <new-dir> [--json]', 'help.cmd_app_theme_snapshot', { managedRun: { scope: 'app', appPosition: 1 },
1198
1233
  output: 'json',
1199
1234
  args: [
@@ -1545,7 +1580,7 @@ const COMMAND_GROUPS = [
1545
1580
  { name: 'title', type: 'string', source: 'positional', position: 3, required: true },
1546
1581
  { name: 'locale', type: 'string', source: 'option', builder_options: ['--locale', '--content-locale', '--lang'] },
1547
1582
  ],
1548
- notes: ['A successful write acknowledgement is not navigation readback. Follow with get-form-config <appType> <formUuid> --json; inspect renderNav, falling back to isRenderNav only when renderNav is absent.'],
1583
+ notes: ['The page write API accepts isRenderNav; keep reads renderNav first and falls back to the returned isRenderNav only when renderNav is absent. URL isRenderNav is a separate per-open parameter, not persistence evidence. A successful write acknowledgement is not navigation readback. Follow with get-form-config <appType> <formUuid> --json; inspect renderNav, falling back to isRenderNav only when renderNav is absent.'],
1549
1584
  }),
1550
1585
  command('get-form-config', ['get-form-config'], 'get-form-config <appType> <formUuid> [--json]', 'help.cmd_get_form_config', { managedRun: { scope: 'app', appPosition: 0 },
1551
1586
  output: 'json',
@@ -1763,7 +1798,7 @@ const COMMAND_GROUPS = [
1763
1798
  output: 'text|json',
1764
1799
  notes: [
1765
1800
  'openyida sample yida-data-management empty-query --output <file.js> emits buildEmptyConditions, buildCountConditions and countFilledRangeEndpoints. Use current schema field IDs and explicit range modes. Returned arrays work with --search-json or page searchFormDatas. The template performs no query, schema change or count maintenance.',
1766
- 'On explicit preview requests only: openyida sample yida-design theme-preview --theme-ids <comma-separated-ids> --output <preview.html> --json generates a standalone read-only HTML gallery on any host. Displays all requested eligible themes, titles and images only, four to six columns on desktop with responsive wrapping and unlimited rows; refuses existing files. No selection buttons. Use ask_human for selection. app-pop requires --pop-art-context explicit-request|showcase with actual task evidence; absent evidence, exclude it from recommendations, choices and previews.',
1801
+ 'On explicit preview requests only: openyida sample yida-design theme-preview --theme-ids <comma-separated-ids> --output <preview.html> --json generates a standalone read-only HTML gallery on any host. Displays all requested eligible themes, titles and images only, four to six columns on desktop with responsive wrapping and unlimited rows; refuses existing files. Named templates reuse the exact catalog previewImage URL; generated cards render without scripts. Returns attachment and presentation metadata: show HTML cards in the conversation and include result.attachment in the same ask_human attachments when selection is needed. A browser tab or bare link alone is not inline delivery; preview-only requests do not create a choice. No selection buttons. app-pop requires --pop-art-context explicit-request|showcase with actual task evidence; absent evidence, exclude it from recommendations, choices and previews.',
1767
1802
  'Optional read-only QwenWork preview, only on explicit user request: run openyida sample yida-design theme-selector --json to prepare only the preview HTML in the host skills directory. No bridge submission; selection always uses ask_human. Pass result.widget_path unchanged to qoder_show_widget; it is relative to result.skillsRoot, not result.outputPath or a repository path. This sample does not accept --output, --var, --design-file or --style-id.',
1768
1803
  'yida-design app-theme reads concrete tokens from --design-file and writes CSS plus a sibling <output>.tokens.md update snapshot. The same output path preserves project CSS and applies changed tokens. Without --design-file it resets the output to the public base template. Menu radius, normal/hover/selected borders and selected shadow apply to side, L-shaped and top navigation; only legacy/shared-skeleton CSS gains missing menu-shape rules. Named templates preserve paired CSS declarations and selectors, instantiate brand placeholders and apply project deltas without auto-adding navigation rules or color-group.',
1769
1804
  'yida-design application-style --style-id supports every catalog theme and exports a three-file design.md/app_theme.css/form-layout.json authoring bundle to an output directory without overwriting files. Each ID selects a complete application theme, never a separate nav-xxx style. Its design.md owns navigation, shell, custom pages, forms and details, including colors, borders, hover, radius and shadow; paired CSS owns authored selectors and CSS defaults; template files are not rewritten to match design defaults. Independent color roles are preserved; only explicit var() references link them. Brand primary and hover values remain project placeholders until app-theme --design-file generates concrete CSS. Use design-plan catalog for presets and its separate creativeOption; free creative requires independent business decisions and explicit design tokens.',
@@ -1774,7 +1809,7 @@ const COMMAND_GROUPS = [
1774
1809
  'form-fields demonstrates field layout, not a default divider design. Choose dividerType for the current business page before creating forms; keep same-level sections consistent and vary different business pages when appropriate.',
1775
1810
  'openyida-page-template canvas-theme outputs the same provider as a reusable fragment; combine its imports and code with the business page in one Canvas file, keeping its scoped loading-mask style. Existing pages must update their provider and be republished; changing application theme CSS alone does not upgrade embedded provider code.',
1776
1811
  'openyida-page-template form-open-container extracts the maintained Canvas form drawer. Merge its imports, CanvasDrawer, FormOpenContainer, useYidaFormOpen and helpers; bind openForm(request) and render formOpenContainer. Do not replace it with a similarly named custom drawer.',
1777
- 'canvas-admin-entry includes canvas-navigation and a theme-aware business-workbench button. Visibility uses the current app-scoped loginUser.isAppAdmin flag (y/true only), matching g_config/pageConfig app identity with conflicts kept unknown, never CLI identities or a copied admin list. Configure a verified business page ID; unknown identity stays hidden and backend authorization remains authoritative.',
1812
+ 'canvas-admin-entry includes canvas-navigation and a theme-aware business-workbench button. For service-management apps, consider a role-aware secondary frontend-to-management shortcut by default (header actions, profile menu or existing navigation), reusing an equivalent entry when present. This is design guidance only: no new required fields, validator or publish gate. The built-in flag supports app-manager identity, not arbitrary business roles; those need the project current-viewer permission adapter. Use the verified registered management destination, never developer admin. Visibility uses the current app-scoped loginUser.isAppAdmin flag (y/true only), matching g_config/pageConfig app identity with conflicts kept unknown, never CLI identities or a copied admin list. Configure a verified business page ID; unknown identity stays hidden and backend authorization remains authoritative.',
1778
1813
  'canvas-view-state restores the current local task view, allowlisted non-sensitive enum filters and page through URL state, preserving unrelated query/hash/history metadata. Use only without an existing router or unsaved-edit blockers; drafts, per-task caches and scroll need explicit policies. Verify iframe refresh/return behavior in the host. Business writes require verified server-side role, record, field and state-transition authorization; hidden buttons do not grant or enforce it.',
1779
1814
  'Extract canvas-theme before antd UI and canvas-navigation before cross-page actions; use verified target types and resource IDs. Local tabs use state. Compile/publish reject known app-prefix omissions and fixed ConfigProvider brand colors; runtime theme and destination verification is still required.',
1780
1815
  ],
@@ -1921,6 +1956,14 @@ function summarizeLocalizedCommands(commands) {
1921
1956
  ask_command_ids: askCommandIds,
1922
1957
  deny_command_ids: denyCommandIds,
1923
1958
  core_workflows: {
1959
+ data_management_ui_improvement: {
1960
+ orchestrator_skill_id: 'yida-data-manage-ui',
1961
+ reference: 'yida-skills/skills/yida-data-manage-ui/SKILL.md',
1962
+ trigger_phrases: ['针对当前数据管理页换一种风格', '数据管理页重在操作', '突出新增和筛选', '数据管理页只保留横线', '突出客户名称列', '列级强调', '状态列改成标签'],
1963
+ command_ids: ['data-manage-style.guide', 'app-theme.snapshot', 'update-app', 'app-theme.restore'],
1964
+ optional_command_ids: ['data-manage-style.list', 'data-manage-style.generate'],
1965
+ policy: 'Derive composable changes from the user task and actual DOM; no preset selection required. Inspect existing token consumers, then author minimal page/field-scoped CSS on the current full theme. Recipes are optional examples, not the style boundary; --lab-native-* is recipe-local, not a platform token API. Preserve native/custom ownership, fields, data, permissions and actions. Shell changes opt-in. Back up and verify recovery before publishing. Do not alter virtual grid geometry or automatically migrate to custom pages. Runtime verification is required.',
1966
+ },
1924
1967
  existing_ui_improvement: {
1925
1968
  orchestrator_skill_id: 'yida-design',
1926
1969
  reference: 'yida-skills/skills/yida-design/sub_skill/ui-improve/SKILL.md',
@@ -2028,7 +2071,7 @@ function summarizeLocalizedCommands(commands) {
2028
2071
  product_design_policy: 'yida-requirement-analysis owns shared facts and first-time intake. yida-prd owns business planning; yida-design owns visual design. Reuse the same confirmed brief and supplied details. Prepare business and base visuals concurrently, then bind visuals to settled page tasks. yida-app merges and checks the artifacts before creating resources. Plan authors scope, data and rules, page organization, business interactions and acceptance once. Ordered blocks {name,purpose} supply default priority and content layers; explicit designs remain authoritative. Project-specific acceptance criteria are merged with generated resource checks. Plan uses the compact authoring contract and selected theme context; the CLI reads full templates and renders all artifacts. Fast, Plan and single-page design share yida-design/templates/design-themes and visual_decision_policy: infer directly from supplied style intent or extracted reference DNA, and use the shared three-direction rule only when comparison is needed. Theme templates declare application-global variables from basic-tokens.json with fixed typography, spacing and Tooltip colors, plus scoped custom-page variables; component recipes consume those variables. Each selected template is the authority for navTheme, its six navigation color tokens, and its selected-item shadow token; free-creative authors its own navigation tone and tokens. Explicit project customization stays in visualStyle.tokens. The CLI declares each fixed application palette once in a shared :root and platform-mode selector block. The CLI preserves mode-independent navigation appearance tokens. Light, dark, white and gray mode classes consume the same project tokens; navTheme remains platform metadata, not a second palette. Legacy generated mode blocks are consolidated on update, while unrelated custom CSS is preserved.',
2029
2072
  ui_guidance_policy: 'Page implementation consumes yida-prd prd.md for positioning, information architecture, page prototype, native form entry policy, material strategy, business-specific checks, resource creation order, page implementation delivery order, navigation order, and acceptance criteria; it consumes yida-design design.md for app custom theme CSS delivery, themeColor/navTheme, visual states, visualScaffold, surface material, rounded rules, density rules, components, state styling, and page-level imageNeed. When a page is imageNeed=required, or beneficial with declared image slots, yida-image-assets prepares a traceable asset manifest alongside page layout and interaction work; wait for final images only at that page\'s image binding and acceptance. imageNeed=none skips the branch. prd.md and design.md remain the only design sources of truth. Page implementation may extract page-spec.json as a derived implementation handoff from prd.md + design.md; conflicts are resolved by sending business conflicts to yida-prd and visual conflicts to yida-design before regenerating the spec. Core normal forms default to 1-3 business sample records before page implementation, followed by query readback. An explicit opt-out, configuration dictionary, sensitive data, or lack of safely constructible values requires a recorded skip reason. Screenshots, public sharing, data-source deep binding, and fine navigation grouping are optional after explicit user request or PRD acceptance criteria.',
2030
2073
  default_nav_order_policy: 'For application-wide custom navigation, implement each entry menu and verify app/page navigation hiding. A standalone frontend preserves platform navigation for the management workspace; frontend-only delivery skips platform ordering. After all scoped resources and page publications finish, perform exactly one navigation order operation. Use management tasks and their default destination, without creating a homepage or putting a frontend page first. When execution.entryRecommendation exists, derive navigationOrder from its management/workspace menus. Call openyida nav-group order <appType> <items...> for an explicit order; otherwise call openyida nav-group auto-order <appType> once for legacy single-workspace plans. Do not use openyida publish ... --auto-nav-order for per-page application builds. Explicit and automatic ordering are mutually exclusive; never generate per-item move loops.',
2031
- final_link_policy: 'Return exactly one user-visible application entry group, never one artifact or link card per form, process, report, schema, manifest, PRD, design file, or intermediate artifact. Include one verified management entry when a management or unified workspace is in scope: the app workbench URL or a planned workbench/formUuid URL with its real viewUuid. Backend-only returns its verified business backend entry and developer admin URL without adding a frontend. Native management does not require a display homepage; frontend-only delivery includes its verified frontend entry and developer admin URL, without inventing a business backend. Add the clean custom page URL only when PRD entryMode=standalone and get-form-config readback confirms renderNav=false (fallback to isRenderNav only when renderNav is absent). Complete application delivery defaults to 2-3 distinct entries: frontend or unified workspace, business backend when separately planned, and developer admin. agent-capabilities application_entry_policy.entries.admin=include is independent of auth runtime. Read adminUrl from create-app or app-list results; route construction does not verify recipient permissions. Explicit user exclusions take precedence; single-page tasks retain their scope. Put the business summary, verification results and remaining work in the terminal delivery artifact description; when no delivery tool is available, return the same content and entry group in the final response. Summarize business resources by capability or count, but every resource count, seed-record count, and completed/verified claim must come from that resource\'s own successful result or readback. When the user or caller explicitly requests a resource manifest, resource UUIDs/IDs, publish status, or test-data summary, include one concise verified delivery manifest in that description (or the final response when no delivery tool is available).',
2074
+ final_link_policy: 'Return exactly one user-visible application entry group, never one artifact or link card per form, process, report, schema, manifest, PRD, design file, or intermediate artifact. Include one verified management entry when a management or unified workspace is in scope: the app workbench URL or a planned workbench/formUuid URL with its real viewUuid. Backend-only returns its verified business backend entry and developer admin URL without adding a frontend. Native management does not require a display homepage; frontend-only delivery includes its verified frontend entry and developer admin URL, without inventing a business backend. For scoped service frontend pages, infer standalone when entryMode is missing; preserve an explicit platform-shell choice and its verified workbenchUrl. Add the clean custom page URL only for standalone pages when get-form-config readback confirms renderNav=false (fallback to isRenderNav only when renderNav is absent). Complete application delivery defaults to 2-3 distinct entries: frontend or unified workspace, business backend when separately planned, and developer admin. agent-capabilities application_entry_policy.entries.admin=include is independent of auth runtime. Read adminUrl from create-app or app-list results; route construction does not verify recipient permissions. Explicit user exclusions take precedence; single-page tasks retain their scope. Put the business summary, verification results and remaining work in the terminal delivery artifact description; when no delivery tool is available, return the same content and entry group in the final response. Summarize business resources by capability or count, but every resource count, seed-record count, and completed/verified claim must come from that resource\'s own successful result or readback. When the user or caller explicitly requests a resource manifest, resource UUIDs/IDs, publish status, or test-data summary, include one concise verified delivery manifest in that description (or the final response when no delivery tool is available).',
2032
2075
  entry_navigation_contract: {
2033
2076
  reference: 'yida-skills/skills/yida-app/references/entry-navigation.md',
2034
2077
  brief_path: 'entryRecommendation',
@@ -2040,7 +2083,7 @@ function summarizeLocalizedCommands(commands) {
2040
2083
  derived_summary: 'execution.entryModeSummary is generated for PRD and preview from the selected mode; do not author it in build-plan.json.',
2041
2084
  roles: ['service', 'management', 'workspace'],
2042
2085
  entry_required_fields: ['key', 'name', 'role', 'menu', 'defaultMenuKey'],
2043
- service_page_binding: 'sceneKey of a standalone page',
2086
+ service_page_binding: 'sceneKey of the frontend page; standalone by default, preserve explicit platform-shell',
2044
2087
  page_navigation_policy: getPageNavigationContract(),
2045
2088
  local_menu_binding: 'Every entry containing local menus, including management/workspace, requires sceneKey matching exactly one page; each local menu requires resource equal to that page name and a nonempty viewKey.',
2046
2089
  menu_target_types: ['local', 'submission', 'page', 'custom'],
@@ -2065,14 +2108,14 @@ function summarizeLocalizedCommands(commands) {
2065
2108
  requirement_fields: ['formUuid', 'operation', 'viewUuid?'],
2066
2109
  resolve_access_output: '{appType,grants:[{formUuid,operation,viewUuid?,allowed:boolean}]}',
2067
2110
  builtin_permission_adapter: false,
2068
- permission_policy: 'Current viewer, exact resource/operation/view and allowed===true; errors never fall back to unrestricted menus. dataScope is enforced by platform permissions, not menu filtering.',
2111
+ permission_policy: 'Current viewer, exact resource/operation/view and allowed===true; errors never fall back to unrestricted menus. dataScope is enforced by platform permissions, not menu filtering. In service-management frontends, consider a secondary management shortcut for authorized current viewers; hide for ordinary, anonymous or unknown viewers. Reuse the registered management destination and existing identity adapter. This is authoring guidance, not a new validation or delivery gate.',
2069
2112
  },
2070
2113
  },
2071
2114
  application_entry_policy: {
2072
2115
  delivery_unit: 'single_application_entry_group',
2073
2116
  persistence: {
2074
2117
  command_ids: ['app-entry.get', 'app-entry.set'],
2075
- policy: 'After successful application delivery, register only verified in-scope runtime entries; set performs partial merge and readback. Do not register admin/design URLs. Incremental resource-only work preserves existing entries. Clear only on explicit removal. Conflicts require reread and review, never blind retries.',
2118
+ policy: 'Before final complete-application delivery, registration and readback of all verified in-scope business runtime entries is required by default: service-management writes both, frontend-only writes frontend, backend-only/unified writes management. Resolve each planned default resource/view independently; never reuse the last published page or substitute a root workbench for a task URL. Use readback urls.frontend/management for final cards. Existing identical entries can reuse the readback; set performs partial merge and readback. Missing verification or failed persistence is incomplete delivery, not a reason to emit guessed links. Respect explicit no-write constraints. Do not register admin/design URLs. Incremental resource-only work preserves existing entries. Clear only on explicit removal. Conflicts require reread and review, never blind retries.',
2076
2119
  },
2077
2120
  workbench: {
2078
2121
  include: 'when_workspace_in_scope',
@@ -2084,7 +2127,7 @@ function summarizeLocalizedCommands(commands) {
2084
2127
  custom: {
2085
2128
  include: 'when_entry_mode_standalone_and_is_render_nav_false_readback',
2086
2129
  url: '{base_url}/{appType}/custom/{formUuid}',
2087
- default_policy: 'A scoped service frontend is a standalone display page even when entryMode was omitted. Plan derives it from role=service and sceneKey; Fast follows the same business intent. Persist page navigation hiding and read it back, without a new flag or navigation URL query. Preserve explicit user choices and backend navigation. Missing verification is incomplete frontend delivery, not permission to silently omit it.',
2130
+ default_policy: 'A scoped service frontend is a standalone display page even when entryMode was omitted. Plan derives it from role=service and sceneKey; Fast follows the same business intent. Persist page navigation hiding and read it back, without a new flag or navigation URL query. Preserve explicit platform-shell frontend choices: use the verified workbenchUrl with app-entry --frontend, not --frontend-page. Only whole-application custom navigation hides hideAppNav; standalone frontend pages must preserve backend platform navigation and shared-form settings. URL isRenderNav for native submission/detail pages is independent of persisted renderNav; keep those per-open parameters. Missing verification is incomplete frontend delivery, not permission to silently omit it.',
2088
2131
  url_source: 'publish.standaloneUrl or post-publish get-form-config.standaloneUrl; require navigationVerification.renderNav=false and verified=true',
2089
2132
  },
2090
2133
  admin: {
@@ -2103,6 +2146,8 @@ function summarizeLocalizedCommands(commands) {
2103
2146
  'create-process',
2104
2147
  'create-page',
2105
2148
  'publish',
2149
+ 'app-entry.get',
2150
+ 'app-entry.set',
2106
2151
  'check-prd-completeness',
2107
2152
  ],
2108
2153
  optional_read_command_ids: ['get-schema', 'list-forms'],
@@ -2122,7 +2167,7 @@ function summarizeLocalizedCommands(commands) {
2122
2167
  'yida-data-source-connectors',
2123
2168
  'yida-dashboard',
2124
2169
  ],
2125
- completion_contract: 'Clarify core functions and actual usage first, then resolve the required resource context and settle entry navigation before PRD planning. Fast generates prd.md and design.md in parallel; Plan uses design-plan init/preview/materialize/patch and confirms the current revision. Validate both artifacts, create or reuse app and core forms/processes, seed and query-check core normal-form samples or record an allowed skip reason, then implement the primary page in PRD resource order, publish it, apply PRD navigation order or lightweight fallback navigation order for platform navigation, or verify app and per-page hiding for application-wide custom navigation. For a standalone frontend in a platform-navigation app, verify that page independently and retain workspace navigation. Return one named application entry group. Include workbench when a management or unified workspace is in scope; backend-only delivers its business backend and developer admin URL without adding a frontend; frontend-only delivery includes its verified frontend entry and developer admin URL, without inventing a business backend. Always include developer adminUrl for complete application delivery unless explicitly excluded by the user, regardless of auth runtime; this is separate from the optional business backend. Use create-app or app-list URL fields and state any unavailable entry instead of silently omitting it. Put the verified business summary, results and remaining work in the delivery artifact description; use the final response when no delivery tool is available.',
2170
+ completion_contract: 'Clarify core functions and actual usage first, then resolve the required resource context and settle entry navigation before PRD planning. Fast generates prd.md and design.md in parallel; Plan uses design-plan init/preview/materialize/patch and confirms the current revision. Validate both artifacts, create or reuse app and core forms/processes, seed and query-check core normal-form samples or record an allowed skip reason, then implement the primary page in PRD resource order, publish it, apply PRD navigation order or lightweight fallback navigation order for platform navigation, or verify app and per-page hiding for application-wide custom navigation. For a standalone frontend in a platform-navigation app, verify that page independently and retain workspace navigation. Before returning one named application entry group, persist and read back the verified in-scope frontend/management access addresses through app-entry get/set; final business links come from urls.frontend/management, not the last publish result. Include workbench when a management or unified workspace is in scope; backend-only delivers its business backend and developer admin URL without adding a frontend; frontend-only delivery includes its verified frontend entry and developer admin URL, without inventing a business backend. Always include developer adminUrl for complete application delivery unless explicitly excluded by the user, regardless of auth runtime; this is separate from the optional business backend. Use create-app or app-list URL fields and state any unavailable entry instead of silently omitting it. Put the verified business summary, results and remaining work in the delivery artifact description; use the final response when no delivery tool is available.',
2126
2171
  },
2127
2172
  },
2128
2173
  };
@@ -48,9 +48,10 @@ module.exports = {
48
48
  },
49
49
  theme_source: { conflict: 'The theme source differs from the existing CSS or cannot be verified. Generate to a new output path, review and migrate project customizations before replacing the original file.' },
50
50
  app_entry: {
51
+ frontend_nav_required: 'Page {0} has no verified hidden-navigation setting. Persist renderNav=false and read it back before registering a standalone frontend.',
51
52
  desc_get: 'Read frontend and management runtime entries',
52
53
  desc_set: 'Register application entries with partial update and readback',
53
- usage: 'Usage: openyida app-entry get <appType> [--json]\n openyida app-entry set <appType> [--frontend <url>|--frontend-page <formUuid>] [--management <url>|--management-page <formUuid>] [--clear-frontend] [--clear-management] [--json]\nRegister only published, verified runtime URLs for this application. Complete URLs are preserved. --frontend-page requires a published display page; --management-page retains the workbench page ID. Generated URLs still need runtime verification; pass the complete URL for public entries. Omitted entries remain unchanged.',
54
+ usage: 'Usage: openyida app-entry get <appType> [--json]\n openyida app-entry set <appType> [--frontend <url>|--frontend-page <formUuid>] [--management <url>|--management-page <formUuid>] [--clear-frontend] [--clear-management] [--json]\nRegister only published, verified runtime URLs for this application. Complete URLs are preserved. --frontend-page requires a published display page with verified renderNav=false; --management-page retains the workbench page ID. Generated URLs still need runtime verification; pass the complete URL for public entries. Omitted entries remain unchanged.',
54
55
  invalid_url: 'Provide a complete http:// or https:// URL for a page, public page, or short link in the current application. Relative paths, designer URLs, and temporary authentication parameters are unsupported.',
55
56
  invalid_response: 'Incomplete entry response. Read the current entries before retrying and check that the server supports the entry configuration API.',
56
57
  readback_failed: 'Saved entries do not match the readback. Read and review the current entries; do not overwrite automatically.',
@@ -2247,3 +2248,16 @@ module.exports.app_theme = {
2247
2248
  };
2248
2249
  module.exports.help.cmd_app_theme_snapshot = 'Archive application theme settings and CSS (read-only)';
2249
2250
  module.exports.help.cmd_app_theme_restore = 'Check or restore an application theme snapshot (supported exact values only)';
2251
+
2252
+ module.exports.data_manage_style = {
2253
+ 'usage': 'openyida data-manage-style guide [--json]\nopenyida data-manage-style list [--json]\nopenyida data-manage-style generate --app APP_ID --form FORM-ID --style <id> --base-css <snapshot/theme.css> --output <new.css> [--focus balanced|actions] [--field-id <actualId>]... [--emphasize-field <actualId>]... [--tag-field <selectFieldId>]... [--follow-shell] [--json]',
2254
+ 'data_style_invalid': 'Invalid style parameters; use actual app/form/field IDs and a style returned by list.',
2255
+ 'data_style_base': 'Provide existing application theme CSS with a brand primary token, no larger than 20 MB.',
2256
+ 'data_style_marker': 'Owned style markers are malformed or duplicated; review the baseline file.',
2257
+ 'data_style_output': 'Output must be a new CSS file; inputs and existing files cannot be overwritten.',
2258
+ 'generated': 'Styles generated: {0}. Not published; complete backup, publication and runtime verification.'
2259
+ };
2260
+ module.exports.help.cmd_data_manage_style_list = 'List optional data-management recipe examples';
2261
+ module.exports.help.cmd_data_manage_style_generate = 'Generate data-management CSS from an optional recipe (local only)';
2262
+
2263
+ module.exports.help.cmd_data_manage_style_guide = 'Inspect data-management UI directions, tokens and scoped CSS guidance';
@@ -48,9 +48,10 @@ module.exports = {
48
48
  },
49
49
  theme_source: { conflict: '主题来源与现有 CSS 不一致或无法确认。请使用新的输出路径生成主题,核对并迁移项目定制后再替换原文件。' },
50
50
  app_entry: {
51
+ frontend_nav_required: '页面 {0} 尚未确认隐藏平台导航;先持久化 renderNav=false 并回读,再登记独立前台地址。',
51
52
  desc_get: '读取应用前台和业务工作台入口',
52
53
  desc_set: '登记应用访问入口(局部更新并回读)',
53
- usage: '用法:openyida app-entry get <appType> [--json]\n openyida app-entry set <appType> [--frontend <url>|--frontend-page <formUuid>] [--management <url>|--management-page <formUuid>] [--clear-frontend] [--clear-management] [--json]\n仅登记已发布并验证的当前应用访问态地址;完整 URL 原样保留;--frontend-page 仅支持已上线的 display 页,--management-page 保留工作台页面 ID;页面参数生成的 URL 仍需访问验收,公开入口请传完整 URL。未指定的入口保持不变。',
54
+ usage: '用法:openyida app-entry get <appType> [--json]\n openyida app-entry set <appType> [--frontend <url>|--frontend-page <formUuid>] [--management <url>|--management-page <formUuid>] [--clear-frontend] [--clear-management] [--json]\n仅登记已发布并验证的当前应用访问态地址;完整 URL 原样保留;--frontend-page 仅支持已上线且回读确认 renderNav=false 的 display 页,--management-page 保留工作台页面 ID;页面参数生成的 URL 仍需访问验收,公开入口请传完整 URL。未指定的入口保持不变。',
54
55
  invalid_url: '请提供当前组织、当前应用的完整访问 URL(http:// 或 https://);支持页面地址、公开地址和短链接,不支持相对路径、开发后台和临时鉴权参数。',
55
56
  invalid_response: '访问入口响应不完整,请重新读取后再决定是否重试,并确认服务端支持入口配置接口。',
56
57
  readback_failed: '访问入口保存后的回读结果不一致,请重新读取并确认;不要自动覆盖。',
@@ -2201,3 +2202,16 @@ module.exports.app_theme = {
2201
2202
  };
2202
2203
  module.exports.help.cmd_app_theme_snapshot = '保存应用主题配置与 CSS 快照(只读)';
2203
2204
  module.exports.help.cmd_app_theme_restore = '检查或恢复应用主题快照(仅支持可精确恢复的配置)';
2205
+
2206
+ module.exports.data_manage_style = {
2207
+ 'usage': 'openyida data-manage-style guide [--json]\nopenyida data-manage-style list [--json]\nopenyida data-manage-style generate --app APP_ID --form FORM-ID --style <id> --base-css <snapshot/theme.css> --output <new.css> [--focus balanced|actions] [--field-id <actualId>]... [--emphasize-field <actualId>]... [--tag-field <selectFieldId>]... [--follow-shell] [--json]',
2208
+ 'data_style_invalid': '数据管理样式参数无效;请使用真实应用、表单和字段 ID,以及 list 返回的样式。',
2209
+ 'data_style_base': '请提供有效的现有应用主题 CSS(包含品牌主色,且不超过 20 MB)。',
2210
+ 'data_style_marker': '当前表单的样式标记损坏或重复,已停止;请核对基线文件。',
2211
+ 'data_style_output': '输出必须是新的 CSS 文件,不能覆盖输入或已有文件。',
2212
+ 'generated': '样式已生成:{0}。尚未发布,需完成备份、发布与实际页面核验。'
2213
+ };
2214
+ module.exports.help.cmd_data_manage_style_list = '列出可选的数据管理样式配方参考';
2215
+ module.exports.help.cmd_data_manage_style_generate = '按可选配方生成数据管理 CSS(仅本地)';
2216
+
2217
+ module.exports.help.cmd_data_manage_style_guide = '查询数据管理 UI 改造方向、token 与 CSS 定位方法';
@@ -103,12 +103,25 @@ function createThemePreview(args) {
103
103
  // Escape HTML-significant characters in the JSON data block, never interpolate markup.
104
104
  const json = JSON.stringify({ themes, ...(popArtContext ? { popArtContext } : {}) }).replace(/[<>&\u2028\u2029]/g,
105
105
  char => '\\u' + char.charCodeAt(0).toString(16).padStart(4, '0'));
106
+ const escapeHtml = value => String(value).replace(/[&<>"']/g, char => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[char]));
107
+ // Conversation attachment renderers may disable scripts. Named templates use
108
+ // their catalog image verbatim; render actual cards instead of an empty JS shell.
109
+ const cards = themes.map(({ id, name, img }, index) => {
110
+ const image = typeof img === 'string' && /^https:\/\//i.test(img)
111
+ ? `<img class="preview" src="${escapeHtml(img)}" alt="${escapeHtml(name)} · 效果参考图">` : '';
112
+ return `<article class="card" role="listitem" data-theme-id="${escapeHtml(id)}" aria-labelledby="theme-title-${index}"><h2 id="theme-title-${index}">${escapeHtml(name)}</h2>${image}</article>`;
113
+ }).join('\n');
106
114
  const html = fs.readFileSync(TEMPLATE_SOURCE, 'utf8').replace(
107
115
  '<script id="preview-data" type="application/json">{}</script>',
108
- () => `<script id="preview-data" type="application/json">${json}</script>`);
116
+ () => `<script id="preview-data" type="application/json">${json}</script>`)
117
+ .replace('<div id="themes" class="themes" role="list" aria-label="风格效果参考"></div>',
118
+ () => `<div id="themes" class="themes" role="list" aria-label="风格效果参考">${cards}</div>`)
119
+ .replace(/\s*<script>[\s\S]*?<\/script>/g, '');
109
120
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
110
121
  fs.writeFileSync(outputPath, html, { flag: 'wx' });
111
122
  return { success: true, purpose: 'preview_only', selectionTool: 'ask_human', outputPath,
123
+ attachment: { name: path.basename(outputPath), path: outputPath },
124
+ presentation: require('../design-plan/visual-policy').getVisualDecisionPolicy().themeSelection.preview.presentation,
112
125
  themes: themes.map(({ id, name }, index) => ({ number: index + 1, themeId: id, name })) };
113
126
  }
114
127
 
@@ -41,7 +41,9 @@ function buildEntryNavigation(plan, resources, pages) {
41
41
  assert(entry.sceneKey === undefined || page, `入口 ${entry.name} 引用了未知 sceneKey`);
42
42
  assert(entry.sceneKey === undefined || pages.filter(item => item.sceneKey === entry.sceneKey).length === 1, `入口 ${entry.name} 的 sceneKey 必须唯一关联页面`);
43
43
  if (entry.role === 'service') {
44
- assert(page?.pageSpecHandoff.entryMode === 'standalone', `访客入口 ${entry.name} 必须关联独立页面`);
44
+ // Standalone is inferred by default; an explicit platform-shell choice
45
+ // must remain usable without forcing page or application navigation off.
46
+ assert(page, `访客入口 ${entry.name} 必须关联页面`);
45
47
  }
46
48
  assert(Array.isArray(entry.menu) && entry.menu.length > 0, `入口 ${entry.name} 缺少任务菜单,单步入口也需记录唯一任务`);
47
49
  const keys = new Set();
@@ -251,7 +251,8 @@ function initialize(inputPath, options = {}) {
251
251
  '首页按使用者的主要任务选择:集中办事可用门户式工作台(workbench),单项任务可直达原生业务页;只有已确认的指标分析或投屏监控任务才规划 dashboard/screen。工作台是围绕应用核心业务,把全局概况、业务管理、分析判断、异常处理和常用快捷入口组织在一起的综合工作空间;包含分析区块不自动改变整页主任务。不得从后台身份、系统名称或暗色风格自动增加驾驶舱。完整规则见 yida-requirement-analysis/references/experience-groups.md 的“首页按任务选择”。', '',
252
252
  '工作台按角色与业务关系规划,参考 8-10 个有独立价值的区块;采用适合信息特点的表现形式,数据和洞察有依据,操作与快捷入口可用,不套固定列表组合。', '',
253
253
  '无论是否分前后台,每个业务入口都要确定默认页面、菜单及分组顺序、打开后的首屏任务。menu 数组记录展示顺序,defaultMenuKey 记录默认任务;按角色需求决定,不按资源创建先后排列。平台导航通过排序命令落实,自定义导航通过页面菜单落实,frontend-only 也必须完成入口排序与首页验收。', '',
254
- '入口方案沿用 execution.entryRecommendation:unified 表示不分前后台,service-management 表示分前后台,frontend-only 表示只有访问前台,backend-only 表示只有访问后台。向用户说明谁使用、看到什么、能做什么;只有前台且提交后需要处理时,在 reason 和业务流程中说明承接方式;逐入口补齐 role、menu 和 defaultMenuKey,前台关联独立页面 sceneKey。任何身份的入口含 local 菜单时,entry.sceneKey 必须关联承载页面,menu.resource 等于页面 name,viewKey 非空。管理端可直接落到原生业务视图,不强制创建首页。所有叶子菜单按资源、视图和操作记录 access 权限依赖,平台导航排序从管理菜单派生。详见 yida-app/references/entry-navigation.md。', '',
254
+ '入口方案沿用 execution.entryRecommendation:unified 表示不分前后台,service-management 表示分前后台,frontend-only 表示只有访问前台,backend-only 表示只有访问后台。向用户说明谁使用、看到什么、能做什么;只有前台且提交后需要处理时,在 reason 和业务流程中说明承接方式;逐入口补齐 role、menu 和 defaultMenuKey,前台关联承载页面 sceneKey,默认独立展示;用户明确保留平台导航时沿用 platform-shell。任何身份的入口含 local 菜单时,entry.sceneKey 必须关联承载页面,menu.resource 等于页面 name,viewKey 非空。管理端可直接落到原生业务视图,不强制创建首页。所有叶子菜单按资源、视图和操作记录 access 权限依赖,平台导航排序从管理菜单派生。详见 yida-app/references/entry-navigation.md。', '',
255
+ '前后台场景默认考虑在前台页头、头像菜单或导航操作区放置按当前访问者身份可见的业务后台次级入口,目标复用已登记并回读的 management 地址。应用管理员片段不能代替业务角色权限;普通、匿名或未知身份不展示,已有等效入口复用。记入现有页面操作说明即可,不新增必填字段、强校验或发布门禁。', '',
255
256
  theme.mode === 'creative'
256
257
  ? '保留 visual.json 中 facts.visualStyle.forUser.visualDirection 和 navigationStyle 的对象结构;自由创意需明确填写 structure=top|side 和 tone=light|dark,并配套导航 Token。'
257
258
  : `保留 visual.json 中 facts.visualStyle.forUser.visualDirection 和 navigationStyle 的对象结构,只补齐内部字段;前者包含 label/description,后者只需维护 structure=top|side。tone=${navTheme} 由已选主题模板派生,不要修改。`, '',
@@ -55,7 +55,7 @@ function getVisualDecisionPolicy(selectedTheme) {
55
55
  quality: ['task_efficiency', 'readability_and_contrast', 'consistent_spacing_and_alignment', 'coherent_visual_identity'],
56
56
  execution: hasSelection ? 'refine_selected_style_without_comparison_or_question' : 'resolve_style_intent_before_any_comparison; infer_directly_from_intent_or_extract_reference_visual_dna',
57
57
  handoff: ['visualSelection.visualDirection.description', 'visualSelection.colorStrategy.usage'],
58
- presentation: hasSelection ? 'continue_design_without_style_question' : 'fast_selects_internally; plan_asks_only_without_style_intent_visual_reference_reusable_selection_or_delegation',
58
+ presentation: hasSelection ? 'continue_design_without_style_question' : 'explicit_preview_to_help_choose_uses_ask_human_in_fast_and_plan; otherwise_fast_selects_internally_and_plan_asks_only_when_needed',
59
59
  themeSelection: {
60
60
  intent: {
61
61
  policy: 'Check the original request, latest reply and references first. Any stated style, color or tone is sufficient; infer missing details without asking for a theme or complete design system. A missing themeId does not mean missing intent. Compare only without intent, references or reusable choice, unless alternatives are explicitly requested. Delegated choices stay internal.',
@@ -67,18 +67,23 @@ function getVisualDecisionPolicy(selectedTheme) {
67
67
  reference: 'yida-skills/skills/yida-design/references/theme-selection.md#选择时展示名称与效果图',
68
68
  selectionTool: 'ask_human',
69
69
  allowStyleQuestion: !hasSelection,
70
- questionCondition: 'Ask only without style intent, visual reference, reusable choice or delegation; explicit requests for alternatives take precedence. allowStyleQuestion is conditional permission, not an instruction to ask.',
70
+ questionCondition: 'Ask only without style intent, visual reference, reusable choice or delegation; explicit requests for alternatives or preview-assisted user selection take precedence in both Fast and Plan. allowStyleQuestion is conditional permission, not an instruction to ask.',
71
71
  eligibility: { 'app-pop': POP_ART_ELIGIBILITY },
72
72
  preview: {
73
73
  trigger: 'explicit_user_request',
74
74
  defaultEnabled: false,
75
75
  purpose: 'preview_only',
76
+ imageSource: 'match_catalog_themeId_first_and_reuse_exact_previewImage_url; never_regenerate_or_substitute_a_named_template_image',
77
+ presentation: { format: 'html_card', surface: 'conversation', timing: 'same_turn_as_ask_human',
78
+ attachmentSource: 'theme-preview result.attachment',
79
+ selectionRequired: 'only_when_user_requests_help_choosing_or_a_choice_is_already_needed',
80
+ fallback: 'report_missing_inline_html_capability; link_or_browser_is_degraded_not_inline_delivery' },
76
81
  maxColumns: 6,
77
82
  desktopColumns: [4, 5, 6],
78
83
  content: ['name', 'img'],
79
84
  scope: 'all_requested_eligible_themes_in_request_order',
80
85
  prepareCommand: 'openyida sample yida-design theme-preview --theme-ids <comma-separated-ids> --output <preview.html> --json',
81
- policy: 'Theme previews are off by default on every host and display surface. Only explicit user requests for reference images, previews or effects permit generation or display. Host capabilities, Plan, theme choice, available image URLs and supplied references never trigger previews. Do not ask whether to preview or claim unseen images were shown. Show requested eligible themes in order: titles and available images only, responsive columns, unlimited rows; omit empty images. HTML is display-only with no selection or bridge submission. Use supported HTML, images or accessible links. Previewing never reopens selection.',
86
+ policy: 'Theme previews are off by default on every host and display surface. Only explicit user requests for reference images, previews or effects permit generation or display. Host capabilities, Plan, theme choice, available image URLs and supplied references never trigger previews. Do not ask whether to preview or claim unseen images were shown. Show requested eligible themes in order: titles and available images only, responsive columns, unlimited rows; omit empty images. HTML is display-only with no selection or bridge submission. Match the catalog first and reuse its exact previewImage URL for named templates, without image generation or substitute screenshots. Render HTML cards inside the conversation. When a choice is needed, attach the generated HTML to that same ask_human request; where attachments are unsupported, display the inline widget immediately before asking in the same turn. A browser tab or bare link alone is not inline delivery. Explicit requests for previews to help choose override Fast internal selection; preview-only requests and already selected themes do not create a new question. Previewing never reopens selection by itself.',
82
87
  },
83
88
  qwenwork: {
84
89
  activeToolId: 'qwenwork',
@@ -94,7 +99,7 @@ function getVisualDecisionPolicy(selectedTheme) {
94
99
  },
95
100
  interactionOrder: {
96
101
  reference: 'yida-skills/skills/yida-design/references/theme-selection.md#交互顺序',
97
- sequence: ['prepare_known_candidates', 'optional_requested_preview', 'batched_ask_human_if_needed', 'confirm_plan_if_required'],
102
+ sequence: ['prepare_known_candidates_and_requested_html', 'present_requested_html_with_batched_ask_human_if_needed', 'confirm_plan_if_required'],
98
103
  batchStyleWithOtherQuestions: true,
99
104
  maxPendingInteractions: 1,
100
105
  policy: 'Batch independent scope, mode and style questions into one ask_human call when candidates are ready and the host supports multiple questions. Keep one decision per question; split only for real answer dependencies or host limits. A preview is not another pending question. Never add a style-only round just for workflow stage ordering. Persist the explicit answer before later plan approval; ignore retired callbacks.',