openyida 2026.10.30-beta.1 → 2026.10.30

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 (33) hide show
  1. package/README.md +3 -16
  2. package/lib/app/custom-theme.js +21 -1
  3. package/lib/app/services/canvas-page-schema-builder.js +4 -0
  4. package/lib/app/update-app.js +47 -19
  5. package/lib/core/agent-capabilities.js +2 -0
  6. package/lib/core/browser-handoff.js +18 -4
  7. package/lib/core/command-manifest.js +6 -4
  8. package/lib/core/qwenwork-login-guidance.js +1 -1
  9. package/lib/design-plan/visual-policy.js +1 -1
  10. package/package.json +1 -1
  11. package/yida-skills/SKILL.md +3 -3
  12. package/yida-skills/references/execution-rules.md +17 -0
  13. package/yida-skills/references/setup-and-env.md +4 -2
  14. package/yida-skills/references/user-facing-summary.md +4 -4
  15. package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +2 -2
  16. package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +2 -0
  17. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +13 -11
  18. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +1 -1
  19. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +25 -22
  20. package/yida-skills/skills/yida-canvas-custom-page/references/waf-safe-authoring.md +14 -37
  21. package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +3 -17
  22. package/yida-skills/skills/yida-create-app/SKILL.md +8 -6
  23. package/yida-skills/skills/yida-design/SKILL.md +1 -1
  24. package/yida-skills/skills/yida-design/references/application-style-library.md +3 -1
  25. package/yida-skills/skills/yida-design/references/application-theme-consistency.md +4 -4
  26. package/yida-skills/skills/yida-design/references/theme-selection.md +7 -6
  27. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +10 -9
  28. package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/design.md +6 -3
  29. package/yida-skills/skills/yida-design/workflow/output-design.md +1 -1
  30. package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +3 -3
  31. package/yida-skills/skills/yida-dingtalk-openapi/references/connector-mapping.md +7 -3
  32. package/yida-skills/skills/yida-login/SKILL.md +2 -2
  33. package/yida-skills/skills/yida-publish-page/SKILL.md +4 -2
package/README.md CHANGED
@@ -143,9 +143,9 @@ openyida login --check-only --json
143
143
  openyida auth status
144
144
  ```
145
145
 
146
- In QwenWork web, `openyida login --qwenwork --json` (or ordinary `login`) sends the sandbox Agent Token and gateway origin directly to the selected YiDA authentication endpoint. YiDA validates the identity through the trusted gateway’s fixed `/api/agent-gw/yida/user_info` endpoint, then issues its own short-lived session. This login flow no longer calls the legacy exchange or `/user/info` endpoints. Login preparation stays silent; failures stop without browser OAuth. Desktop and other hosts retain their existing authentication paths.
146
+ In QwenWork web, `openyida login --qwenwork --json` (or ordinary `login`) reuses the sandbox identity. If cloud authentication fails, the same command falls back to OAuth; follow its authorization URL and wait for completion. Desktop and other hosts keep their existing login paths.
147
147
 
148
- QwenWork login supports DingTalk organizations, QwenWork organizations and personal ODS identities. Local ODS tenant IDs retain their `yida` prefix. When the server supplies a refresh token, the CLI stores it for the standard refresh flow. Optional server fields `identity_type` (`PERSONAL` / `ORGANIZATION`) and `organization_type` (`DINGTALK` / `QWENWORK` / `OTHER`, organizations only) are preserved in the session and `auth status`; historical sessions with both fields absent, null or empty default to `ORGANIZATION` / `DINGTALK`. Authentication failures expose bounded `status`, `stage`, `exchange_id`, HTTP `status_code` and local validation `reason` in JSON diagnostics, without response bodies or credentials.
148
+ Login supports DingTalk and QwenWork organizations, ODS and personal spaces. Sessions preserve server-provided identity classification, business domain and refresh credentials. Historical sessions default to DingTalk organizations. JSON errors include bounded diagnostics without credentials or response bodies.
149
149
 
150
150
  For QwenWork cloud identity diagnostics, run inside the actual cloud execution environment:
151
151
 
@@ -153,20 +153,7 @@ For QwenWork cloud identity diagnostics, run inside the actual cloud execution e
153
153
  openyida auth qwenwork user-info --json
154
154
  ```
155
155
 
156
- This diagnostic command uses the legacy user-token protocol; it does not validate the current web Agent Token login flow. Use `openyida login --check-only --json` to check the YiDA session.
157
-
158
- This command exchanges the injected `QWENWORK_AGENT_TOKEN` (legacy: `MULE_AGENT_TOKEN` / `AGENT_TOKEN`) at `QWENWORK_GATEWAY_URL` (legacy: `MULE_GATEWAY_URL`), then queries `/user/info` on the same origin. It reports identity and scope metadata without printing or persisting either credential. It does not create an OpenYida login session, and QwenWork organization IDs are not YiDA `corpId` values. Existing OAuth/profile behavior is unchanged.
159
-
160
- Programmatic callers can use `exchangeQwenWorkAgentToken`, `getQwenWorkUserInfo` and `getQwenWorkIdentity` from `lib/auth/qwenwork-client.js`. The exchange function returns an in-memory `access_token` credential for the identity query or a future server-side login exchange; callers must not log or persist it.
161
-
162
- ```js
163
- const { exchangeQwenWorkAgentToken, getQwenWorkUserInfo } = require('openyida/lib/auth/qwenwork-client');
164
- async function readCloudUser() {
165
- const credential = await exchangeQwenWorkAgentToken();
166
- // The result contains QwenWork identifiers, not YiDA IDs.
167
- return getQwenWorkUserInfo(credential);
168
- }
169
- ```
156
+ This legacy diagnostic reads the injected host credentials and reports identity and scope without printing or saving tokens. It does not create or validate a YiDA session; QwenWork organization IDs are not YiDA `corpId` values. Check the YiDA session with `openyida login --check-only --json`. See [QwenWork login guidance](yida-skills/references/qwenwork-cloud-login.md) for authentication behavior.
170
157
 
171
158
  To reuse an existing shared login profile for the current project, switch first instead of starting a new OAuth login:
172
159
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const { createHash } = require('crypto');
5
6
  const { resolveBearerAuthHeaders } = require('../core/utils');
6
7
  const { readManagedContext, managedError } = require('../agent/managed-context');
7
8
  const { CliError } = require('../core/cli-error');
@@ -306,7 +307,26 @@ async function uploadCustomThemeFile(appType, filePath, authRef, options = {}) {
306
307
  if (!response.ok) {
307
308
  throw new Error((payload && (payload.errorMsg || payload.message)) || `主题文件上传失败: HTTP ${response.status}`);
308
309
  }
309
- return payload;
310
+ // Hash multipart bytes, not reread files or server-supplied receipt fields.
311
+ if (payload?.success === false) {
312
+ return { ...payload, themeUpload: { requested: true, status: 'rejected', uploadedThisRun: false, appType } };
313
+ }
314
+ const upload = unwrapUploadResponse(payload);
315
+ return { ...payload, themeUpload: {
316
+ requested: true,
317
+ status: 'uploaded',
318
+ uploadedThisRun: true,
319
+ appType,
320
+ baseUrl: new URL(authRef.baseUrl).origin,
321
+ corpId: authRef.corpId || null,
322
+ sourceFileName: file.name,
323
+ sourceBytes: file.content.length,
324
+ sourceSha256: createHash('sha256').update(file.content).digest('hex'),
325
+ themeColor: file.themeColor,
326
+ cssUrl: upload.downloadUrl || upload.url,
327
+ cssFileName: upload.name || '',
328
+ resourceContentVerified: false,
329
+ } };
310
330
  }
311
331
 
312
332
  module.exports = {
@@ -81,6 +81,10 @@ var OPENYIDA_UTIL_API_METHODS = [
81
81
  function openyidaYidaApiBridgeTargets() {
82
82
  var targets = [];
83
83
  function pushTarget(target) {
84
+ if (target !== window) {
85
+ var origin = window.location.origin;
86
+ if (!origin || origin === 'null' || target.location.origin !== origin) { return; }
87
+ }
84
88
  if (target && targets.indexOf(target) === -1) {
85
89
  targets.push(target);
86
90
  }
@@ -566,6 +566,25 @@ function themeSettingsMatch(expected, actual) {
566
566
  ['cssUrl', 'cssFileName'].every((key) => (expectedStyle[key] || '') === (actualStyle[key] || ''));
567
567
  }
568
568
 
569
+ function themeReadbackEvidence(params, details) {
570
+ const operation = params.disableCustomTheme ? 'disable'
571
+ : params.customThemeStyle ? 'bind_resource'
572
+ : params.colour || params.themeColor ? 'update_theme' : 'preserve';
573
+ return { ...details, scope: 'configuration_readback', operation, runtimeVerified: false };
574
+ }
575
+
576
+ function noThemeUpload(appType) {
577
+ return { requested: false, status: 'not_requested', uploadedThisRun: false, appType };
578
+ }
579
+
580
+ function themeFailureEvidence(themeUpload) {
581
+ const uncertain = themeUpload.uploadedThisRun === true || themeUpload.status === 'unknown';
582
+ return { themeUpload, runtimeVerification: { verified: false, status: 'not_checked' },
583
+ ...(uncertain ? { target: { appType: themeUpload.appType }, retrySafe: false,
584
+ sideEffectState: 'unknown', readbackAllowed: true } : {}),
585
+ };
586
+ }
587
+
569
588
  function getRequestedNavigationSettings(params, payload) {
570
589
  return Object.fromEntries(['navTheme', 'layoutDirection', 'hideAppNav', 'logoSource']
571
590
  .filter((name) => params[name] !== undefined && params[name] !== null && params[name] !== '')
@@ -631,7 +650,7 @@ async function saveAppSettings(params, authRef) {
631
650
  if (themeVerified && navigationVerified) {
632
651
  return { ...response,
633
652
  ...(submittedFields ? { submittedFields } : {}),
634
- ...(verifyTheme ? { themeVerification: { verified: true, ...actual } } : {}),
653
+ ...(verifyTheme ? { themeVerification: themeReadbackEvidence(params, { verified: true, ...actual }) } : {}),
635
654
  ...(navigationFields.length ? { navigationVerification: { verified: true, ...actualNavigation } } : {}),
636
655
  };
637
656
  }
@@ -646,8 +665,8 @@ async function saveAppSettings(params, authRef) {
646
665
  ...(submittedFields ? { submittedFields } : {}),
647
666
  errorCode: themeVerified ? 'APP_NAVIGATION_NOT_PERSISTED' : 'APP_THEME_NOT_PERSISTED',
648
667
  errorMsg: t(themeVerified ? 'update_app.navigation_not_persisted' : 'update_app.theme_not_persisted'),
649
- ...(verifyTheme ? { themeVerification: themeVerified
650
- ? { verified: true, ...actual } : { verified: false, expected, actual, readbackError } } : {}),
668
+ ...(verifyTheme ? { themeVerification: themeReadbackEvidence(params, themeVerified
669
+ ? { verified: true, ...actual } : { verified: false, expected, actual, readbackError }) } : {}),
651
670
  ...(navigationFields.length ? { navigationVerification: navigationVerified
652
671
  ? { verified: true, ...actualNavigation }
653
672
  : { verified: false, expected: expectedNavigation, actual: actualNavigation, readbackError } } : {}),
@@ -663,22 +682,25 @@ async function applyCustomThemeUpdate(appType, themeParams, authRef) {
663
682
  assertNavTheme(params.navTheme);
664
683
  assertLogoSource(params.logoSource);
665
684
 
666
- assertCustomThemeOptions(params);
667
- await checkAppStyleInjection(params, authRef);
668
- const themeFile = readThemeCssFile(params.themeFile);
669
- params.themeColor = themeFile.themeColor;
670
- const uploadResponse = await requestWithAutoLogin(
671
- (auth) => uploadCustomThemeFile(appType, params.themeFile, auth),
672
- authRef
673
- );
674
- params.customThemeStyle = buildCustomThemeStyle(uploadResponse);
675
-
685
+ let themeUpload = { requested: true, status: 'not_started', uploadedThisRun: false, appType };
676
686
  try {
677
- return await saveAppSettings(params, authRef);
687
+ assertCustomThemeOptions(params);
688
+ await checkAppStyleInjection(params, authRef);
689
+ readThemeCssFile(params.themeFile);
690
+ // Lost responses leave upload state unknown.
691
+ themeUpload = { ...themeUpload, status: 'unknown', uploadedThisRun: null };
692
+ const uploadResponse = await requestWithAutoLogin(
693
+ (auth) => uploadCustomThemeFile(appType, params.themeFile, auth),
694
+ authRef
695
+ );
696
+ if (uploadResponse.themeUpload) {themeUpload = uploadResponse.themeUpload;}
697
+ params.customThemeStyle = buildCustomThemeStyle(uploadResponse);
698
+ params.themeColor = themeUpload.themeColor;
699
+ return { ...await saveAppSettings(params, authRef), themeUpload };
678
700
  } catch (error) {
679
- error.details = { ...error.details, appType, uploadedResource: params.customThemeStyle,
680
- applicationSettingsVerified: false };
681
- throw error;
701
+ throwCommandError(error.message, { code: error.code || 'APP_THEME_UPDATE_FAILED', exitCode: error.exitCode,
702
+ details: { ...error.details, appType, uploadedResource: params.customThemeStyle,
703
+ ...themeFailureEvidence(themeUpload), applicationSettingsVerified: false } });
682
704
  }
683
705
  }
684
706
 
@@ -767,6 +789,8 @@ async function run(args) {
767
789
  postData
768
790
  );
769
791
  }, authRef);
792
+ const themeUpload = params.themeFile ? response?.themeUpload : noThemeUpload(params.appType);
793
+ if (themeUpload?.uploadedThisRun) {params.themeColor = themeUpload.themeColor;}
770
794
 
771
795
  // Verify the persisted resource before exposing success or a mutation receipt.
772
796
  let nameVerification;
@@ -789,9 +813,10 @@ async function run(args) {
789
813
  success: true,
790
814
  appType: params.appType,
791
815
  themeVerification: response.themeVerification,
816
+ themeUpload,
792
817
  nameVerification,
793
818
  navigationVerification: response.navigationVerification,
794
- ...(params.disableCustomTheme ? { runtimeVerification: { verified: false } } : {}),
819
+ runtimeVerification: { verified: false, status: 'not_checked' },
795
820
  updatedFields: {
796
821
  name: params.name || undefined,
797
822
  desc: params.desc || undefined,
@@ -810,11 +835,14 @@ async function run(args) {
810
835
  const errorMsg = response ? response.errorMsg || t('common.unknown_error') : t('common.request_failed');
811
836
  chalkResult(false, t('update_app.failed', errorMsg));
812
837
  console.log(JSON.stringify({ success: false, appType: params.appType, error: errorMsg,
838
+ themeUpload,
839
+ runtimeVerification: { verified: false, status: 'not_checked' },
813
840
  submittedFields: response && response.submittedFields,
814
841
  errorCode: response && response.errorCode, themeVerification: response && response.themeVerification,
815
842
  navigationVerification: response && response.navigationVerification }));
816
843
  throwCommandError(errorMsg, { code: response && response.errorCode,
817
- details: response && (response.errorCode === 'APP_NAVIGATION_NOT_PERSISTED' ? response.navigationVerification : response.themeVerification) });
844
+ details: response && { ...(response.errorCode === 'APP_NAVIGATION_NOT_PERSISTED' ? response.navigationVerification : response.themeVerification),
845
+ ...themeFailureEvidence(themeUpload) } });
818
846
  }
819
847
  }
820
848
 
@@ -356,6 +356,7 @@ function buildBuilderPath(login, projectRoot, manifest, env = process.env, runti
356
356
  },
357
357
  auth,
358
358
  runtime,
359
+ browser_verification: require('./browser-handoff').browserVerificationPolicy(runtime, env),
359
360
  interactive_login: buildInteractiveLogin(auth, runtime),
360
361
  communication: isQwenWorkWebRuntime(runtime) || (runtime.tool === 'qwenwork' && runtime.subtype === 'qwenwork_desktop') ? {
361
362
  preparation_visibility: 'silent',
@@ -484,6 +485,7 @@ function compactBuilderPath(builderPath) {
484
485
  preflight: builderPath.preflight,
485
486
  auth,
486
487
  runtime: builderPath.runtime,
488
+ browser_verification: builderPath.browser_verification,
487
489
  interactive_login: builderPath.interactive_login,
488
490
  communication: builderPath.communication,
489
491
  environment_check_simplification: {
@@ -1,9 +1,20 @@
1
1
  'use strict';
2
2
 
3
- const { detectActiveTool } = require('./utils');
3
+ const { detectActiveTool, detectRuntimeCapabilities } = require('./utils');
4
4
 
5
5
  const BROWSER_TOOLS = ['codex', 'qwenwork', 'qoderwork', 'qoder'];
6
6
 
7
+ function browserVerificationPolicy(runtime = {}, env = process.env) {
8
+ const remote = ['cloud', 'remote'].includes(String(env.OPENYIDA_MANAGED_RUNTIME || '').toLowerCase()) ||
9
+ ['web_sandbox', 'cloud', 'remote'].includes(runtime.runtime) || runtime.tool === 'mulerun' ||
10
+ !!(env.CI || env.CODEX_CI || env.SSH_CONNECTION || env.SSH_CLIENT || env.SSH_TTY ||
11
+ env.VSCODE_REMOTE_NAME || env.CODESPACES || env.GITPOD_WORKSPACE_ID || /^rdp/i.test(env.SESSIONNAME || ''));
12
+ const local = !remote && ['desktop_shell', 'local'].includes(runtime.runtime);
13
+ return { execution_location: remote ? 'remote' : local ? 'local' : 'unknown',
14
+ allowed: local, action: local ? 'use_existing_local_browser_tool' : 'do_not_open_browser',
15
+ unavailable: 'record_internal_not_checked_and_continue_readback', default_user_action_required: false };
16
+ }
17
+
7
18
  function parseOpenOption(args) {
8
19
  let mode = null;
9
20
  const filteredArgs = [];
@@ -46,11 +57,13 @@ function buildBrowserHandoff(url, options = {}, mode = null) {
46
57
  return null;
47
58
  }
48
59
 
60
+ const policy = browserVerificationPolicy(typeof detectRuntimeCapabilities === 'function' ? detectRuntimeCapabilities() : {});
49
61
  return {
50
- status: 'open_url',
62
+ status: policy.allowed ? 'open_url' : 'present_url',
51
63
  handoff_type: 'browser',
52
- can_auto_use: true,
53
- agent_action: 'open_url_in_in_app_browser',
64
+ can_auto_use: policy.allowed,
65
+ agent_action: policy.allowed ? 'open_url_in_in_app_browser' : 'present_url_to_user',
66
+ browser_verification: policy,
54
67
  url,
55
68
  stage: options.stage,
56
69
  title: options.title,
@@ -69,6 +82,7 @@ function withBrowserHandoff(payload, url, options = {}, mode = null) {
69
82
  }
70
83
 
71
84
  module.exports = {
85
+ browserVerificationPolicy,
72
86
  parseOpenOption,
73
87
  buildBrowserHandoff,
74
88
  withBrowserHandoff,
@@ -1372,7 +1372,7 @@ const COMMAND_GROUPS = [
1372
1372
  notes: ['Updates completed fact modules in preview/ drafts. Modules share a source base; one writer updates changed sections and preserves custom CSS. Final materialize validates the complete plan and writes PRD, design, HTML and theme together.'],
1373
1373
  }),
1374
1374
  command('design-plan.materialize', ['design-plan', 'materialize'], 'design-plan materialize <build-plan.json> [--from-preview | --business-file <json> --visual-file <json>] [--output-dir <dir>] [--rebase-parts] [--check] [--json]', 'help.cmd_design_plan_materialize', { managedRun: {'scope':'local'},
1375
- notes: ['Local validation failures include repair.inputHash/errorHash. Do not retry unchanged inputs and arguments; stop after two repair attempts with the same error set. CLI reports diagnostics without persisting retry counters.', 'Validates readable text/surface pairs before materializing. Known low contrast returns DESIGN_THEME_CONTRAST_LOW and never silently recolors the project. Use check-design --json to inspect uncomputed pairs and review them in the browser.', 'Derives navigationStyle.tone and appConfig.navTheme from the selected theme template. Navigation appearance tokens come directly from that template plus explicit visualStyle.tokens; mode-independent tokens such as --pod-nav-menu-item-selected-shadow flow through the base token contract; materialize does not synthesize a light/dark replacement palette. Free creative uses project-authored navigation tone and tokens. Validates image slots before writing artifacts. JSON assetTasks lists taskKey/taskState, independent searches ordered by required/hero/design order, collection-wide concurrency, host-enforced time budgets, two-round limits, resume policy, output paths and resolve argv. After matching plan confirmation, executionContract requires checking background Agent and Bash separately, dispatching only verified nonblocking tools or doing authorized business work before synchronous asset batches; record real job IDs and continue app/form/page work; replace <appType> with the real ID for upload. Materialized PRD execution pages include navigationPolicy derived from app/page navigation: platform-shell is content-only; standalone without a planned menu renders no application menu. This command performs no search or upload. Local asset inputs resolve against the command cwd; absolute paths survive page-task directory changes.'],
1375
+ notes: ['Local validation failures include repair.inputHash/errorHash. Do not retry unchanged inputs and arguments; stop after two repair attempts with the same error set. CLI reports diagnostics without persisting retry counters.', 'Validates readable text/surface pairs before materializing. Known low contrast returns DESIGN_THEME_CONTRAST_LOW and never silently recolors the project. Use check-design --json to inspect uncomputed pairs. Browser review follows builder_path.browser_verification and is limited to a local agent on the user machine.', 'Derives navigationStyle.tone and appConfig.navTheme from the selected theme template. Navigation appearance tokens come directly from that template plus explicit visualStyle.tokens; mode-independent tokens such as --pod-nav-menu-item-selected-shadow flow through the base token contract; materialize does not synthesize a light/dark replacement palette. Free creative uses project-authored navigation tone and tokens. Validates image slots before writing artifacts. JSON assetTasks lists taskKey/taskState, independent searches ordered by required/hero/design order, collection-wide concurrency, host-enforced time budgets, two-round limits, resume policy, output paths and resolve argv. After matching plan confirmation, executionContract requires checking background Agent and Bash separately, dispatching only verified nonblocking tools or doing authorized business work before synchronous asset batches; record real job IDs and continue app/form/page work; replace <appType> with the real ID for upload. Materialized PRD execution pages include navigationPolicy derived from app/page navigation: platform-shell is content-only; standalone without a planned menu renders no application menu. This command performs no search or upload. Local asset inputs resolve against the command cwd; absolute paths survive page-task directory changes.'],
1376
1376
  resultReuse: { mode: 'local_input_dependent', inputs: ['buildPlan', 'businessFile', 'visualFile', 'preview'], onInputChange: 'rerun_after_known_success', writes: 'local_artifacts_only' },
1377
1377
  requiresLogin: false,
1378
1378
  output: 'text|json',
@@ -1419,11 +1419,12 @@ const COMMAND_GROUPS = [
1419
1419
  { name: 'showAppNav', type: 'boolean', source: 'option', builder_options: ['--show-app-nav'] },
1420
1420
  ],
1421
1421
  notes: [
1422
- 'Before uploading or binding CSS, read server-resolved appThemeEnable/appThemeMode and verify app/corp identity. Only y/modern allows automatic app-level injection. Legacy or unknown capability requires asking the human about this exact application and reviewed stylesheet scope before --confirm-legacy-app-style; unavailable confirmation means stop before uploading. Prefer page-scoped styles for legacy applications. --disable-custom-theme retains the existing resource URL/name and sets enabled/iframePropagation to boolean false. Never submit an empty style or remove the resource URL. Inspect strict configuration readback, then refresh the actual visit page to verify link#yida-app-custom-theme is absent and the CSS is not requested; HTTP success alone is insufficient.',
1422
+ 'Before uploading or binding CSS, read server-resolved appThemeEnable/appThemeMode and verify app/corp identity. Only y/modern allows automatic app-level injection. Legacy or unknown capability requires asking the human about this exact application and reviewed stylesheet scope before --confirm-legacy-app-style; unavailable confirmation means stop before uploading. Prefer page-scoped styles for legacy applications. --disable-custom-theme retains the existing resource URL/name and sets enabled/iframePropagation to boolean false. Never submit an empty style or remove the resource URL. Inspect strict configuration readback. Checking that link#yida-app-custom-theme is absent and CSS is not requested follows builder_path.browser_verification; HTTP success alone is insufficient.',
1423
1423
  'Upload application CSS with --theme-file; the primary color is read from CSS and saved with the theme resource. Edit navigation radius, three border states and selected shadow in design.md, regenerate CSS, then upload; these tokens are not separate update-app flags. Unknown options, missing values and combined --hide-app-nav/--show-app-nav are rejected.',
1424
+ 'themeUpload records this invocation: target app/environment, source name/bytes/SHA-256, themeColor and resource URL. resourceContentVerified=false means no remote byte comparison. Without --theme-file it is not_requested/uploadedThisRun=false. Unknown upload or failed binding needs readback before another upload; accepted uploads retain their receipt.',
1424
1425
  'One design document and theme CSS cover navigation, application shell, native forms, custom pages and record details. tokens.application-global.appearance.navigation defines navigation surfaces, text and interaction states, logo, groups, search, popups, borders, radius and density. Platform-supported tokens are applied through the active navigation mode; page content consumes the same application theme.',
1425
1426
  'Applications with appThemeEnable=n or appThemeMode=legacy persist side/top/l_shape as ver/hoz/ver and map unsupported white/gray navigation colors to legacy light. Existing legacy layouts and navType are preserved. Unknown theme capability keeps the existing save protocol.',
1426
- 'navigationVerification reads back explicitly requested navTheme, layoutDirection, hideAppNav and logoSource. A mismatch returns APP_NAVIGATION_NOT_PERSISTED and a nonzero exit code; only readback is retried. themeVerification independently verifies the uploaded theme resource. Verify rendered navigation, form, custom page and record detail after both checks pass.',
1427
+ 'navigationVerification checks requested navTheme/layoutDirection/hideAppNav/logoSource; mismatch is APP_NAVIGATION_NOT_PERSISTED, nonzero, with read-only retries. themeVerification.scope=configuration_readback; operation is bind_resource/update_theme/disable/preserve. preserve is not upload evidence. New themes need an uploaded receipt and matching enabled resource/color. runtimeVerification stays false/not_checked. Browser checks follow builder_path.browser_verification; only a local agent on the user machine may run them. Cloud/sandbox/remote/unknown keep not_checked internal and deliver confirmed results and entry URLs, without default user verification tasks.',
1427
1428
  'For generated design artifacts, pass themeProfile.navTheme from design.md to --nav-theme. Do not infer it from the business brief or omit it expecting a light default.',
1428
1429
  'Platform preset keys are used independently of --theme-file and --theme-color. Choose --hide-app-nav or --show-app-nav to set navigation visibility.',
1429
1430
  ],
@@ -1765,7 +1766,7 @@ const COMMAND_GROUPS = [
1765
1766
  command('check-design', ['check-design'], 'check-design <design.md> [--prd <prd.md>] [--base-dir <dir>] [--json]', 'help.cmd_check_design', { managedRun: { scope: 'local' },
1766
1767
  requiresLogin: false,
1767
1768
  output: 'text|json',
1768
- notes: ['Checks 26 text/surface pairs with alpha compositing and a 4.5:1 minimum. Known failures return DESIGN_THEME_CONTRAST_LOW with token names and ratios; paletteContrast.unresolved requires browser review and is not a pass. Disabled colors and decorative swatches are excluded. Gradients, images and local component CSS need visual verification. Theme generation with a complete design document uses the same check and never silently recolors the project.', 'Local validation failures include repair.inputHash/errorHash. Do not retry unchanged inputs and arguments; stop after two repair attempts with the same error set. CLI reports diagnostics without persisting retry counters.', 'Checks declared iconSystem.colorPairs against resolved design tokens. Each pair has a unique name, foreground, background and an opaque surface when the background is translucent. Icon contrast must reach 3:1; JSON iconContrast reports the checked pairs. Unsupported colors and missing surfaces fail rather than count as verified. This does not inspect browser styles, inherited opacity, gradients or undeclared icon colors.'],
1769
+ notes: ['Checks 26 text/surface pairs with alpha compositing and a 4.5:1 minimum. Known failures return DESIGN_THEME_CONTRAST_LOW with token names and ratios; paletteContrast.unresolved remains unverified; browser review follows builder_path.browser_verification and is limited to a local agent on the user machine. Disabled colors and decorative swatches are excluded. Gradients, images and local component CSS need visual verification. Theme generation with a complete design document uses the same check and never silently recolors the project.', 'Local validation failures include repair.inputHash/errorHash. Do not retry unchanged inputs and arguments; stop after two repair attempts with the same error set. CLI reports diagnostics without persisting retry counters.', 'Checks declared iconSystem.colorPairs against resolved design tokens. Each pair has a unique name, foreground, background and an opaque surface when the background is translucent. Icon contrast must reach 3:1; JSON iconContrast reports the checked pairs. Unsupported colors and missing surfaces fail rather than count as verified. This does not inspect browser styles, inherited opacity, gradients or undeclared icon colors.'],
1769
1770
  args: [
1770
1771
  { name: 'design', type: 'file', source: 'positional', position: 0, required: true },
1771
1772
  { name: 'prd', type: 'file', source: 'option', builder_options: ['--prd'] },
@@ -2344,6 +2345,7 @@ function summarizeLocalizedCommands(commands) {
2344
2345
  failures: 'State what failed, the actual impact and the next safe action. Explain only evidenced causes; describe unknown outcomes as pending verification.',
2345
2346
  waiting: 'Explain what is taking time and what can continue. Give time estimates only with a basis and update them when circumstances change.',
2346
2347
  diagnostics: 'Keep exact technical fields and error codes in tool results and implementation handoffs; explain them when the user asks for technical details.',
2348
+ browser_acceptance: 'Cloud browser checks are not applicable, not failed delivery work. Keep not_checked internal; no install reports or default open/refresh/screenshot requests. Report unverified functions only for requested acceptance or business issues; never claim visual verification.',
2347
2349
  },
2348
2350
  optional_asset_branch: {
2349
2351
  skill_id: 'yida-image-assets',
@@ -5,7 +5,7 @@ const path = require('node:path');
5
5
  /** 从当前调用或同一安装前缀定位入口,不依赖模型重写 PATH。 */
6
6
  function resolveExecutable(invoked = process.argv[1], packageRoot = path.resolve(__dirname, '../..')) {
7
7
  const usable = value => typeof value === 'string' && /^(?:\/|[a-z]:[\\/])/i.test(value)
8
- && /^[\w./:\\-]+$/.test(value) && /^openyida(?:\.cmd|\.exe)?$/i.test(path.basename(value.replace(/\\/g, '/')));
8
+ && /^[\w./:\\~-]+$/.test(value) && /^openyida(?:\.cmd|\.exe)?$/i.test(path.basename(value.replace(/\\/g, '/')));
9
9
  if (usable(invoked)) { return invoked.replace(/\\/g, '/'); }
10
10
  const candidates = [path.resolve(packageRoot, '../../../bin/openyida'), path.resolve(packageRoot, '../../openyida.cmd')];
11
11
  for (const candidate of candidates) {
@@ -58,7 +58,7 @@ function getVisualDecisionPolicy(selectedTheme) {
58
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
- 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.',
61
+ policy: 'Honor the latest explicit style and color instructions and preserve the selected theme. Without newer instructions, retain its palette, materials and component rules. Apply requested colors through project tokens while preserving theme structure; clarify only actual conflicts. Avoid black, dark gray or gray-black as the default primary color for ordinary apps. Use black only for explicit dark-mode, high-contrast, luxury-brand or minimal-black intent. Template examples, frequency and order never make green or blue a default. CRM or inventory labels do not determine color: use actual tasks, brand evidence, reading conditions and status semantics for unspecified decisions. A missing themeId does not mean missing intent; any style, color or tone suffices. Compare only without intent, references or reusable choice, unless alternatives are requested; use different hue families or a neutral-led direction only when color is unconstrained. Delegated choices stay internal.',
62
62
  referenceVisual: {
63
63
  reference: 'yida-skills/skills/yida-design/references/reference-visual-dna.md',
64
64
  policy: 'Inspect the reference website or liked image itself, not its filename or OCR alone; images need no URL or style name. Extract color roles, typography, composition, density, surfaces and observed states. Preserve the URL/image identifier and distinguish observations from inferred values, hover, motion and responsiveness. Follow the linked guide to generate design.md and CSS through the existing CLI (Plan: visual.json, materialize, outputs.theme); prefer free-creative when templates distort the reference. Never reopen theme selection. If unreadable, state the missing evidence and use available material or request an accessible copy. Data links and bug screenshots alone are not style preferences.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.10.30-beta.1",
3
+ "version": "2026.10.30",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",
@@ -12,9 +12,9 @@ description: >
12
12
 
13
13
  在执行宜搭应用/页面/审批流等任务前先确认环境与登录态,再根据用户需求和已解析的 app/page/form/process 等上下文资源,分析任务属于完整搭建、已有资源补齐还是单点任务,并加载对应子技能执行。
14
14
 
15
- 完整系统不能只读本入口、查看 CLI 帮助后直接拼装命令:必须加载 `yida-app`,进入实施与收尾时继续读取对应 workflow。前后台场景默认是两个业务入口:前台页面自绘导航并持久化隐藏平台导航,后台使用原生导航,前台专用页面从后台 PC/移动端菜单隐藏;页面隐藏、菜单隐藏、权限和公开访问分别处理。两个入口均须实际打开验收,发布回读不代表交付完成。
15
+ 完整系统不能只读本入口、查看 CLI 帮助后直接拼装命令:必须加载 `yida-app`,进入实施与收尾时继续读取对应 workflow。前后台场景默认是两个业务入口:前台页面自绘导航并持久化隐藏平台导航,后台使用原生导航,前台专用页面从后台 PC/移动端菜单隐藏;页面隐藏、菜单隐藏、权限和公开访问分别处理。各入口核验配置与数据;浏览器验收仅限用户本机 Agent,按[证据规则](references/execution-rules.md#本地浏览器验收)执行。
16
16
 
17
- Fast / Plan 选定的主题和主色属于整个应用:导航、原生页、自定义页共用项目 design.md 与同一份应用主题 CSS。不能只把自定义页写成品牌色、让应用继续默认蓝色;发布不会自动保存应用主题。完整搭建交付前按 yida-app 回读应用主题设置并实测各类页面,未同步或未验证不得宣称主题统一。
17
+ Fast / Plan 选定的主题和主色属于整个应用:导航、原生页、自定义页共用项目 design.md 与同一份应用主题 CSS。不能只把自定义页写成品牌色、让应用继续默认蓝色;发布不会自动保存应用主题。交付前按 yida-app 回读主题设置;本机 Agent 按证据规则实测页面,配置回读不代表视觉已验证。
18
18
 
19
19
  ## 本地使用问题与贡献反馈
20
20
 
@@ -278,7 +278,7 @@ Fast / Plan 选定的主题和主色属于整个应用:导航、原生页、
278
278
 
279
279
  20. **报表和可视化先分流**:标准统计与原生报表用 `yida-report`;定制图表页面默认用 `yida-rechart`;只有明确 ECharts、维护旧 ECharts 页面或复杂 option 超出 Recharts 能力时用 `yida-chart`。
280
280
  21. **应用主题只有一份**:涉及应用蓝图、页面视觉、应用主题色、品牌色、全局换肤或 `--color-brand1-*` 时先读 `yida-design`。导航、应用框架、提交/编辑表单、记录详情和自定义页面按[整体主题规则](skills/yida-design/references/application-theme-consistency.md#导航与应用框架)一起设计。`app-theme.css` 只在应用级统一配置,各类页面消费同一份主题 token。严禁在页面级重复写入、同步或向上层注入主题样式;`YidaCodeCanvas` 源码只在 `YidaComp` 内消费现有主题 token。
281
- 22. **默认完成即停止**:完整应用默认以资源发布成功、轻量导航排序完成、示例数据就绪并提供按用途命名的真实入口与业务交付总结为 doneWhen;截图、精细导航整理和额外深读属于 optionalAfterDone,除非用户明确要求。
281
+ 22. **默认完成即停止**:完整应用默认以资源发布成功、轻量导航排序完成、示例数据就绪并提供按用途命名的真实入口与业务交付总结为 doneWhen;验收按[证据规则](references/execution-rules.md#发布与验收证据)执行;截图、精细导航整理和额外深读属于 optionalAfterDone,除非用户明确要求。
282
282
  23. **说清楚做什么**:用业务语言说明功能、进度、问题影响和处理办法。技术字段、命令、文件与排障过程留在内部。所有任务收尾只总结本轮结果,修改不重述完整应用或字段清单;详见[用户可见表达契约](skills/yida-design/references/ask-human-interaction-contract.md)。交付内容包含简短业务总结、核验结果、剩余事项与真实入口,按用途区分访问态前台、业务后台和开发者后台,最多三类。不得把需求信息文件、PRD、视觉设计、build manifest、资源清单、Schema 或每个表单/流程/报表分别登记成用户可见交付物;入口核验、开发者后台和范围组合按 Step 9 执行;用户明确排除的入口和单页任务范围优先。
283
283
  24. **任务复盘沉淀**:用户多次纠正、平台接口假成功、页面骨架共性质量问题、线上回读验收方法、一次性脚本可产品化等情况,完成前判断是否需要沉淀到 CLI、测试或 skill。
284
284
 
@@ -6,3 +6,20 @@
6
6
  | 查已有表单的字段 ID | 字段级命令优先内部解析;仅当歧义未解、页面/流程/公式/数据代码需要多字段映射,或要人工确认时,用 `openyida get-schema <appType> <formUuid> --compact --resolve-fields "字段名"`,只使用唯一命中的 `fieldId` |
7
7
  | 更新已有表单字段 | 表单用 `create-form` 的 update/add-option/bind-datasource/validation/rule:`openyida create-form update <appType> <formUuid> '[{"action":"update","label":"备注","changes":{"required":true}}]'`;CLI 内部读 schema 并输出 resolved/updated evidence,通常不需要先 `get-schema` |
8
8
  | 发布提示 corpId 不匹配 | 问用户:确认在当前组织继续操作已解析资源,或 `openyida logout` 后重新登录到正确组织 |
9
+
10
+ ## 发布与验收证据
11
+
12
+ 按环境、组织、appType 保存本轮 CLI 原始回执。
13
+
14
+ ### 主题上传与绑定
15
+
16
+ - 上传成功须 `themeUpload.status=uploaded`、`uploadedThisRun=true`。核对目标、文件名、`sourceSha256/sourceBytes` 和 `cssUrl`,保存到执行记录或 `build-manifest.themeEvidence`;CSS 变更后重新上传。
17
+ - 绑定成功须 `themeVerification.scope=configuration_readback`、`operation=bind_resource`、`verified=true`,且 `colour=custom`、主色符合设计、`customThemeStyle.enabled=true`、URL 与上传回执一致;导航单独回读。
18
+ - `not_requested` / `operation=preserve` 按已有主题复用记录。上传 `unknown` 或绑定失败时保留 appType、资源和错误,先只读核实,再决定是否重试。
19
+ - `sourceSha256` 对应实际上传字节;`resourceContentVerified=false` 表示远端文件内容尚未比对。`runtimeVerified=false` / `runtimeVerification.verified=false` 表示页面未验,`iframePropagation` 是配置;均不能单独诊断主题加载失败。`app-settings --section theme` 仅提供入口。
20
+
21
+ ### 本地浏览器验收
22
+
23
+ - 先读取 `builder_path.browser_verification` 并核实执行位置。浏览器验收仅限用户本机 Agent,使用已有工具、登录态和正确目标;云端、沙箱、远程或未知位置一律跳过,即使有浏览器、登录态或公开 URL,也不得打开。能力不足时记录待验,不安装工具或注入凭据补验。
24
+ - 各环境均执行编译、发布回读、资源/配置核对和数据 query。云端的页面未验状态留在内部,按[完成总结](user-facing-summary.md)交付业务结果与真实入口,不默认交给用户补验。
25
+ - 运行态通过须有页面和交互证据;`browser_handoff`、打开标签页及完整性检查只证明各自结果。用户明确要求的页面验收未执行时,记录未完成;入口按权威 URL 及配置回读登记。
@@ -1,6 +1,6 @@
1
1
  ## 千问办公网页态的后台登录
2
2
 
3
- 当前快照为 `runtime.tool=qwenwork`、`runtime.subtype=qwenwork_web` 且 `interactive_login.mode=cloud_token` 时,按[云端后台免登规则](qwenwork-cloud-login.md)执行快照推荐命令。CLI 自动消费沙箱凭据;成功后继续业务,失败不回退浏览器 OAuth,不读取或展示凭据。桌面态继续使用桌面换票。
3
+ 当前快照为 `runtime.tool=qwenwork`、`runtime.subtype=qwenwork_web` 且 `interactive_login.mode=cloud_token` 时,按[云端免登与 OAuth 回退规则](qwenwork-cloud-login.md)执行快照推荐命令并等待;需要授权时按 `oauth_fallback` 打开 CLI 输出的 URL。成功后继续业务,不读取或展示凭据。桌面态继续使用桌面换票。
4
4
 
5
5
  ## 千问办公桌面端
6
6
 
@@ -38,7 +38,7 @@ openyida login --check-only --json
38
38
  | command not found | 安装或更新 `openyida`;不要创建资源 |
39
39
  | `workdir_exists=false` 或 `active.projectRootExists=false` | 先执行 `openyida copy`;工作目录存在前不要创建资源 |
40
40
  | `auth_mode=token` 且 `status=ok` 或 `can_auto_use=true` | 继续执行 |
41
- | `interactive_login.mode=cloud_token` 且未登录 | 静默执行快照推荐的 `openyida login --qwenwork --json`;CLI 自动消费云端凭据,成功后继续业务;失败停止,不打开浏览器或回退 OAuth |
41
+ | `interactive_login.mode=cloud_token` 且未登录 | 执行推荐的 `openyida login --qwenwork --json` 并等待;免登认证失败由同一进程回退 OAuth,按 `oauth_fallback` 打开授权 URL;成功后继续业务 |
42
42
  | `auth_mode=token` 且 `status=profile_required` | 不猜组织;执行 `openyida auth profiles`,再用 `openyida auth profile switch <auth_profile>` 切换已有 profile |
43
43
  | snapshot 返回 `auth_source=env` / `failure_reason=env_token_missing` | 进入运行环境注入 token 模式;缺 token 时停止,让 Codex、yida-agent 等宿主注入 `OPENYIDA_ACCESS_TOKEN` 或 `OPENYIDA_REFRESH_TOKEN`;不要执行 OAuth |
44
44
  | `auth_mode=token`,未登录,且 `interactive_login.mode=cli_auto_open` | 只执行一次 `openyida login`,等待该命令结束,并使用其最终 JSON 判断结果 |
@@ -113,3 +113,5 @@ openyida auth refresh
113
113
  - 不要读取 `.cache/cookies*.json` 作为登录态。
114
114
  - 不要要求用户导出浏览器 Cookie。
115
115
  - 不要打印 Cookie、CSRF、`access_token` 或 `refresh_token`。
116
+
117
+ 环境检测后按[本地浏览器验收](execution-rules.md#本地浏览器验收)核对 `builder_path.browser_verification`;验收位置与 OAuth 登录浏览器分别判定。
@@ -26,11 +26,11 @@
26
26
 
27
27
  修复总结使用“已完成 ×× 问题的修复”,用一句话点明修复的问题及完成状态。多项修复合并概述;需要处理的剩余事项或用户操作,用一句话补充。
28
28
 
29
- 发布命令、CLI 参数和操作步骤只在用户明确询问命令或用法时提供。用户询问发布状态时,直接说明是否完成;询问排障或实现细节时,只解释所问范围,不自动附上 CLI 指令。
29
+ 发布状态直接说明结果。CLI 指令仅用户明确询问命令或用法时提供;排障和实现细节只解释所问范围。
30
30
 
31
- 文件名、路径、行号、内部 ID、JSON 字段、布尔值、字节数、错误码、Token/profile、域名切换和环境排障经过留在内部执行记录。用户明确询问技术细节时,只解释所问范围;业务修复或发布请求本身不代表用户要看实现报告。发布回读、健康检查和页面验收仍按各技能完成,结论由证据决定;无需把证据原文贴给用户。最终回复、卡片标题与描述、后续建议均按此规则检查,后续建议只写用户有必要进行的业务操作。
31
+ 文件路径、内部 ID、校验字段、认证信息和环境排障经过留在执行记录。业务修复或发布请求本身不代表用户要看实现报告;技术问题只解释所问范围。验收按执行位置和对应技能进行,用户总结提炼业务结果,不贴回执原文;卡片与后续建议同样执行本规则。
32
32
 
33
- 未完成时用一句话说明具体功能的影响和必要下一步。登录失效可说“页面暂未更新,请先重新登录宜搭”;目标组织或环境不一致时先确认正确目标,保持未完成状态,不自行改发到其他环境,也不将内部域名排查过程作为总结。仅完成发布回读时可说明“页面已发布”;需要报告尚未验收的功能时说“预约提交效果还未验证”,不把未验证写成成功,也不默认追加让用户刷新或排障的建议。
33
+ 未完成时说明功能影响与必要下一步。登录失效可说“页面暂未更新,请先重新登录宜搭”;组织或环境冲突先确认目标,不自行换环境。仅发布回读通过可说“页面已发布”,未验的功能不说成功,也不默认要求刷新或排障。
34
34
 
35
35
  已核实相应结果时,可采用以下表达:
36
36
 
@@ -42,4 +42,4 @@
42
42
  | 登录失效,无法发布 | 页面暂未更新,请先重新登录宜搭。 |
43
43
  | 发布完成,指定功能尚未验收 | 预约页面已发布,预约提交效果还未验证。 |
44
44
 
45
- 内部记录保存修改范围、发布回执、运行检查和必要诊断;用户总结依据这些记录提炼业务结果。发布成功只证明已发布,功能验证通过后才说功能已恢复;结果未知时先核实,无法核实时如实说明,不能用简短总结掩盖失败或扩大完成范围。
45
+ 云端按已完成的发布、配置和数据核对结果交付,页面未验状态留在内部。仅用户明确要求页面验收或存在影响使用的业务问题时,说明未确认的功能与必要操作;权限范围、审批人选等真实缺项照常说明。页面效果按实际证据表述,无需默认安排用户打开、刷新或截图补验。
@@ -37,8 +37,8 @@ Fast / Plan 已选主题是全应用决定,本步骤必须保存同一份应
37
37
 
38
38
  主题文件的职责与生效边界见 [应用主题 CSS 的职责](../../yida-design/workflow/output-design.md#应用主题-css-的职责),使用当前流程记录的实际 CSS 路径。
39
39
 
40
- `create-app` 只负责获取应用,创建阶段不提交 `colour`、`navTheme`、`layoutDirection` 等应用设置,统一等待主题 CSS 生成后同步更新。`update-app --theme-file` 上传 CSS 到主题资源接口,再调用 `updateApp` 保存 `colour=custom`、从 `--color-brand1-6` 提取的 `themeColor`、含资源 URL 的 `customThemeStyle` 和导航配置;不要额外传 `--theme-color`。用户明确只创建空壳、暂不配置主题,或旧主题未取得应用层注入确认时跳过这一步;保留平台主题并记录实际覆盖范围。
41
- 必须收到 `themeVerification.verified=true` 且 `customThemeStyle.enabled=true`、`cssUrl` 非空,才能确认应用设置已绑定主题资源。失败时先核对错误原因;注入确认缺失时停止上传并向用户说明范围,不能自动追加确认参数。其他错误使用同一个 `appType` 修复并重试 `update-app --theme-file`,不得重复创建应用。CSS 后续有修改时也必须重新执行该更新命令,仅修改本地文件不会更新应用设置。
40
+ `create-app` 只取得应用,不配置主题。CSS 生成后由 `update-app --theme-file` 上传并保存 `colour=custom`、CSS 主色、资源 URL 与导航;不另传 `--theme-color`。空壳请求、暂不换肤或旧主题未获注入确认时,保留平台主题并说明范围。
41
+ 按[主题上传与绑定](../../../references/execution-rules.md#主题上传与绑定)保存 `themeUpload` 与 `themeVerification` 回执。失败保留 appType 和已有资源,先只读核实;CSS 修改后重新上传,旧主题注入仍须用户确认。
42
42
  6. 已有 app 不自动改名。外部工具预创建 app 时,OpenYida 侧只复用 `appType`,但用户明确要求应用级换肤时可以执行上一步主题更新。
43
43
 
44
44
  ## 应用导航配置
@@ -2,6 +2,8 @@
2
2
 
3
3
  完成总结遵循[用户可见总结](../../../references/user-facing-summary.md):仅概述本轮业务变化和真实结果,简单修改一句即可;内部记录保留源码、命令和校验细节。
4
4
 
5
+ 页面检查先按[本地浏览器验收](../../../references/execution-rules.md#本地浏览器验收)确认执行位置。
6
+
5
7
  发布本轮修改过的页面源码到真实 display 页面,并执行轻量导航排序。平台导航排序必须等待本轮全部页面开发、发布和相关资源创建完成;逐页发布期间不触发平台排序,主页面先完成也不能提前触发排序。自定义菜单在页面开发时按已确认顺序实现,发布后统一验收。
6
8
 
7
9
  ## 输入
@@ -2,7 +2,9 @@
2
2
 
3
3
  最终输出让用户先理解业务能力,再看到按用途命名的真实应用入口。内部 ID、构建产物、中间文件和逐项业务资源不作为用户可见交付物。
4
4
 
5
- 交付话术遵循 [用户可见表达契约](../../yida-design/references/ask-human-interaction-contract.md)。本文件中的接口字段用于内部核验;对用户描述已验证的业务效果。例如,自定义导航完成配置和跨页跳转验证后写“已启用自定义导航,可在各业务页面间切换”。尚未验证的部分如实说明待验证的页面或操作。
5
+ 按[证据规则](../../../references/execution-rules.md#发布与验收证据)分别记录上传、配置回读与页面验收;浏览器检查仅限用户本机 Agent。
6
+
7
+ 交付话术遵循 [用户可见表达契约](../../yida-design/references/ask-human-interaction-contract.md)。本文件中的接口字段用于内部核验;对用户描述已验证的业务效果。例如,自定义导航完成配置和跨页跳转验证后写“已启用自定义导航,可在各业务页面间切换”。真实业务缺口及用户明确要求但未执行的验收须说明;云端未验状态留在内部。
6
8
 
7
9
  完成总结按[完成总结规则](../../../references/user-facing-summary.md)执行:默认 1–3 句,简单修改一句即可。最终回复、卡片描述和后续建议只表达业务变化、实际结果与必要下一步;内部校验记录、发布命令和环境排障过程保留在执行记录。CLI 指令仅在用户明确询问命令或用法时提供。
8
10
 
@@ -30,23 +32,23 @@
30
32
 
31
33
  1. 主页面发布成功;
32
34
  2. Canvas 主页面发布结果为 `publishMode=canvas`,读回 `hasYidaCodeCanvas=true` 且 `runtimeCodeBytes>0`;
33
- 3. 真实数据页面在已登录浏览器中退出 loading、无业务错误,并显示至少一条已 query 确认的记录;看板还必须有至少一个 KPI 数量或列表记录与只读 query 结果一致。已知记录数大于 0 但页面 KPI 全 0、列表为空或显示“暂无数据”时必须失败或标记 `needs_review`;
35
+ 3. 本机 Agent 验收真实数据页面时,核对页面退出 loading、无业务错误,并显示至少一条已 query 确认的记录;看板还必须有至少一个 KPI 数量或列表记录与只读 query 结果一致。已知记录数大于 0 但页面 KPI 全 0、列表为空或显示“暂无数据”时必须失败或标记 `needs_review`;
34
36
  4. 获得可访问 URL;
35
37
  5. 各入口的菜单顺序、默认页面和首屏任务已按规划落实;平台导航有排序回读,自定义导航有页面验证。未完成项按入口明确列出,不能仅因平台命令不适用就将排序记为完成;
36
38
  6. 新建或作为页面数据源的核心普通表单已写入 1-3 条真实示例记录并 query 抽查,或明确说明跳过原因;
37
- 7. 汇合此前并行执行的主题任务;旧主题或能力未知且未取得应用层注入确认时,按 [旧主题注入与停用规则](../../yida-design/references/application-style-library.md#旧主题应用的样式作用域与确认) 核验保留平台主题及页面局部样式范围,不将其判为主题上传失败。符合应用层注入条件时,确认 `themeVerification.verified=true`、`colour=custom`、`themeColor`、`navTheme`、`layoutDirection` 与实际保存协议对应的已确认设置一致,`customThemeStyle.cssUrl` 非空;这里是完成检查,不是首次更新主题的时机。应用主题文件已在应用级统一配置,自定义页面只在 `YidaComp` 内消费对应 token,未向上层注入或同步主题样式。
38
- 8. final 前先写入轻量 `prd/<项目名>/build-manifest.json`,再运行 `openyida check-prd-completeness prd/<项目名>/prd.md --app-type <appType> --build-manifest prd/<项目名>/build-manifest.json --json`;该命令检查页面/资源数量及已提供的素材执行记录一致性,不能替代第 3 条运行态数据验收。只有 `verdict=pass` 且运行态数据证据通过时才说“已按 PRD 完成搭建”;`verdict=needs_review` 时可以交付但必须列出 `items` 中 `status=needs_review/not_checked` 的复核项,`verdict=fail` 时列出 `hardFailures` 并说明未完成;
39
+ 7. 汇合主题任务,按[主题证据](../../../references/execution-rules.md#主题上传与绑定)保存上传、绑定、主色与导航回执到 `build-manifest.themeEvidence`。已有主题记录复用;旧主题或能力未知时按[旧主题规则](../../yida-design/references/application-style-library.md#旧主题应用的样式作用域与确认)保留平台主题及页面局部样式。主题更新在前序完成,收尾只汇合;自定义页面只在 `YidaComp` 内消费对应 token。
40
+ 8. final 前先写入轻量 `prd/<项目名>/build-manifest.json`,再运行 `openyida check-prd-completeness prd/<项目名>/prd.md --app-type <appType> --build-manifest prd/<项目名>/build-manifest.json --json`;该命令检查页面/资源数量及已提供的素材执行记录一致性,不能替代运行态验收。`verdict=pass` 后按证据规则描述已完成范围;只有运行态证据通过才宣称页面验收通过。`needs_review` 时说明真实业务复核项,`fail` 时说明 `hardFailures` 对应未完成项;云端页面未验不转为用户补验任务;
39
41
  9. 未继续执行用户未要求的公开访问、额外截图报告、报表、大屏、数据源深接或精细导航分组。
40
42
 
41
- 第 7 条的设置回读不代表视觉通过。按 [跨页面主题验收](../../yida-design/references/application-theme-consistency.md#验证主题是否真正一致) 检查实际存在的原生页、自定义页与抽屉/详情;没有浏览器证据时标记跨页面视觉待验证。不能以 `iframePropagation=false` 推断主题未加载,也不能以页面 fallback 正常或资源检查通过宣称全应用风格一致。
43
+ 适用的页面检查按[跨页验收](../../yida-design/references/application-theme-consistency.md#验证主题是否真正一致)核对原生页、自定义页与抽屉;视觉结果单独记录。
42
44
 
43
- PRD 结构化交接保留 `applicationTheme`,其中 themeColor 来自最终设计的 `--color-brand1-6`,navTheme 来自选定主题。Plan 自动生成,Fast 也记录相同字段;不能删去它绕过收尾检查。`check-prd-completeness` 默认对声明的期望读取当前应用设置,不信任旧的主题保存回执;默认蓝色、品牌不符、未启用 CSS 或缺少资源等不匹配返回 fail,无法读取为 needs_review。只检查配置,仍需分别打开原生导航、原生业务页与自定义页;页面固定色盘不能作为主题加载证据。资源局部修改不追加全应用换肤任务。
45
+ PRD 保留 `applicationTheme`:themeColor 取最终 `--color-brand1-6`,navTheme 取选定主题;Plan 自动生成,Fast 同样记录。`check-prd-completeness` 读取当前配置;品牌、CSS 启用或资源不符为 fail,无法读取为 needs_review。局部修改保持原换肤范围。
44
46
 
45
- 前后台场景在最终登记前分别打开两个真实业务入口:前台只出现页面内自定义导航,后台保留原生导航及管理任务,默认页不是前台首页;前台专用页面在 PC/移动端管理菜单已隐藏。将真实资源 ID 写入 build-manifest 后,完整性检查会核验 PRD `navigationVisibility`,仍可见为 fail、配置或 ID 未知为 needs_review;没有该字段的历史 PRD 不自动获得已验证结论,应按入口用途补齐清单或单独回读。
47
+ 本地验收前后台入口:前台仅页面内导航,后台保留原生导航、管理任务及正确默认页;前台专用页在后台 PC/移动菜单隐藏。`build-manifest` 写入真实资源 ID 后,完整性检查核验 PRD `navigationVisibility`:仍可见为 fail,配置或 ID 未知为 needs_review。历史 PRD 缺字段不算通过,须补清单或回读。
46
48
 
47
- 两端以及实际使用的原生表单、列表、详情和抽屉均按 [阅读无障碍门禁](../../yida-design/references/page-quality-gates.md#阅读无障碍门禁) 核对文字、背景、字号行高、选中/悬停/错误态、窄屏与放大后的阅读路径。低对比材质不等于低对比文字;浅金、小字、透明度和图片上的文字按最终叠加背景检查。`check-design` 检查 token 配色,不能替代局部 CSS 和运行画面。用户不需要截图报告时仍应只读打开页面验收;明确禁止浏览器操作时遵从并标记未验证,不能自行把“不主动截图”解释为免验。
49
+ 页面验收覆盖[阅读无障碍门禁](../../yida-design/references/page-quality-gates.md#阅读无障碍门禁)中的适用页面、抽屉、窄屏及放大态。`check-design` 检查 token 配色,运行画面另验;未执行的检查按证据规则记录待验。
48
50
 
49
- 用户明确禁止的动作优先于上述默认条件:禁止 `theme-file` 时,第 7 条改为核验本轮没有生成、复制、修改或上传主题文件,并如实说明沿用现有/平台默认主题,不把它判为主题任务失败;禁止 `publish` 时不得宣称完整应用已发布或把历史 URL 当成本轮发布产物,只交付已完成的未发布范围和后续发布条件;禁止 `page-source` 时不得宣称本轮修改了页面源码,也不得用编译或现有页面状态替代源码变更证据。跳过项必须来自用户明确约束,不能由模型自行缩减范围。
51
+ 用户明确禁止的动作优先于上述默认条件:禁止 `theme-file` 时,第 7 条改为核验本轮没有生成、复制、修改或上传主题文件,并如实说明沿用现有/平台默认主题,不把它判为主题任务失败;禁止 `publish` 时不得宣称完整应用已发布或把历史 URL 当成本轮发布产物,只交付已完成的未发布范围和后续发布条件;禁止 `page-source` 时不得宣称本轮修改了页面源码,也不得用编译或现有页面状态替代源码变更证据。写操作的跳过必须来自用户明确约束;浏览器检查先按执行位置判定,仅用户本机 Agent 可执行;未验不能记为通过或省略接口回读。
50
52
 
51
53
  第 6 条按资源独立核验:每个被 final 声称“已写入”“已验证”或给出记录数的表单/流程,都必须有该资源自己的成功 create 返回值和只读 query/readback 证据。不得把一张表单的 3 条记录复制成其他表单也有 3 条;没有证据的资源只能写“未写入”“未抽查”或“跳过”,不能用应用发布成功替代数据核验。
52
54
 
@@ -69,7 +71,7 @@ PRD 结构化交接保留 `applicationTheme`,其中 themeColor 来自最终设
69
71
  | 范围内入口尚未验证、登记失败或回读不一致 | 标记该端未完成,修复后再交付;不输出猜测或错位链接,不传空字符串、`null`、占位 URL |
70
72
 
71
73
  - 按入口规划的 `sceneKey`、`defaultMenuKey`、真实页面 ID 和视图建立前后台映射,不按页面创建/发布顺序或页面标题猜测。业务后台优先使用已验证的具体任务 `/workbench/{formUuid}?viewUuid=...`;只有确认根 `/workbench` 落到正确默认任务时才使用根入口。
72
- - 已有配置与本轮已验证目标一致时直接复用回读,不重复保存;不同且在本轮完整搭建范围内时局部更新。登记后以 `urls` 为最终入口事实源,保持完整域名、页面 ID、有效 query/hash 和公开地址。`verification.configurationReadback=true` 只证明配置保存,`runtimeVerified=false` 仍需实际打开验证,不能当访问成功。
74
+ - 已有配置与本轮已验证目标一致时直接复用回读,不重复保存;不同且在本轮完整搭建范围内时局部更新。登记后以 `urls` 为最终入口事实源,保持完整域名、页面 ID、有效 query/hash 和公开地址。`verification.configurationReadback=true` 只证明配置保存,`runtimeVerified=false` 不能当访问成功。页面验收遵循本机 Agent 边界。
73
75
  - 用户明确保留平台导航的前台,使用已验证的 `workbenchUrl` 通过 `--frontend` 登记;`--frontend-page` 专用于独立前台,不适用这种例外。
74
76
  - 前台地址必须来自已发布页面的成功返回及回读;独立前台核验持久化 `renderNav=false`,仅字段缺失时兼容 `isRenderNav`,不通过 URL 参数配置。
75
77
  - **登记 URL 必须是发布返回并回读确认的那条完整访问地址,原样传入 `app-entry set`**:直接复用 `publish` 成功返回的完整运行时 URL,并结合 `get-form-config` 核对页面配置和实际访问结果,保留其域名、路径段(如 `/workbench/{formUuid}` 或 `/custom/{formUuid}`)和有效参数。不得手工改写、截断或凭 `appType`/`formUuid` 拼接一条未经发布回读的路径来登记;保存后回读核对同一条完整 URL。完整 URL 参数不会截断工作台页面 ID。
@@ -194,7 +196,7 @@ PRD 结构化交接保留 `applicationTheme`,其中 themeColor 来自最终设
194
196
  | 数据桥深度接入 | `use_skill("yida-canvas-data-binding", "接入页面数据")` 或 `use_skill("yida-data-source-connectors", "绑定设计器数据源")` | 页面真实数据读写稳定,空态/错误态可恢复 |
195
197
  | 报表/图表 | `use_skill("yida-report", "创建原生报表")`、`use_skill("yida-rechart", "创建 Recharts 页面")` 或 `use_skill("yida-chart", "创建 ECharts 页面")` | 报表或图表页面已创建/发布 |
196
198
  | 公开访问 | `use_skill("yida-page-config", "配置页面公开访问")` | 分享配置保存成功 |
197
- | 截图/人工验收 | 按当前工具能力 | 截图或用户确认通过 |
199
+ | 截图/人工验收 | 本机 Agent 或用户明确要求的人工验收 | 有实际截图或用户确认 |
198
200
 
199
201
  ## 错误处理
200
202
 
@@ -222,6 +222,6 @@ openyida get-schema <appType> <formUuid> --field-map-json
222
222
  | [data-bridge-guide.md](references/data-bridge-guide.md) | 表单、平台连接器与自定义同源接口的数据桥 | 接入真实数据时阅读 |
223
223
  | [canvas-theme-provider.md](references/canvas-theme-provider.md) | 统一主题接入、示例装配、预览与发布 | 编写或调整 antd 页面时必读 |
224
224
  | [canvas-style-implementation-guide.md](references/canvas-style-implementation-guide.md) | 将 `design.md` 的 App 主题色、antd token、背景层、卡片边界、圆角密度、控件焦点/下拉 reset、图表配色落到 `YidaCodeCanvas` 组件 | MUST:新建页面或调整视觉前完整读取,见顶部编码前必读 |
225
- | [waf-safe-authoring.md](references/waf-safe-authoring.md) | 接口加白前规避平台安全(WAF)误拦的临时写法:不写正则字面量、危险 HTML/脚本串、注入特征串,及二分法定位 | 仅当用户反馈发布反复失败、疑似 WAF 拦截时阅读;常规生成不套用 |
225
+ | [waf-safe-authoring.md](references/waf-safe-authoring.md) | 按原始回执诊断发布异常,保留正常 JSX 事件写法 | 发布失败或需要解释原因时阅读,不预设 WAF |
226
226
  | [component-library-guide.md](references/component-library-guide.md) | 组件库推荐组合和页面选型建议 | 选择 UI/图表依赖时阅读 |
227
227
  | [canvas-authoring-examples.md](references/canvas-authoring-examples.md) | 最小组件、hooks、副作用、图表示例 | 手写 `.canvas.jsx` 代码时阅读 |
@@ -50,23 +50,35 @@
50
50
 
51
51
  `dataBinding.mode=form` 默认调用 `window.__OPENYIDA_YIDA_API__.searchFormDatas(params)`。同一个桥还会暴露 `saveFormData`、`updateFormData`、`deleteFormData`、`startProcessInstance`、`updateProcessInstance`、`getProcessInstances`、`getProcessInstanceIds`、`getProcessInstanceById`、`request`、`searchUserList` 等 `this.utils.yida` 方法,并运行时枚举已有的额外函数;流程列表、流程详情或发起审批时优先使用这些桥接方法。根级 `toast`、`dialog`、`router.push`、`openPage`、`isMobile` 等工具通过 `window.__OPENYIDA_UTILS__` 访问,`window.__OPENYIDA_UTILS__.yida` 等于同一个 yida API 桥。表单查询参数至少包含 `formUuid`、`currentPage`、`pageSize` 和 `searchFieldJson`,`pageSize` 一般显式写 `50`;`appType` 可保留在 `dataBinding` 中用于校验和构造详情/提交链接。只有桥不存在时才同源直连 `/dingtalk/web/<appType>/v1/form/searchFormDatas.json`;直连 query 至少包含 `formUuid`、`appType`、`currentPage`、`pageSize`、`searchFieldJson` 和 `_csrf_token`,并设置 `credentials: 'include'` 与 `global_csrf_token` 头。`/query/form/searchFormDatas.json` 不是可用表单数据端点。
52
52
 
53
+ ## 同源窗口与身份
54
+
55
+ 桥先取当前窗口,再沿同源父链查找;每次单独读取并捕获异常,命中即返回。跨域、不可访问或不透明源返回未就绪。同源要求协议、主机和端口相同;`parentWindow` 参数不保证指向原生父窗口。
56
+
57
+ 身份仅取当前页面 `window.loginUser` 或本页 utils 的 `getLoginUserId()`,缺失显示未就绪;父窗口及 CLI 身份不作兜底。
58
+
53
59
  ## 可复用读数据 Hook
54
60
 
55
61
  ```jsx
56
62
  import React, { useCallback, useEffect, useRef, useState } from 'react';
57
63
 
64
+ function getCanvasBridge(key, method) {
65
+ var target = window;
66
+ while (target) {
67
+ try {
68
+ if (target !== window && (!window.location.origin || window.location.origin === 'null'
69
+ || target.location.origin !== window.location.origin)) return null;
70
+ var bridge = target[key];
71
+ if (bridge && typeof bridge[method] === 'function') return bridge;
72
+ var parent = target.parent;
73
+ if (!parent || parent === target) return null;
74
+ target = parent;
75
+ } catch (err) { return null; }
76
+ }
77
+ return null;
78
+ }
79
+
58
80
  function getYidaApiBridge() {
59
- var candidates = [];
60
- try { candidates.push(window.__OPENYIDA_YIDA_API__); } catch (err) {}
61
- try { candidates.push(window.parent && window.parent.__OPENYIDA_YIDA_API__); } catch (err) {}
62
- try {
63
- if (typeof parentWindow !== 'undefined') {
64
- candidates.push(parentWindow.__OPENYIDA_YIDA_API__);
65
- }
66
- } catch (err) {}
67
- return candidates.find(function (item) {
68
- return item && typeof item.searchFormDatas === 'function';
69
- }) || null;
81
+ return getCanvasBridge('__OPENYIDA_YIDA_API__', 'searchFormDatas');
70
82
  }
71
83
 
72
84
  function getCsrfToken() {
@@ -235,18 +247,9 @@ function normalizeFormRow(row) {
235
247
  ## 在组件里调用平台连接器
236
248
 
237
249
  ```jsx
250
+ // 复用上方同源桥 helper。
238
251
  function getConnectorBridge() {
239
- var candidates = [];
240
- try { candidates.push(window.__OPENYIDA_CONNECTOR_API__); } catch (err) {}
241
- try { candidates.push(window.parent && window.parent.__OPENYIDA_CONNECTOR_API__); } catch (err) {}
242
- try {
243
- if (typeof parentWindow !== 'undefined') {
244
- candidates.push(parentWindow.__OPENYIDA_CONNECTOR_API__);
245
- }
246
- } catch (err) {}
247
- return candidates.find(function (item) {
248
- return item && typeof item.invoke === 'function';
249
- }) || null;
252
+ return getCanvasBridge('__OPENYIDA_CONNECTOR_API__', 'invoke');
250
253
  }
251
254
 
252
255
  var CONNECTOR_BINDING = {
@@ -1,45 +1,22 @@
1
- # WAF 安全误拦规避写法
1
+ # 发布异常:先核实证据
2
2
 
3
- > **仅在用户反馈发布反复失败、且疑似被平台安全(WAF)拦截时才启用本文档。**
4
- > 只用于已确认的 WAF 误拦排查;正常生成 Canvas 页面不套用这些规则。
3
+ 按本次 CLI 回执区分编译、业务保存与页面运行错误,修复已定位的问题。
5
4
 
6
- ## 何时判定为 WAF 拦截
5
+ ## 正常事件写法
7
6
 
8
- 发布返回 **HTTP 200,但响应体不是 JSON**,且 body 中含 `waf_block` 字样,即为安全拦截(并非业务报错)。表现为「发布一直失败」但登录态、参数、源码语法都正常。
7
+ 保留 React/JSX 的 `onClick={handleClick}`、`onClick={() => handleClick()}` 和 `onChange={handleChange}`。事件属性本身不是拦截证据,属性展开或编码不作为排障方案;正常 JSX 误拦交由平台修正规则或发布通道。
9
8
 
10
- ## 定位命中片段
9
+ ## 证据与处理
11
10
 
12
- 在用户授权的测试页面保留完整源码副本,用二分法定位被拦截的源码片段;不得在交付页面发布删减内容。未获得测试目标时保留错误证据并停止:
11
+ | 本次回执 | 下一步 |
12
+ | --- | --- |
13
+ | 超时、HTTP 非 JSON、403 或空响应 | 记录原因未确认,先只读核对保存结果 |
14
+ | 明确的 `waf_block` 或安全拦截码 | 保留提示、时间、接口、状态和可用请求 ID,由平台核对规则;具体命中内容须有日志或稳定对照支持 |
15
+ | 平台日志或授权测试明确定位问题 | 按已确认的因果解释和修复;一次换写法成功只记录为对照结果 |
16
+ | 发布回读通过,页面显示或交互异常 | 检查运行时异常、依赖、导出和数据状态;页面检查遵循本地验收规则 |
13
17
 
14
- 1. 注释/删除页面源码的一半,重发 `openyida publish`。
15
- 2. 若仍被拦截,命中片段在保留的一半;若通过,命中片段在删掉的一半。
16
- 3. 继续对半缩小范围,直到定位到触发拦截的具体写法。
17
- 4. 按下方规则替换该写法后,恢复其余代码并重新发布。
18
+ HTTP 工具可能只返回 `__httpStatus`、`__nonJsonResponse` 和摘要。保留实际拿到的证据,其余信息记未知。结果未知先读回;确需复现时使用授权的独立测试目标,每次只改一个因素并保留完整源码和对照。
18
19
 
19
- ## 规避写法
20
+ ## 对用户的表达
20
21
 
21
- ### 1. 不写正则字面量
22
-
23
- 日期/字符串解析改用非正则方式:`String.split()`、`slice()`、`Number()`、`Date` 等。
24
-
25
- 确需正则时用 `new RegExp('...')` 动态构造,避免形如 `\d{4}` 这类字面量直接出现在源码里。
26
-
27
- - 命中示例(避免):直接在源码里写 `/\d{4}-\d{2}-\d{2}/` 之类的正则字面量。
28
- - 安全写法:`new RegExp('\\d{4}')`,或直接用 `str.split('-')` 取段、`Number(x)`、`new Date(str)` 解析。
29
-
30
- ### 2. 不出现危险 HTML / 脚本字符串字面量
31
-
32
- 源码中避免出现下列字符串字面量:`<script`、``eval(` ``、`innerHTML`、`javascript:`、`onerror=`、`<iframe` 等。
33
-
34
- 事件处理写成 JSX 的 `onClick={fn}`,不要拼成 HTML 属性字符串(如把 `onclick="..."` 拼进字符串再注入)。
35
-
36
- ### 3. 不出现注入特征串
37
-
38
- 源码中不要出现 `union select`、`or 1=1`、`../`、`$(...)`、`/etc/passwd` 等注入特征串,即使只是常量、注释或示例数据。
39
-
40
- ## 复发时的处理顺序
41
-
42
- 1. 确认是 WAF 拦截(HTTP 200 + 非 JSON + `waf_block`),排除登录态/参数/语法问题。
43
- 2. 二分法定位命中片段。
44
- 3. 按上述规则替换写法。
45
- 4. 恢复完整源码,重新发布并回读验收。
22
+ 按[完成总结](../../../references/user-facing-summary.md)说明业务影响与必要操作。用户问原因且证据不足时,可说“发布返回异常,具体原因尚未确认”;已定位时说明实际原因及处理结果。
@@ -146,25 +146,11 @@ function unwrapTotal(payload, rows) {
146
146
 
147
147
  ## yida JS-API 桥
148
148
 
149
- 发布使用 `YidaCodeCanvas` 组件实现的页面时,必须在外层页面 `actions.module.source` 中注入桥接脚本,并把根节点 `componentDidMount` 指向 `didMount`。桥接脚本把普通页面运行态可用的 `this.utils.yida.*` 封装到 `window.__OPENYIDA_YIDA_API__` 和 `window.openyidaYidaApi`,并把根级 `this.utils.*` 方法封装到 `window.__OPENYIDA_UTILS__` 和 `window.openyidaUtils`,供组件内部通过 `window.parent` / `parentWindow` 查找。
149
+ Canvas 发布层在外层 `actions.module.source` 注册桥,根节点 `componentDidMount` 指向 `didMount`:`this.utils.yida` 对应 `window.__OPENYIDA_YIDA_API__`,根级 utils 对应 `window.__OPENYIDA_UTILS__`。只查同源窗口;身份只取当前页面。
150
150
 
151
- 页面内固定使用下面的读取顺序:
151
+ 先复制[同源桥 helper](../yida-canvas-custom-page/references/data-bridge-guide.md#同源窗口与身份)中的 `getCanvasBridge/getYidaApiBridge`,再读取数据:
152
152
 
153
153
  ```javascript
154
- function getYidaApiBridge() {
155
- var candidates = [];
156
- try { candidates.push(window.__OPENYIDA_YIDA_API__); } catch (err) {}
157
- try { candidates.push(window.parent && window.parent.__OPENYIDA_YIDA_API__); } catch (err) {}
158
- try {
159
- if (typeof parentWindow !== 'undefined') {
160
- candidates.push(parentWindow.__OPENYIDA_YIDA_API__);
161
- }
162
- } catch (err) {}
163
- return candidates.find(function (item) {
164
- return item && typeof item.searchFormDatas === 'function';
165
- }) || null;
166
- }
167
-
168
154
  async function fetchFormRows(binding, signal) {
169
155
  var bridge = getYidaApiBridge();
170
156
  var payload;
@@ -183,7 +169,7 @@ async function fetchFormRows(binding, signal) {
183
169
  var rows = unwrapRows(payload);
184
170
  var totalCount = unwrapTotal(payload, rows);
185
171
  if (totalCount > 0 && rows.length === 0) {
186
- throw new Error('表单返回 totalCount > 0,但页面没有解析到行数据,请检查返回体包裹层和字段映射。');
172
+ throw new Error('表单有记录但未解析到行,请检查响应结构和字段映射。');
187
173
  }
188
174
  return { rows: rows, totalCount: totalCount, raw: payload };
189
175
  }
@@ -47,7 +47,7 @@ description: 创建宜搭应用并返回 appType;仅当没有目标 app 且用
47
47
  ```bash
48
48
  openyida create-app --name <appName> [--desc <description>]
49
49
  # 提取返回的 appType 后,单独更新应用基础设置
50
- openyida update-app <appType> --theme-file <app-theme.css> --nav-theme light --logo-source appIcon --layout <side|top|l_shape>
50
+ openyida update-app <appType> --theme-file <app-theme.css> --nav-theme <light|dark> --logo-source appIcon --layout <side|top|l_shape>
51
51
  ```
52
52
 
53
53
  `openyida create-app` 不支持 `--json` 参数;不要添加 `--json`。创建成功时命令本身会输出一行 JSON,从该输出中提取 `appType`。
@@ -97,11 +97,13 @@ openyida create-app "客户管理" --quiet --no-open
97
97
 
98
98
  创建命令只提交名称、描述、图标和必要的创建标记,不推断或提交应用主题与导航配置。创建完整应用、使用 PRD/design.md 或用户要求配置主题时,主题更新命令默认传入主题文件;只有用户明确只创建空壳或暂不配置主题时才跳过主题更新。搭建流程必须采用先 `create-app`、再 `update-app --theme-file` 的两个步骤。`create-app` 不接受 `--theme-file` 和 `--logo-source`,也不接受 `--colour`、`--theme`、`--nav-theme`、`--layout` 或旧位置参数里的主题与布局,不隐式执行应用设置更新。
99
99
 
100
- CLI 始终按“显式 `--icon` → 行业推断 → 随机系统图标”的顺序选择图标,只有未显式指定且未命中行业时才随机,与后续是否更新主题文件无关。主题文件生成后按 PRD 在 `update-app --layout` 中显式设置布局;用户未指定布局时按业务场景选择,不固定使用 `l_shape`。参数映射与保存后核对遵守 [平台导航参数](../yida-app/workflow/step-3-create-or-reuse-app.md#平台导航参数)。在 `update-app --theme-file` 步骤中校验 CSS 并将图标颜色统一为 `--color-brand1-6` 转换后的 HEX;导航配置沿用 PRD,普通浅色方案显式传 `--nav-theme light --logo-source appIcon`。
100
+ CLI 始终按“显式 `--icon` → 行业推断 → 随机系统图标”的顺序选择图标,只有未显式指定且未命中行业时才随机,与后续是否更新主题文件无关。主题文件生成后按 PRD 在 `update-app --layout` 中显式设置布局;用户未指定布局时按业务场景选择,不固定使用 `l_shape`。参数映射与保存后核对遵守 [平台导航参数](../yida-app/workflow/step-3-create-or-reuse-app.md#平台导航参数)。在 `update-app --theme-file` 步骤中校验 CSS 并将图标颜色统一为 `--color-brand1-6` 转换后的 HEX;`--nav-theme` 显式取 design.md 的 `themeProfile.navTheme`,不按页面明暗推断导航;Logo 配置沿用 PRD。
101
101
 
102
102
  **应用主题(colour)口径**:
103
103
 
104
- 默认不要把黑色、深灰或灰黑中性色作为普通应用主题色。创建业务系统、工作台、门户、数据管理类应用时,先根据行业、品牌、业务情绪和视觉目标做创意色彩判断;`podBlue`、`podGreen`、`podOrange` 只是常用浅底候选,不是固定默认,也不是行业刻板答案。`black` 仅在用户明确要求暗色模式、高对比、奢侈品牌或极简黑色视觉时使用,`greyBlue` 也只在工业制造、技术工程等稳重场景下作为 fallback。
104
+ 默认不要把黑色、深灰或灰黑中性色作为普通应用主题色。`black` 仅在用户明确要求暗色模式、高对比、奢侈品牌或极简黑色视觉时使用。
105
+
106
+ 主色遵从用户最新指定的颜色、风格及已选主题;品牌化差异通过项目 token 成组落实。未指定部分按实际任务、品牌证据、阅读条件和状态语义分析;CRM、库存等业务名称不绑定颜色。预置 key 和模板示例均不构成默认色,也不按行业限制蓝、绿或中性色。
105
107
 
106
108
  主题颜色不受平台预置 key 限制。先执行以下命令复制内置主题模板,再定点修改品牌相关 token:
107
109
 
@@ -114,14 +116,14 @@ openyida sample yida-design app-theme --output .cache/openyida/<项目名>/app-t
114
116
  ```bash
115
117
  openyida create-app --name "<应用名>" --desc "<描述>"
116
118
  # 从创建结果提取 appType 后执行;以下为 PRD 选择平台顶部导航的示例
117
- openyida update-app <appType> --theme-file <app-theme.css> --nav-theme light --logo-source appIcon --layout top --show-app-nav
119
+ openyida update-app <appType> --theme-file <app-theme.css> --nav-theme <light|dark> --logo-source appIcon --layout top --show-app-nav
118
120
  ```
119
121
 
120
- CLI 会先校验主题文件完整声明平台实际生成的 `--color-brand1-1/2/3/5/6/9/10`,并允许不存在 `--color-brand1-4/7/8`。`update-app --theme-file` 上传 CSS,再调用应用基础设置的 `updateApp` 接口联合保存从 `--color-brand1-6` 提取的 `themeColor`、`customThemeStyle`、`navTheme`、`logoSource` 和 `layoutDirection`。更新主题时,系统应用图标会同步保存为 `iconName%%主题色HEX`;外链或上传图片图标保持原值。
122
+ CLI 校验主题文件完整声明平台实际生成的 `--color-brand1-1/2/3/5/6/9/10`,允许缺少 4/7/8。`update-app --theme-file` 联合保存 CSS 资源、主色及导航设置;系统应用图标会同步保存为 `iconName%%主题色HEX`,图片图标保持原值。
121
123
 
122
124
  `colour` 只保存平台 key,实际色值保存在 `themeColor`。导入主题文件时自动使用 `colour=custom`,无需手动填写;显式 `--colour custom` 需要主题文件或有效主题色,已有有效主题色可沿用。平台预置 key 不能与自定义 CSS 或 `--theme-color` 同传;切换预置主题会清空旧自定义 CSS。
123
125
 
124
- 更新结果必须包含 `themeVerification.verified=true` 和非空的 `customThemeStyle.cssUrl`,证明主题资源已绑定到应用设置;仅有本地 CSS 或创建成功不代表主题已应用。回读失败时保留已有 appType,修复后重试 `update-app --theme-file`,不要重复创建应用。修改 CSS 后也必须重新上传保存。
126
+ 按[主题上传与绑定](../../references/execution-rules.md#主题上传与绑定)保存本轮上传与绑定证据;失败沿用当前 appType 和资源,先只读核实。CSS 变更后重新上传。
125
127
 
126
128
  完整应用主题 key、颜色倾向和 token 变量统一维护在 `yida-design/references/theme/theme-token-presets.md`,本技能不重复维护完整清单。
127
129
 
@@ -14,7 +14,7 @@ description: >
14
14
 
15
15
  已有资源的 UI 调整和美化后恢复,先进入 [ui-improve](sub_skill/ui-improve/SKILL.md),再按其确认、备份和业务保留约束使用下方设计规则。
16
16
 
17
- 先读已有需求、设计和应用主题,确定哪些地方需要调整。优先使用平台已有的主题变量,只在表达不了所需效果时新增变量,并写清哪些组件使用它;见 [主题扩展规则](references/application-theme-consistency.md#平台基础变量是应用主题基础框架)。
17
+ 先按[用户意图与已选主题](references/theme-selection.md#已选风格只确认一次)落实主题、风格和颜色;模板示例的绿、蓝配色不作为默认推荐。只为未明确部分结合真实任务补设计。优先使用已有主题变量,表达不了时再扩展并注明消费位置,见 [主题扩展规则](references/application-theme-consistency.md#平台基础变量是应用主题基础框架)。
18
18
 
19
19
  导航默认沿用平台样式,像连续目录一样排列。只调整需要改变的颜色、尺寸和状态;边框、阴影可不设置,不要把每个菜单项做成输入框、按钮或卡片。改完后打开实际的数据管理页和业务页面检查,见 [导航形状与密度的案例经验](references/application-theme-consistency.md#导航形状与密度的案例经验)。
20
20
 
@@ -34,7 +34,7 @@ openyida sample yida-design application-style --style-id free-creative --output
34
34
 
35
35
  用户要求“去掉应用中的 appCustomTheme / customThemeStyle / 应用级自定义 CSS”时,按宜搭界面关闭“启用主题换肤”的方式执行 `openyida update-app <appType> --disable-custom-theme`。先读取最新基础设置,解析原 customThemeStyle,保留 cssUrl、cssFileName 和其他资源字段,将 enabled 与 iframePropagation 改为布尔值 false,再 JSON.stringify 后以字符串提交到该应用的 query/app/updateApp.json;沿用登录态与 CSRF,其他基础设置取最新回读并保持原值。不能清空 URL,不能传空字符串、null、空对象/数组或只传 {"enabled":false};原配置缺失或无法解析时停止写入并说明原因。该参数不与其他更新字段同传,不删除 CDN 文件,也不修改页面源码。
36
36
 
37
- 保存后核对 themeVerification.verified=true,确认 enabled/iframePropagation 为布尔值 false,URL 与文件名仍为原值。随后刷新真实访问页,确认没有 link#yida-app-custom-theme,且网络请求不再加载原主题 CSS;仅 success=true 或配置回读通过不代表访问态已验证。页面检查失败时报告已提交的非认证字段和实际回读结果,隐藏 Token、Cookie、CSRF 等认证信息。结果描述为“自定义主题 CSS 已停用,资源配置仍保留”;没有浏览器证据时补充“访问态效果待验证”。
37
+ 保存后核对 themeVerification.verified=true,确认 enabled/iframePropagation 为布尔值 false,URL 与文件名仍为原值。页面检查按[本地浏览器验收](../../../references/execution-rules.md#本地浏览器验收)核验没有 link#yida-app-custom-theme,且网络请求不再加载原主题 CSS;仅 success=true 或配置回读通过不代表访问态已验证。页面检查失败时报告已提交的非认证字段和实际回读结果,隐藏 Token、Cookie、CSRF 等认证信息。结果描述为“自定义主题 CSS 已停用,资源配置仍保留”;页面未验留在内部,用户明确要求的验收未完成时说明。
38
38
 
39
39
  ## 命令与参数
40
40
 
@@ -86,6 +86,8 @@ Plan 的 `visualStyle.tokens` 必须显式提供:画布 `--pod-page-bg-color`
86
86
 
87
87
  ## 验收与维护
88
88
 
89
+ 按[证据规则](../../../references/execution-rules.md#发布与验收证据)分别记录上传、配置与适用的页面验收结果。
90
+
89
91
  看应用整体,覆盖导航、自定义页、提交、编辑、详情、数据管理内嵌、抽屉和移动端,核对正文/底栏对齐、背景连续、hover/focus、禁用/错误和窄屏布局。应用主题与表单组件树共同作为验收基线。命名主题同时对照原模板与获批方案,检查浏览器实际生效的图片、圆角、阴影、分割线及导航展开/折叠;不能仅凭 token、CSS 文件存在或上传成功判定通过。
90
92
 
91
93
  每个主题目录的 `design.md` 与 `app_theme.css` 配对维护:设计文档记录 token 与视觉约束,配对 CSS 保留实际选择器规则。导航、应用框架、自定义页面、表单和详情的颜色、边框、hover、圆角、阴影、字体与密度在同一文件中定义。`form-layout.json` 维护表单结构,`app_theme.css` 的既有专属选择器是维护来源;也可在同目录 `theme-rules.css` 独立维护新增规则,构建时合入独立样式块;不另建导航配色库或应用风格配置覆盖它们。修改设计后运行 `node scripts/build-application-styles.js`,脚本保留现有配对 CSS 原文,不因设计 token 与 CSS 不同就覆盖声明;仅缺少配对 CSS 的新模板使用公共骨架。脚本不改写设计正文、Token、表单结构或索引。`npm run check:themes`(即 `node scripts/build-application-styles.js --check`)检查索引内配对 CSS 的同步及静态配色问题,已接入 `check:quick` 和 CI。仓库维护基线记录已确认模板的源码摘要和已知审计结果;原样模板的已知风险继续报告,新增风险、对比度恶化或风险模板源码变化仍阻断,不能自动更新基线消除失败。`node scripts/build-application-styles.js --check --strict-palette` 不使用基线,完整报告并阻断所有配色风险及未解析底面。基线仅用于仓库维护,不放宽项目设计或生成时的配色校验,也不代表浏览器验收通过。新增或恢复模板时添加三文件目录和索引项;未登记模板可保留在目录中,但不参与选型、导出、Plan 生成和默认 CSS 同步检查。已有 Plan 若引用已移出索引的主题,需要恢复该索引记录后才能继续物化;已生成的项目设计和 CSS 可继续使用。随后运行模板、Plan/Fast、原生布局测试及 `check:skills`。该脚本仅用于仓库维护,应用搭建使用上述 CLI。
@@ -202,12 +202,12 @@ antd 控件按 Canvas 技能接入 `CanvasThemeProvider`,由适配层读取当
202
202
 
203
203
  ## 验证主题是否真正一致
204
204
 
205
- 完整应用的 PRD 结构化交接记录 `applicationTheme: {themeColor, navTheme, designFile}`,是从最终设计派生的交付期望,不写回 build-plan。Plan materialize 自动生成;Fast 用最终 design.md 中可解析的 `--color-brand1-6` 与明确的导航明暗记录相同字段。收尾 `check-prd-completeness` 会读取当前应用设置,核验 `colour=custom`、主色相符、自定义 CSS 已启用且存在资源 URL、导航明暗相符;不一致为 fail,读取或期望未知为 needs_review。该结果的 `runtimeVerified=false` 明确保留,不能拿配置通过代替浏览器的跨页面视觉验收。历史 PRD 未声明此字段不代表通过主题检查。
205
+ PRD 的 `applicationTheme: {themeColor, navTheme, designFile}` 来自最终设计,不写回 build-plan。Plan 自动生成,Fast 记录同一字段。`check-prd-completeness` 核对当前 colour=custom、主色、自定义 CSS 启用及 URL、导航明暗;不符为 fail,未知为 needs_review。`runtimeVerified=false` 保留;历史 PRD 缺字段不代表通过。
206
206
 
207
207
  1. **设计与文件**:逐项对照已确认的导航、框架、主色、页面底、面板、文字、字体及特色强调,确认它们出现在生成的主题 token 中,页面用的是同一角色。导航明暗与模板或自由创意决策一致,导航状态、形状与密度保留本项目设计。文件名叫 `app_theme.css` 或 `app-theme.css` 不影响契约,以本次输出路径为准。
208
- 2. **绑定**:`themeVerification.verified=true` 仅证明应用设置绑定了主题资源。`iframePropagation` 是配置字段,不是当前页面主题加载的检测结果;不能凭它为 `false` 就断言 Canvas 收不到变量,更不能据此改成固定色盘。
209
- 3. **运行态**:检查实际导航及其默认、悬停和选中状态,并对照应用框架、原生列表、提交/编辑表单、记录详情、自定义页及实际存在的抽屉。逐类检查实际存在的页面,核对主题资源、容器计算变量、背景、文字、形状、间距和主操作;iframe 单独检查其文档。变量缺失查资源加载与作用域,变量正确但外观不对查页面硬编码与样式覆盖。
210
- 4. **交付**:原生页共享其组件支持的配色、字体、圆角等主题能力;杂志封面、分栏与特色版式只在有相应布局实现的页面出现。没有运行态证据时明确标记“主题已绑定,跨页面视觉待验证”,不能用编译成功、资源齐全或 fallback 看起来正确代替验收。
208
+ 2. **上传与绑定**:按[证据规则](../../../references/execution-rules.md#主题上传与绑定)核对本轮上传哈希、目标应用、资源地址和绑定回读;复用已有主题时记录复用。
209
+ 3. **运行态**:仅本地按[浏览器规则](../../../references/execution-rules.md#本地浏览器验收)逐类核对已有导航三态、框架、原生列表/表单/详情、自定义页和抽屉的资源、计算变量与外观;iframe 独立检查。变量缺失查加载和作用域,变量正确查硬编码及覆盖。远端或本地能力不足时标记待验证。
210
+ 4. **交付**:原生页共享其组件支持的配色、字体和圆角;特色版式由具体页面实现。按[完成总结](../../../references/user-facing-summary.md)概述已验证的效果,页面未验状态按执行位置记录。
211
211
 
212
212
  移动端 `--color-brand-1`、`--color-brand-2`、`--color-brand-3`、`--color-brand-4` 由 CLI 按应用色阶生成桥接;最终 CSS 也必须在顶层 `:root` 完整提供这四项。生成后和上传前同时校验 PC 七档与移动端四档的缺失、无效颜色及引用循环,不要求在 design.md 重复声明自动桥接。
213
213
 
@@ -18,7 +18,7 @@ Fast、Plan 和单页设计共用 [主题索引](../templates/design-themes/inde
18
18
 
19
19
  用户明确要求比较多种风格时可以提供候选;“你来定、按业务选”是委托 AI 决定,不再反问。业务字段、审批与范围的必要澄清可以继续,不能夹带已明确的风格题。Plan 的整体方案确认和已有应用美化的实施确认仍按原流程执行。
20
20
 
21
- - 用户选中主题、自由创意或推荐项后,选择即完成。配色、Token 或页面布局未完成时,由 AI 补齐,不重新询问风格。
21
+ - 用户选中主题、自由创意或推荐项后,按该方向的配色、材质和组件规则落地;未明确部分由 AI 补齐,不重新选型。用户最新明确的风格、颜色要求优先,主题与指定颜色并存时保留主题结构,在项目 token 中协调颜色角色;确有不兼容项只澄清冲突。
22
22
  - 先保存 `visualSelection.themeId` 和 `visualDirection.source`:选项回答记为 `user_selected`,直接指定记为 `user_explicit`。Plan 沿用到 `visualStyle.internal.selectedTheme`;补设计细节时保留原选择来源。
23
23
  - 自然语言意向先写入 `visualDirection.label/description`,`source=user_explicit`;AI 根据约束匹配合法 `themeId`,无合适模板时用 `free-creative`。参考材料中实际提取的视觉事实使用 `material_extracted`,AI 补齐的配色等细节标记 `ai_default`,不伪造用户选项回答。
24
24
  - 名称、编号或“用推荐的”按当时的候选映射确定 ID。文件缺失时从真实历史回答恢复;映射不清时只澄清具体歧义,不重发整套候选。较早交互的迟到回传不能覆盖用户后来的选择。
@@ -30,8 +30,9 @@ Fast、Plan 和单页设计共用 [主题索引](../templates/design-themes/inde
30
30
  先读取已确认需求、现有应用主题和视觉证据;页面内容、导航类型、数据、字段、流程和权限沿用业务规划。视觉证据按以下顺序处理:
31
31
 
32
32
  ```text
33
- 用户明确视觉要求
34
- > 品牌规范、Logo、官网、参考图和已有设计
33
+ 用户最新明确的主题、风格和颜色要求
34
+ > 已确认主题与已有应用设计
35
+ > 品牌规范、Logo、官网和参考图
35
36
  > 业务场景、使用频率、信息密度和主要终端
36
37
  > AI 补齐
37
38
  ```
@@ -42,7 +43,7 @@ Fast、Plan 和单页设计共用 [主题索引](../templates/design-themes/inde
42
43
 
43
44
  先判断用户每天做什么,再判断行业气质。高频查阅、比较与处理任务,首先比较数字扫描、筛选与批量操作、状态辨认以及原生表格和表单的连续使用体验。行业与对象名称不等于用户要求奶油、咖啡、复古或大面积暖黄。
44
45
 
45
- 没有明确品牌或整体配色要求时,优先用低饱和近白画布、清楚的内容面、中性正文和局部品牌强调建立层次;主色仍由项目决定,不固定为蓝色。命名模板的纸纹、黄底、金棕导航或大面积彩色表面必须有任务或视觉证据支持,不能因“更有表现力”自动胜出。用户明确要求这些风格时予以保留,并保证文字、状态和表格可读。
46
+ 没有明确视觉约束时,用近白画布、清楚的内容面、中性正文和局部品牌强调建立层次。默认不要把黑色、深灰或灰黑中性色作为普通应用主题色;`black` 仅在用户明确要求暗色模式、高对比、奢侈品牌或极简黑色视觉时使用。CRM 的跟进、比较与状态辨认,库存的盘点、出入库与异常定位,用于确定层级、密度和颜色用途,不对应固定绿、蓝配色。主色结合本项目品牌证据、阅读环境及状态区分确定;模板示例色、数量和排列不作为推荐依据。纸纹、暖黄或大面积彩色表面按真实任务或用户要求采用,并保证可读。
46
47
 
47
48
  比较方向时,`recommendationReason` 说明它如何支持当前高频任务;`visualDirection.description` 说明导航、内容和主操作的层次,`colorStrategy.usage` 区分大面积背景、中性文字和局部强调。沿用现有字段,不另造评分体系。通用示例的颜色、主题排列顺序和预览色都不是默认推荐。
48
49
 
@@ -102,7 +103,7 @@ Fast 与 Plan 使用下面同一套三方向生成规则和候选字段,不得
102
103
  跨环境生成只读预览页:
103
104
 
104
105
  ```bash
105
- openyida sample yida-design theme-preview --theme-ids app-sage,app-teal,app-terminal,app-platinum --output .cache/theme-preview.html --json
106
+ openyida sample yida-design theme-preview --theme-ids <本轮实际候选ID,逗号分隔> --output .cache/theme-preview.html --json
106
107
  ```
107
108
 
108
109
  `--theme-ids` 按用户要求的顺序传入可用主题 ID,总数不限。包含波普时还须根据真实依据传 `--pop-art-context explicit-request`(明确指定波普)或 `--pop-art-context showcase`(主要任务偏展示);无依据时排除波普。CLI 返回预览路径与编号映射;拒绝未知、重复 ID 和覆盖已有文件,需要更新时使用新的预览路径。生成结果含 `attachment`,HTML 已包含静态卡片和模板原图,禁用脚本的预览也可显示。需选型时将它与问题一次交给宿主,见[交互附件示例](ask-human-interaction-contract.md#风格预览与选择同步)。只看效果时仅展示卡片。预览不写入用户选择。
@@ -113,7 +114,7 @@ openyida sample yida-design theme-preview --theme-ids app-sage,app-teal,app-term
113
114
 
114
115
  1. 识别最先想到的完整方案及其惯性来源,例如“销售驾驶舱 → 蓝色商务卡片”。将该方案排除,不进入后续候选、推荐或最终交付。
115
116
  2. 在排除后重新生成上述三套方向(两个命名模板方向和一个自由创意方向)。各方向相对被排除方案,在构图、字体层级、表面层次、色彩关系中至少两项有实质差异;不得仅改名称、微调色值或调换候选顺序后重新引入原方案。
116
- 3. 缺少品牌或颜色约束时,至少有一个候选采用不同色相或中性主导关系,不能三套都是同一蓝色底稿只换名称;用户已明确主色时保留该主色,用其他维度形成差异。排除的是完整默认方案,不将某个颜色或主题 ID 永久列为禁用。
117
+ 3. 仅在没有品牌或颜色约束、也没有已有选择时,候选至少覆盖两种色相或中性主导关系,避免绿系或蓝系扎堆;各方向说明与实际任务的关系,不为凑差异随机换色。用户已明确主色时保留该主色,用其他维度形成差异;已有主题直接复用,不触发此比较。颜色与主题 ID 均不永久禁用。
117
118
  4. 对照数字扫描、目标与实际比较、异常辨认、长期阅读及平台页面连续性,从新方向中选出推荐项,再确定展示顺序。沿用 `recommendationReason` 和 `colorStrategy.usage` 写清业务取舍,不得仅写“专业、科技、适合销售”。
118
119
 
119
120
  例如销售经营看板,拒绝直接套用最先想到的蓝色商务卡片方案,按日常经营复盘、目标差距追踪或投屏监控任务重新设计层级、密度和强调方式。不能为了拒绝第一直觉随机换色,也不能损害可读性和业务功能。
@@ -4,7 +4,6 @@
4
4
  * OpenYida 应用主题模板
5
5
  *
6
6
  * 生成后的 CSS 是应用级主题资源:平台组件和自定义页面消费这里的 token 与样式。
7
- * 文件名可以是 app-theme.css 或 app_theme.css,以 --theme-file 的实际路径为准。
8
7
  * 修改后需通过 update-app --theme-file 上传并绑定应用设置;本地文件存在不代表已生效。
9
8
  * 布局、导航交互和 iframe 高度仍由页面/容器实现,iframe 不自动继承父文档变量。
10
9
  *
@@ -19,10 +18,8 @@
19
18
  * PC 七档与移动端四档必须完整存在于顶层 :root;生成与上传均校验最终值、引用和循环,局部声明不能充数。
20
19
  * 5. 按 design.md 的 Token 名更新颜色、边框、状态、圆角与阴影;显式 var() 引用才建立联动。
21
20
  * 导航、表单、详情和自定义页面共同设计;辅助色、材质色与状态色不因换主色自动重染。
22
- * 6. 页面、卡片、抽屉和控件通过变量引用继承项目圆角,不强制所有主题使用大圆角。
23
- * 7. 固定主题只声明一套变量;:root 与平台各导航模式选择器共用此声明块。
24
- * is-light/is-dark 和 nav-* 只作为覆盖入口,不生成另一套配色。
25
- * CLI 在生成前后和上传前检查 CSS 结构闭合,失败时修复源文件后重试。
21
+ * 6. 圆角按角色继承,不统一改大。
22
+ * 7. 各导航模式共用一套配色;生成及上传前检查 CSS 闭合,失败先修源码。
26
23
  */
27
24
 
28
25
  /* openyida-theme-tokens:start */
@@ -132,8 +129,8 @@
132
129
  --pod-shell-content-container-padding: 0px;
133
130
  --pod-shell-content-container-margin: 0px;
134
131
  --pod-shell-content-container-border: none;
135
- --pod-shell-content-container-bg-color: transparent;
136
- --pod-shell-theme-bg-color: #FFFFFF;
132
+ --pod-shell-content-container-bg-color: var(--pod-page-bg-color);
133
+ --pod-shell-theme-bg-color: #171717;
137
134
 
138
135
  /* 页面容器 */
139
136
  --pod-page-surface-padding: 0px;
@@ -277,9 +274,9 @@
277
274
  --pod-nav-item-text-color: #A0A0A0;
278
275
  --pod-nav-item-text-hover-color: #E7E7E7;
279
276
  --pod-nav-item-text-disabled-color: rgba(24, 28, 31, 0.24);
280
- --pod-page-header-bg-color: var(--pod-shell-theme-bg-color, var(--color-white, #fff));
277
+ --pod-page-header-bg-color: var(--pod-page-bg-color);
281
278
  --pod-theme-text-color: var(--pod-nav-item-text-color, var(--color-text1-4, #202020));
282
- --pod-page-header-text-color: var(--pod-theme-text-color, var(--color-text1-4, #202020));
279
+ --pod-page-header-text-color: var(--color-text1-4);
283
280
  --pod-nav-item-text-selected-color: #FFFFFF;
284
281
 
285
282
  /* 原生表单:编辑态字段 */
@@ -765,3 +762,7 @@ body.pod-premium .flow-steps .next-step-dot
765
762
  }
766
763
 
767
764
  /* openyida-navigation-shape:end */
765
+
766
+ .pod-premium :is(.next-shell-content, .yida-data-manage-drawer, .drawer-container) {
767
+ --pod-shell-theme-bg-color: var(--pod-page-bg-color);
768
+ }
@@ -14,9 +14,12 @@ tokens:
14
14
  "--pod-card-border": "1px solid var(--color-line1-1)"
15
15
  "--pod-table-cell-color": "var(--pod-card-bg-color)" # 表格正文跟随卡片表面
16
16
  "--pod-shell-padding": "0px"
17
+ "--pod-shell-content-container-bg-color": "var(--pod-page-bg-color)"
17
18
  navigation:
18
19
  "--pod-nav-sub-divider-color": "#353535"
19
- "--pod-shell-theme-bg-color": "#FFFFFF"
20
+ "--pod-shell-theme-bg-color": "#171717"
21
+ "--pod-page-header-bg-color": "var(--pod-page-bg-color)"
22
+ "--pod-page-header-text-color": "var(--color-text1-4)"
20
23
  "--pod-nav-item-text-color": "#A0A0A0"
21
24
  "--pod-nav-item-text-hover-color": "#E7E7E7"
22
25
  "--pod-nav-item-text-selected-color": "#FFFFFF"
@@ -178,7 +181,7 @@ tokens:
178
181
 
179
182
  ### 2.2 应用导航
180
183
 
181
- 白色 Shell 与白色内容画布承接平台深色侧栏。侧栏容器沿用平台原生布局与外形,不叠加背景图片、外描边、16px 大圆角或 overflow 裁剪。菜单保留深灰 hover 与 selected 底、浅色文字,navTheme 继续为 dark。白色 Shell 不代表侧栏改成浅色导航。
184
+ navTheme 为 dark,侧栏壳使用深炭底与浅字,搜索和菜单三态沿用深灰层次;内容、页头及详情抽屉独立保持白面与深字。侧栏沿用原生布局与外形。
182
185
 
183
186
  Shell 外留白消费 `--pod-shell-padding`,展开侧栏四边内距消费 `--pod-nav-slide-aside-padding`。这两项保留工作区间距,不为侧栏构造额外的浮岛背景或外轮廓;导航无需图片素材。
184
187
 
@@ -337,7 +340,7 @@ Shell 外留白消费 `--pod-shell-padding`,展开侧栏四边内距消费 `--
337
340
 
338
341
  YAML 是变量值的唯一事实源;正文只规定消费关系,独立局部参数在出现处标为推断。全局变量供平台原生页面与自定义页面消费;页面层补充主题色深色对象信息、图表色、认证标志、自绘品牌区和独立指标排版。递归提取以 `--` 开头的标量叶子,分组名称不拼接到 CSS 变量名;平台基础与项目扩展均在主题中声明,可无环引用,并按实际设计补充变量。
339
342
 
340
- 导航配色与平台侧栏布局成组确定,白色 Shell 不改变正文与深色菜单的职责。原生导航额外通过 `--pod-nav-menu-item-selected-shadow` 使用不占位的选中标记;自绘导航才消费页面层品牌区色。若平台组件不开放细节样式,保留其原生交互与布局能力,在开放范围内落实表面、边界和文字层级。
343
+ 导航深色底与浅字成组配置,内容区独立保持白底深字。原生导航额外通过 `--pod-nav-menu-item-selected-shadow` 使用不占位的选中标记;自绘导航才消费页面层品牌区色。若平台组件不开放细节样式,保留其原生交互与布局能力,在开放范围内落实表面、边界和文字层级。
341
344
 
342
345
  需求决定内容,平台约束限定可实现能力,主题规定视觉机制。特色配方不能覆盖真实数据语义、自然内容增长或明确平台限制。未提供具体名称的平台状态变量按语义对接,不杜撰变量接口。
343
346
 
@@ -139,7 +139,7 @@ Fast 写入 `design.md`;Plan 写入 `visualStyle.forUser.iconSystem.colorPairs
139
139
 
140
140
  ## 用户配色与模板的优先级
141
141
 
142
- 用户确认的整体氛围高于模板默认灰阶。根据品牌和已确认方向协调页面、卡片、导航、填充、边界和交互,同时保留文字可读性和独立状态语义;不能只改按钮,也不能统一抹掉主题原有层次与材质。
142
+ 用户最新明确的风格、颜色要求优先;没有新要求时保留已选主题的配色与材质,模板示例色不替代用户选择。指定颜色写入项目 token,并协调页面、卡片、导航和交互;保留主题结构、文字可读性与独立状态语义,不只改按钮或统一染色。
143
143
 
144
144
  Fast 与 Plan 使用同一主题的颜色推导、组件规则和页面设计标准。只计算模板中已有的主色公式,固定值与变量引用按原设计保留;项目差异直接写入 token,不强制其他色阶跟随主色。Plan 输入使用 `visualStyle.tokens`,Fast 写入 `design.md.tokens`,最终设计相同。导航差异 token(未声明项沿用平台绑定) 沿用主题模板,并合入明确的项目差异;`navigationStyle.tone` 表示导航明暗,具体外观由这组 token 表达。导航明暗与内容画布分别设计。用户明确保留中性参考或只改强调色时尊重该范围。`--pod-nav-menu-item-selected-shadow` 作为方向无关的基础外观 Token 独立透传,`none` 表示关闭;CLI 不根据导航明暗生成替代配色。
145
145
 
@@ -18,9 +18,9 @@
18
18
 
19
19
  | 优先级 | themeColorSource | 触发条件 | 输出规则 |
20
20
  | --- | --- | --- | --- |
21
- | 1 | `user-specified` | 用户明确给出色值、品牌色、主题 key 或换肤要求 | 原样记录用户意图;命中平台 key 才允许传 `--theme`,任意色值写 token |
21
+ | 1 | `user-specified` | 用户已选主题,或明确风格、颜色及换肤要求 | 保留所选主题并落实最新颜色要求;平台 key 才传 `--theme`,任意色值写 token |
22
22
  | 2 | `application-theme` | 已有应用或工作区中能读到当前 `theme`、`colour`、`themeColor` 或 `navTheme` | 沿用应用整体配色和已定义的组件颜色角色;缺少角色时结合当前背景补齐,不要求所有强调都用主色 |
23
- | 3 | `business-inferred` | 无明确主题证据,需要根据行业、品牌气质、业务情绪和视觉目标推导 | 设计任意合法的自定义品牌色盘,写应用主题 token 和文件交付方案 |
23
+ | 3 | `business-inferred` | 无明确视觉约束或可复用主题,按真实任务、阅读环境和状态区分推导 | 写清颜色用途与业务依据,形成项目色盘;不因 CRM、库存等名称默认选绿或蓝 |
24
24
 
25
25
 
26
26
  1. 先判断主要任务、使用频率、信息密度与终端,再结合品牌证据和视觉目标判断业务气质。高频管理按[场景适配判断](../references/theme-selection.md#高频管理场景的适配判断)设计;行业名词不能直接决定配色、材质或布局。
@@ -65,7 +65,7 @@
65
65
 
66
66
  ## 写颜色角色
67
67
 
68
- - 主色:先按行业、品牌、业务情绪和视觉目标做创意判断,可选择平台预置主题,也可设计自定义品牌色盘;不得固定为 `podBlue` / #1677ff,不得套用行业刻板配色。
68
+ - 主色:沿用用户选定主题及最新风格、颜色要求;无约束时结合品牌证据、真实任务、阅读环境和状态色设计,记录用途;模板中的绿、蓝示例不作为默认答案。
69
69
  - 辅助色:按整体搭配用于内容分组、图表系列、指标或操作强调;明确用途和面积,状态提示仍沿用语义色。
70
70
  - 中性色:背景、文字、边框、分割线,默认保持浅底业务风。
71
71
  - 语义色:成功、警告、错误、信息保持稳定,不随意改成品牌色。
@@ -14,10 +14,14 @@ Canvas 的 `dataBinding.mode=connector` 使用稳定资源身份:
14
14
  }
15
15
  ```
16
16
 
17
+ 复用[同源桥 helper](../../yida-canvas-custom-page/references/data-bridge-guide.md#同源窗口与身份);unavailable 显示未就绪状态。
18
+
17
19
  ```javascript
18
- var bridge = window.__OPENYIDA_CONNECTOR_API__
19
- || (window.parent && window.parent.__OPENYIDA_CONNECTOR_API__);
20
- var result = await bridge.invoke(DATA_BINDING, DATA_BINDING.inputs);
20
+ async function callConnector(inputs) {
21
+ var bridge = getCanvasBridge('__OPENYIDA_CONNECTOR_API__', 'invoke');
22
+ if (!bridge) return { status: 'unavailable' };
23
+ return { status: 'ready', data: await bridge.invoke(DATA_BINDING, inputs) };
24
+ }
21
25
  ```
22
26
 
23
27
  数字 `connectorId`(例如 `917319`)只用于 `openyida connector detail/list-actions/test` 等管理命令。页面调用网关时必须使用 `connector create/detail --json` 回读的内部 `connectorName`,其值以 `Http_` 开头;发布层会把它映射为 `serviceInfo.connectorInfo.connectorId`。不得把数字 ID 写入页面 binding。
@@ -18,7 +18,7 @@ description: 宜搭登录态管理。以 OpenYida auth snapshot 为准;默认
18
18
 
19
19
  ## 千问办公网页态的后台登录
20
20
 
21
- 当前快照为 `runtime.tool=qwenwork`、`runtime.subtype=qwenwork_web` 且 `interactive_login.mode=cloud_token` 时,按[云端后台免登规则](../../references/qwenwork-cloud-login.md)执行快照推荐命令。CLI 自动消费沙箱凭据;成功后继续业务,失败不回退浏览器 OAuth,不读取或展示凭据。桌面态继续使用桌面换票。
21
+ 当前快照为 `runtime.tool=qwenwork`、`runtime.subtype=qwenwork_web` 且 `interactive_login.mode=cloud_token` 时,按[云端免登与 OAuth 回退规则](../../references/qwenwork-cloud-login.md)执行快照推荐命令并等待;需要授权时按 `oauth_fallback` 打开 CLI 输出的 URL。成功后继续业务,不读取或展示凭据。桌面态继续使用桌面换票。
22
22
 
23
23
  ## 千问办公桌面端的后台登录
24
24
 
@@ -46,7 +46,7 @@ openyida login --check-only --json
46
46
  | 状态 | 动作 |
47
47
  |---|---|
48
48
  | `auth_mode=token` 且 `status=ok` 或 `can_auto_use=true` | 继续执行业务命令 |
49
- | `interactive_login.mode=cloud_token` 且未登录 | 静默执行快照推荐的 `openyida login --qwenwork --json`;CLI 自动消费云端凭据,成功后继续业务;失败停止,不打开浏览器或回退 OAuth |
49
+ | `interactive_login.mode=cloud_token` 且未登录 | 执行推荐的 `openyida login --qwenwork --json` 并等待;免登认证失败由同一进程回退 OAuth,按 `oauth_fallback` 打开授权 URL;成功后继续业务 |
50
50
  | `status=profile_required` 且返回 `candidates` / `next_step` | 不猜组织;先执行 `openyida auth profiles`,再用 `openyida auth profile switch <auth_profile>` 切到用户确认或目标匹配的已有 profile |
51
51
  | `auth_source=env` / `failure_reason=env_token_missing` | 进入运行环境注入 token 模式;缺 token 时停止,让 Codex、yida-agent 等宿主注入 `OPENYIDA_ACCESS_TOKEN` 或 `OPENYIDA_REFRESH_TOKEN`;不要执行 OAuth |
52
52
  | `auth_mode=token`,未登录,且 `interactive_login.mode=cli_auto_open` | 只执行一次 `openyida login`,等待该命令结束,并使用其最终 JSON 判断结果 |
@@ -130,7 +130,7 @@ openyida list-forms <appType> --keyword <页面名>
130
130
  以下命令和字段供 Agent 判断真实完成状态,保留在内部执行记录。最终回复、交付卡片和后续建议统一遵循[完成总结](../../references/user-facing-summary.md),无需展示校验字段或执行过程。
131
131
 
132
132
  - Canvas 发布完成的内部判定:成功执行 `openyida publish <source> <appType> <displayPageFormUuid> --canvas --health-check`,且结果包含 `publishMode=canvas`、`publishReadbackVerified=true`、`healthCheck.ok=true`、`healthCheck.readback.hasYidaCodeCanvas=true` 和 `runtimeCodeBytes>0`。对用户按本轮业务改动表述,例如“已将工作台的‘待清洁房间’改为‘待清洁’,并更新页面”。
133
- - `--health-check` 是发布内容读回校验,不是浏览器运行态 smoke。输出中的 `runtimeSmokeVerified=false`、`runtimeSmokeStatus=not_checked` 表示本命令没有验证页面渲染与交互;需要宣称“页面可运行”时,必须另有专项运行态证据。
133
+ - `--health-check` 是发布内容读回校验,不是浏览器运行态 smoke。输出中的 `runtimeSmokeVerified=false`、`runtimeSmokeStatus=not_checked` 表示本命令没有验证页面渲染与交互;需要宣称“页面可运行”时,必须另有专项运行态证据。页面检查按[本地浏览器验收](../../references/execution-rules.md#本地浏览器验收)执行。
134
134
  - `<source>` 必须是本轮实际 Write/Edit/Create 过的页面源码;`<displayPageFormUuid>` 必须是已解析的 display 自定义页面。发布了其他文件或其他目标页面,不满足本轮源码修改的 doneWhen。
135
135
  - 若 publish 没执行、执行失败、目标不明、登录态/组织不一致或用户要求先暂停,保持未发布状态。已完成修改时可说“已完成修改,待发布”;确实需要用户操作时,用一句话说明影响和必要下一步。CLI 指令仅在用户明确询问命令或用法时提供。
136
136
  - 平台 JSX 组件页面的 `check-page` / `compile`、Canvas 页面统一入口 `openyida compile <源文件> --json` 都是发布前 guard,不是远端完成证据。
@@ -187,6 +187,8 @@ body { background-color: #f2f3f5; }
187
187
 
188
188
  ## 异常处理
189
189
 
190
+ 保留正常 JSX 事件绑定(如 `onClick={fn}`);发布失败按[诊断规则](../yida-canvas-custom-page/references/waf-safe-authoring.md)核对回执,修复已定位的问题。
191
+
190
192
  | 异常场景 | 处理方式 |
191
193
  |---------|----------|
192
194
  | 源码构建或发布前校验失败 | 回到对应页面开发技能修复源码格式、语法或运行时兼容问题,再重新执行 `openyida publish` |
@@ -196,7 +198,7 @@ body { background-color: #f2f3f5; }
196
198
  | 发布后页面空白 | `YidaCodeCanvas` 页面检查 `YidaComp` 是否正确导出和依赖是否可加载;平台 JSX 组件页面检查 `renderJsx` 是否正确导出;同时查看浏览器控制台报错 |
197
199
  | 发布接口成功但页面坏了 | 先用 `get-schema` 只读核对远端 Schema 与本轮发布回执,不为检查结果重复写入;首屏渲染、控制台报错和体验问题仍结合浏览器验证 |
198
200
  | 发布后功能异常 | `YidaCodeCanvas` 页面优先查依赖白名单、`YidaComp` 导出、hooks 副作用清理;平台 JSX 组件页面检查 `forceUpdate is not a function` 等常见错误,参考 `yida-custom-page` 平台 JSX 组件页面规范 |
199
- | 用户反馈发布反复失败、疑似安全(WAF)拦截 | 仅当发布返回 HTTP 200 但响应体非 JSON 且 body 含 `waf_block` 时判定为拦截;按 `yida-canvas-custom-page` 的 [waf-safe-authoring.md](../yida-canvas-custom-page/references/waf-safe-authoring.md) 用二分法定位命中片段并替换写法后重发。非常规场景不启用 |
201
+ | 发布超时、非 JSON 或网关提示 | 保留回执并先只读核实保存结果,按上方诊断规则定位原因;证据不足时记录原因未确认 |
200
202
 
201
203
  ## 错误恢复
202
204