@appaflytech/zeyrek-ai-mcp 0.0.28 → 0.0.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.
- package/.env.example +15 -0
- package/README.md +5 -5
- package/dist/config.js +37 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/access.js +802 -0
- package/dist/knowledge/backendConstraints.js +18 -1
- package/dist/knowledge/case.js +41 -0
- package/dist/knowledge/componentCatalog.js +494 -34
- package/dist/knowledge/componentEvents.js +132 -7
- package/dist/knowledge/connectDataBind.js +819 -0
- package/dist/knowledge/connectDataDefinition.js +341 -0
- package/dist/knowledge/connectDataExecute.js +93 -0
- package/dist/knowledge/connectDataRules.js +128 -0
- package/dist/knowledge/decision.js +121 -0
- package/dist/knowledge/expressionPrelude.js +59 -0
- package/dist/knowledge/layoutSummary.js +140 -2
- package/dist/knowledge/microserviceParameters.js +58 -0
- package/dist/knowledge/mining.js +94 -0
- package/dist/knowledge/mobileComponentCatalog.js +859 -0
- package/dist/knowledge/mobileComponentEvents.js +239 -0
- package/dist/knowledge/normalize.js +236 -67
- package/dist/knowledge/outlines.js +36 -0
- package/dist/knowledge/platform/lint.js +385 -0
- package/dist/knowledge/platform/mobile.js +142 -0
- package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
- package/dist/knowledge/platform/mobileRules.js +231 -0
- package/dist/knowledge/platform/mobileSpacing.js +82 -0
- package/dist/knowledge/platform/mobileTabs.js +108 -0
- package/dist/knowledge/platform/nativeBoxModel.js +44 -0
- package/dist/knowledge/platform/profile.js +25 -0
- package/dist/knowledge/platform/resolve.js +122 -0
- package/dist/knowledge/platform/web.js +39 -0
- package/dist/knowledge/policy.js +18 -4
- package/dist/knowledge/process.js +510 -0
- package/dist/knowledge/reservedTables.js +79 -0
- package/dist/knowledge/rowFilter.js +43 -0
- package/dist/knowledge/rules.js +319 -21
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +50 -4
- package/dist/knowledge/styleResolveNative.js +527 -0
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +6 -3
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/actions.js +333 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/{screenshot.js → capture/capture.js} +105 -519
- package/dist/platform/capture/console-log.js +122 -0
- package/dist/platform/capture/index.js +11 -0
- package/dist/platform/capture/readiness.js +103 -0
- package/dist/platform/capture/summary.js +77 -0
- package/dist/platform/capture/target.js +5 -0
- package/dist/platform/capture/targets/studio-mobile.js +15 -0
- package/dist/platform/capture/targets/studio-web.js +5 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +162 -10
- package/dist/platform/http.js +29 -8
- package/dist/platform/impersonation.js +45 -0
- package/dist/platform/jsonArray.js +24 -0
- package/dist/platform/responses.js +39 -5
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +17 -1
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
- package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
- package/dist/resources/examples/bpm-satinalma.json +376 -0
- package/dist/resources/examples/connect-data-convert-verify.json +107 -0
- package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
- package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
- package/dist/resources/examples/connect-data-query-params.json +50 -0
- package/dist/resources/examples/connect-data-table-entity.json +147 -0
- package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
- package/dist/resources/examples/mobile-app-shell-drawer.json +243 -0
- package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
- package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
- package/dist/resources/examples/mobile-form-screen.json +365 -0
- package/dist/resources/examples/mobile-list-detail-form.json +672 -0
- package/dist/resources/examples/page-detail-profile.json +2 -1
- package/dist/resources/examples/page-dynamic-form.json +3 -2
- package/dist/resources/examples/page-flex-sections.json +1 -1
- package/dist/resources/examples/page-html-embed.json +1 -1
- package/dist/resources/examples/workflow-basic.json +8 -2
- package/dist/resources/guides/access.md +796 -0
- package/dist/resources/guides/bpm.md +686 -0
- package/dist/resources/guides/case.md +153 -0
- package/dist/resources/guides/component.md +91 -4
- package/dist/resources/guides/decision.md +185 -0
- package/dist/resources/guides/entity.md +50 -8
- package/dist/resources/guides/expressions.md +14 -5
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +38 -1
- package/dist/resources/guides/layout.md +11 -7
- package/dist/resources/guides/microservice.md +81 -2
- package/dist/resources/guides/middleware.md +44 -2
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile.md +495 -0
- package/dist/resources/guides/page.md +454 -75
- package/dist/resources/guides/theme.md +8 -2
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +58 -1
- package/dist/resources/read.js +41 -0
- package/dist/resources/rules.js +11 -5
- package/dist/resources/schema-fingerprint.json +7 -5
- package/dist/resources/schemas/AccessExplanationDto.json +144 -0
- package/dist/resources/schemas/AccessMatrixDto.json +170 -0
- package/dist/resources/schemas/AccessPermissionDto.json +57 -0
- package/dist/resources/schemas/AccessPolicyDto.json +223 -0
- package/dist/resources/schemas/AccessResourceDto.json +86 -0
- package/dist/resources/schemas/AppSessionDto.json +53 -0
- package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
- package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
- package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
- package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
- package/dist/resources/schemas/CreateEntityCommand.json +118 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
- package/dist/resources/schemas/CreateQueryCommand.json +4 -0
- package/dist/resources/schemas/CreateWorkflowCommand.json +143 -1
- package/dist/resources/schemas/DataJobDto.json +6 -1
- package/dist/resources/schemas/DecisionDto.json +302 -0
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
- package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +21 -1
- package/dist/resources/schemas/MicroserviceDto.json +15 -3
- package/dist/resources/schemas/OrganizationProjectDto.json +335 -0
- package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +22 -1
- package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
- package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
- package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
- package/dist/resources/schemas/StepDto.json +22 -1
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
- package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
- package/dist/resources/schemas/UpdateViewCommand.json +0 -2
- package/dist/resources/schemas/UpdateWorkflowCommand.json +143 -1
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
- package/dist/server.js +27 -1
- package/dist/tools/access.js +369 -2
- package/dist/tools/accessOperations.js +135 -0
- package/dist/tools/accessPolicies.js +170 -0
- package/dist/tools/accessRuntime.js +203 -0
- package/dist/tools/applications.js +60 -3
- package/dist/tools/bpm.js +343 -0
- package/dist/tools/bpmGovernance.js +266 -0
- package/dist/tools/bpmOperations.js +230 -0
- package/dist/tools/bpmReporting.js +154 -0
- package/dist/tools/bpmRouting.js +150 -0
- package/dist/tools/bpmRuntime.js +396 -0
- package/dist/tools/case.js +178 -0
- package/dist/tools/caseRuntime.js +89 -0
- package/dist/tools/common.js +7 -0
- package/dist/tools/components.js +118 -14
- package/dist/tools/connectData.js +449 -0
- package/dist/tools/connectors.js +193 -0
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/decisions.js +206 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +2 -2
- package/dist/tools/designSchemaPatch.js +34 -9
- package/dist/tools/dtos.js +36 -5
- package/dist/tools/entities.js +79 -12
- package/dist/tools/externalComponents.js +26 -5
- package/dist/tools/flows.js +122 -14
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +5 -2
- package/dist/tools/layouts.js +50 -17
- package/dist/tools/merges.js +1 -1
- package/dist/tools/microservices.js +77 -25
- package/dist/tools/mobile.js +221 -0
- package/dist/tools/modules.js +48 -0
- package/dist/tools/organizationStructure.js +137 -0
- package/dist/tools/pages.js +465 -98
- package/dist/tools/platformInfo.js +27 -8
- package/dist/tools/profileWrite.js +92 -0
- package/dist/tools/projects.js +71 -9
- package/dist/tools/rows.js +69 -13
- package/dist/tools/schemaOps.js +61 -1
- package/dist/tools/screenWrite.js +37 -8
- package/dist/tools/screens.js +95 -5
- package/dist/tools/templates.js +26 -1
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +11 -5
- package/dist/tools/workflows.js +10 -5
- package/package.json +2 -2
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { IMAGE_SOURCE_PROPS, IMAGE_SOURCE_PROP_NAMES, MOBILE_RULES, REDIRECT_PRESENTATIONS, fileIdImageExpression, missingComponentAdvice, styleReplacement } from './mobileRules.js';
|
|
2
|
+
import { findingLine } from './profile.js';
|
|
3
|
+
import { SURFACE_KEYS } from '../styleResolveNative.js';
|
|
4
|
+
import { DOCUMENT_STYLE_TYPES } from '../styleResolve.js';
|
|
5
|
+
import { listThemeVarNames } from '../themeResolve.js';
|
|
6
|
+
import { closestArgument } from '../../platform/argumentAliases.js';
|
|
7
|
+
function asRecord(value) {
|
|
8
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
9
|
+
}
|
|
10
|
+
function walk(value, visit) {
|
|
11
|
+
if (!Array.isArray(value)) {
|
|
12
|
+
const single = asRecord(value);
|
|
13
|
+
if (single)
|
|
14
|
+
walk([single], visit);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
for (const entry of value) {
|
|
18
|
+
const node = asRecord(entry);
|
|
19
|
+
if (!node)
|
|
20
|
+
continue;
|
|
21
|
+
visit(node);
|
|
22
|
+
walk(node.children, visit);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Koda ya da çeviriye bağlı prop: değeri çalışma anında `ref`'ten gelir, `data` yalnız tasarım yedeğidir.
|
|
27
|
+
* Belge CSS'i alan tipte (`DOCUMENT_STYLE_TYPES`) bağlı `style` metin sayılır — şema onu uzun metin ilan eder.
|
|
28
|
+
*/
|
|
29
|
+
function boundWrapper(value) {
|
|
30
|
+
const wrapped = asRecord(value);
|
|
31
|
+
return wrapped !== undefined && (wrapped.type === 'code' || wrapped.type === 'translation');
|
|
32
|
+
}
|
|
33
|
+
function propValue(value) {
|
|
34
|
+
const wrapped = asRecord(value);
|
|
35
|
+
return wrapped && 'data' in wrapped && typeof wrapped.type === 'string' ? wrapped.data : value;
|
|
36
|
+
}
|
|
37
|
+
/** Prop'un bağlı ifadesi: code sarmalının `ref`i (yoksa `data`sı) ya da çıplak şablon metni. */
|
|
38
|
+
function boundExpression(value) {
|
|
39
|
+
if (typeof value === 'string')
|
|
40
|
+
return value;
|
|
41
|
+
const wrapped = asRecord(value);
|
|
42
|
+
if (!wrapped || wrapped.type !== 'code')
|
|
43
|
+
return undefined;
|
|
44
|
+
const expression = wrapped.ref ?? wrapped.data;
|
|
45
|
+
return typeof expression === 'string' ? expression : undefined;
|
|
46
|
+
}
|
|
47
|
+
function label(node) {
|
|
48
|
+
return String(node.elementId ?? node.name ?? node.type ?? '?');
|
|
49
|
+
}
|
|
50
|
+
const GRID_LAYOUTS = new Set(['grid', 'cssgrid']);
|
|
51
|
+
const RELATIVE_UNIT = /\b\d*\.?\d+(em|rem)\b|calc\(/i;
|
|
52
|
+
/**
|
|
53
|
+
* THEME-012: mobil çizici `var(--ng-…)`'yi yalnız temanın yaydığı değişkenlerden çözer
|
|
54
|
+
* (`compose-native-style.ts` `resolveThemeToken`); web'deki bileşen yerel değişkenleri (`--ng-nav-bg` …)
|
|
55
|
+
* cihazda yoktur. Temada olmayan, yedeksiz bir ad çözülmez: zemin saydam kalır ve iOS gölgesi her çocuğa
|
|
56
|
+
* düşer (koşu 37 K37-c, `var(--ng-surface)`). Web profili bu kuralı koşmaz.
|
|
57
|
+
*/
|
|
58
|
+
const STYLE_GROUP_PROPS = new Set(['surfaceStyle', 'typographyStyle', 'headerStyle']);
|
|
59
|
+
const UNRESOLVED_TOKEN = /var\(\s*(--ng-[\w-]+)\s*\)/g;
|
|
60
|
+
let themeVarNames;
|
|
61
|
+
function nearestThemeVar(name) {
|
|
62
|
+
themeVarNames ??= listThemeVarNames();
|
|
63
|
+
const tail = `-${name.replace(/^--ng-/, '')}`;
|
|
64
|
+
const bySuffix = themeVarNames.filter(known => known.endsWith(tail)).sort((left, right) => left.length - right.length)[0];
|
|
65
|
+
return bySuffix ?? closestArgument(name, themeVarNames);
|
|
66
|
+
}
|
|
67
|
+
function unknownThemeTokens(value) {
|
|
68
|
+
themeVarNames ??= listThemeVarNames();
|
|
69
|
+
const found = [];
|
|
70
|
+
const visit = (entry) => {
|
|
71
|
+
const unwrapped = propValue(entry);
|
|
72
|
+
if (typeof unwrapped === 'string') {
|
|
73
|
+
for (const match of unwrapped.matchAll(UNRESOLVED_TOKEN)) {
|
|
74
|
+
const name = match[1] ?? '';
|
|
75
|
+
if (!themeVarNames.includes(name))
|
|
76
|
+
found.push(name);
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
for (const nested of Object.values(asRecord(unwrapped) ?? {}))
|
|
81
|
+
visit(nested);
|
|
82
|
+
};
|
|
83
|
+
visit(value);
|
|
84
|
+
return [...new Set(found)];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Profilin taşınabilirlik bulguları. Web profilinde HER ZAMAN boş dizi döner — web davranışı
|
|
88
|
+
* değişmez (G1 kilidi); bulgular yalnız mobil profilde üretilir.
|
|
89
|
+
*/
|
|
90
|
+
export function profileFindings(profile, input) {
|
|
91
|
+
if (profile.platform === 'web')
|
|
92
|
+
return [];
|
|
93
|
+
const findings = [];
|
|
94
|
+
const nodes = [];
|
|
95
|
+
const blockNodes = [];
|
|
96
|
+
const gridNodes = [];
|
|
97
|
+
const styleHits = new Map();
|
|
98
|
+
const unknownTypes = new Map();
|
|
99
|
+
const shellNodes = new Map();
|
|
100
|
+
const legacyNodes = [];
|
|
101
|
+
const fileIdSources = [];
|
|
102
|
+
const nodeTypes = new Map();
|
|
103
|
+
const floorImages = [];
|
|
104
|
+
const namelessIconButtons = [];
|
|
105
|
+
const unknownTokens = [];
|
|
106
|
+
const hit = (map, key, target) => map.set(key, [...(map.get(key) ?? []), target]);
|
|
107
|
+
const inspectTokens = (target, value) => {
|
|
108
|
+
for (const name of unknownThemeTokens(value)) {
|
|
109
|
+
const nearest = nearestThemeVar(name);
|
|
110
|
+
unknownTokens.push(nearest ? `${target}: ${name} → ${nearest}` : `${target}: ${name}`);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const inspectAppearance = (target, appearance) => {
|
|
114
|
+
if (!appearance)
|
|
115
|
+
return;
|
|
116
|
+
for (const key of profile.appearance.ignoredKeys) {
|
|
117
|
+
const value = appearance[key];
|
|
118
|
+
if (value !== undefined && value !== null && value !== '' && value !== 0)
|
|
119
|
+
hit(styleHits, key, target);
|
|
120
|
+
}
|
|
121
|
+
for (const key of ['widthString', 'heightString']) {
|
|
122
|
+
const value = appearance[key];
|
|
123
|
+
if (typeof value === 'string' && RELATIVE_UNIT.test(value))
|
|
124
|
+
hit(styleHits, `${key} (${value})`, target);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
// Mobil derleyici yüzey grubunun yalnız bilinen alanlarını okur; kalan alt anahtar cihazda düşer.
|
|
128
|
+
// `data`sız sarmal (`{ type: 'code', ref }`) bütün grubu bir ifadeye bağlar: denetlenecek alt anahtar yoktur.
|
|
129
|
+
const inspectSurface = (target, value) => {
|
|
130
|
+
const raw = asRecord(value);
|
|
131
|
+
if (raw && typeof raw.type === 'string' && !('data' in raw))
|
|
132
|
+
return;
|
|
133
|
+
for (const [key, wrapped] of Object.entries(asRecord(propValue(value)) ?? {})) {
|
|
134
|
+
const value = propValue(wrapped);
|
|
135
|
+
if (!SURFACE_KEYS.has(key) && value !== undefined && value !== null && value !== '')
|
|
136
|
+
hit(styleHits, `surfaceStyle.${key}`, target);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const inspectNode = (node) => {
|
|
140
|
+
const target = label(node);
|
|
141
|
+
const type = String(node.type ?? '');
|
|
142
|
+
const props = asRecord(node.props) ?? {};
|
|
143
|
+
nodes.push(node);
|
|
144
|
+
nodeTypes.set(target, type);
|
|
145
|
+
for (const prop of IMAGE_SOURCE_PROPS[type] ?? []) {
|
|
146
|
+
const expression = fileIdImageExpression(boundExpression(props[prop]) ?? '');
|
|
147
|
+
if (expression)
|
|
148
|
+
fileIdSources.push({ target: `${target}.${prop}`, expression });
|
|
149
|
+
}
|
|
150
|
+
if (Array.isArray(node.blocks) && node.blocks.length > 0 && !profile.layout.blocksMeaningful)
|
|
151
|
+
blockNodes.push(target);
|
|
152
|
+
const layout = propValue(props.layout);
|
|
153
|
+
if (typeof layout === 'string' && GRID_LAYOUTS.has(layout))
|
|
154
|
+
gridNodes.push(`${target} (layout:${layout})`);
|
|
155
|
+
inspectAppearance(target, asRecord(node.appearance));
|
|
156
|
+
inspectSurface(target, props.surfaceStyle);
|
|
157
|
+
for (const group of STYLE_GROUP_PROPS)
|
|
158
|
+
inspectTokens(target, props[group]);
|
|
159
|
+
if (type === 'image' && !imageHasDefiniteHeight(asRecord(node.appearance)))
|
|
160
|
+
floorImages.push(target);
|
|
161
|
+
if (type === 'button' && propValue(props.size) === 'icon' && !String(propValue(props.ariaLabel) ?? '').trim() && !String(propValue(props.text) ?? '').trim()) {
|
|
162
|
+
namelessIconButtons.push(target);
|
|
163
|
+
}
|
|
164
|
+
if (!profile.style.freeStyle) {
|
|
165
|
+
const style = propValue(props.style);
|
|
166
|
+
const documentCss = DOCUMENT_STYLE_TYPES.has(type) && (typeof style === 'string' || boundWrapper(props.style));
|
|
167
|
+
if (!documentCss && ((asRecord(style) && Object.keys(style).length > 0) || (typeof style === 'string' && style.trim() !== '')))
|
|
168
|
+
hit(styleHits, 'style', target);
|
|
169
|
+
}
|
|
170
|
+
if (!profile.style.hoverStyle) {
|
|
171
|
+
const hover = propValue(props.hoverStyle);
|
|
172
|
+
if (asRecord(hover) && Object.keys(hover).length > 0)
|
|
173
|
+
hit(styleHits, 'hoverStyle', target);
|
|
174
|
+
}
|
|
175
|
+
if (type && input.catalog && input.catalog.size > 0 && !input.catalog.has(type) && !profile.shell.legacyShellNodes.has(type))
|
|
176
|
+
hit(unknownTypes, type, target);
|
|
177
|
+
if (type && profile.shell.suggestions[type])
|
|
178
|
+
hit(shellNodes, type, target);
|
|
179
|
+
if (type && profile.shell.legacyShellNodes.has(type))
|
|
180
|
+
legacyNodes.push(`${target} (${type})`);
|
|
181
|
+
};
|
|
182
|
+
walk(input.nodes, inspectNode);
|
|
183
|
+
const savedTypes = new Map();
|
|
184
|
+
walk(input.savedNodes, node => savedTypes.set(label(node), String(node.type ?? '')));
|
|
185
|
+
for (const op of input.ops ?? []) {
|
|
186
|
+
const target = typeof op.target === 'string' ? op.target : '?';
|
|
187
|
+
if (op.op === 'add_component')
|
|
188
|
+
walk(op.component, inspectNode);
|
|
189
|
+
if (op.op === 'set_block' && !profile.layout.blocksMeaningful)
|
|
190
|
+
blockNodes.push(`${target} (set_block)`);
|
|
191
|
+
if (op.op === 'set_appearance')
|
|
192
|
+
inspectAppearance(target, asRecord(op.appearance));
|
|
193
|
+
if (op.op === 'replace_prop' || op.op === 'set_binding') {
|
|
194
|
+
const prop = String(op.prop ?? '');
|
|
195
|
+
const value = propValue(op.value);
|
|
196
|
+
const targetType = nodeTypes.get(target) ?? savedTypes.get(target) ?? '';
|
|
197
|
+
const documentCss = DOCUMENT_STYLE_TYPES.has(targetType) && (typeof value === 'string' || op.op === 'set_binding' || boundWrapper(op.value));
|
|
198
|
+
if (prop === 'layout' && typeof value === 'string' && GRID_LAYOUTS.has(value))
|
|
199
|
+
gridNodes.push(`${target} (layout:${value})`);
|
|
200
|
+
if (prop === 'style' && !profile.style.freeStyle && !documentCss)
|
|
201
|
+
hit(styleHits, 'style', target);
|
|
202
|
+
if (prop === 'hoverStyle' && !profile.style.hoverStyle)
|
|
203
|
+
hit(styleHits, 'hoverStyle', target);
|
|
204
|
+
if (prop === 'surfaceStyle')
|
|
205
|
+
inspectSurface(target, op.value);
|
|
206
|
+
if (STYLE_GROUP_PROPS.has(prop))
|
|
207
|
+
inspectTokens(target, op.value);
|
|
208
|
+
// MOB-011: var olan düğümün tipi op'ta yok — tip biliniyorsa (aynı yazımda eklendi) ona, yoksa prop adına bakılır.
|
|
209
|
+
const knownType = nodeTypes.get(target);
|
|
210
|
+
const imageProp = knownType === undefined ? IMAGE_SOURCE_PROP_NAMES.has(prop) : (IMAGE_SOURCE_PROPS[knownType] ?? []).includes(prop);
|
|
211
|
+
const expression = imageProp ? fileIdImageExpression(boundExpression(typeof op.expression === 'string' ? op.expression : op.value) ?? '') : undefined;
|
|
212
|
+
if (expression)
|
|
213
|
+
fileIdSources.push({ target: `${target}.${prop}`, expression });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (blockNodes.length > 0 || gridNodes.length > 0) {
|
|
217
|
+
findings.push({
|
|
218
|
+
code: MOBILE_RULES['MOB-001'].id,
|
|
219
|
+
severity: 'warn',
|
|
220
|
+
message: [
|
|
221
|
+
blockNodes.length > 0 ? `blok (12 sütun ızgara) mobil çizerde yok sayılır: ${[...new Set(blockNodes)].join(', ')}` : '',
|
|
222
|
+
gridNodes.length > 0 ? `grid/cssgrid yerleşimi yok, kap flex çizilir: ${gridNodes.join(', ')}` : ''
|
|
223
|
+
].filter(Boolean).join(' · ') + ". Sıra dizi indeksinden, yerleşim flex'ten gelir (direction/gap/horizontalAlign). Yazım platforma gitti.",
|
|
224
|
+
guide: "platform_guide('mobile')"
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
for (const [key, targets] of styleHits) {
|
|
228
|
+
findings.push({
|
|
229
|
+
code: MOBILE_RULES['MOB-002'].id,
|
|
230
|
+
severity: 'warn',
|
|
231
|
+
target: [...new Set(targets)].join(', '),
|
|
232
|
+
message: `'${key}' mobilde çizilmez — ${styleReplacement(key.split(' ')[0] ?? key)}. Yazım platforma gitti.`
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (unknownTokens.length > 0) {
|
|
236
|
+
findings.push({
|
|
237
|
+
code: 'THEME-012',
|
|
238
|
+
severity: 'warn',
|
|
239
|
+
target: [...new Set(unknownTokens.map(entry => entry.split(':')[0] ?? entry))].join(', '),
|
|
240
|
+
message: `temada tanımlı olmayan jeton cihazda çözülmez (zemin saydam kalır, iOS gölgesi içeriğe düşer): ${[...new Set(unknownTokens)].join(' · ')}. Geçerli adlar temanın yaydığı değişkenlerdir (theme_get); bilerek kullanıyorsanız yedek verin: var(--ad, #fff). Yazım platforma gitti.`,
|
|
241
|
+
guide: "platform_guide('theme')"
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
for (const [type, targets] of unknownTypes) {
|
|
245
|
+
findings.push({
|
|
246
|
+
code: MOBILE_RULES['MOB-003'].id,
|
|
247
|
+
severity: 'warn',
|
|
248
|
+
target: [...new Set(targets)].join(', '),
|
|
249
|
+
message: missingComponentAdvice(type)
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
for (const [type, targets] of shellNodes) {
|
|
253
|
+
findings.push({
|
|
254
|
+
code: MOBILE_RULES['MOB-004'].id,
|
|
255
|
+
severity: 'warn',
|
|
256
|
+
target: [...new Set(targets)].join(', '),
|
|
257
|
+
message: `'${type}' web kabuğudur, mobil uygulamada karşılığı: ${profile.shell.suggestions[type]}`,
|
|
258
|
+
guide: "examples/mobile-app-shell-{tab,stack,drawer}.json"
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
if (floorImages.length > 0) {
|
|
262
|
+
findings.push({
|
|
263
|
+
code: MOBILE_RULES['MOB-012'].id,
|
|
264
|
+
severity: 'info',
|
|
265
|
+
target: floorImages.join(', '),
|
|
266
|
+
message: "görselin boyu belirsiz, 96 dp taban boyla çizilir — boy verin (heightType: 'fixed' + heightString ya da 'fill': görsel kabuğu doldurur) ya da oran verin (appearance.aspectRatio + heightType: 'auto' + widthType 'fill'/'fixed')",
|
|
267
|
+
guide: "platform_guide('mobile')"
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
if (namelessIconButtons.length > 0) {
|
|
271
|
+
findings.push({
|
|
272
|
+
code: MOBILE_RULES['MOB-013'].id,
|
|
273
|
+
severity: 'info',
|
|
274
|
+
target: namelessIconButtons.join(', '),
|
|
275
|
+
message: "ikon boyundaki düğmenin adı yok — size: 'icon' metni çizmez ve ekran okuyucu ikonun ligatür adını (ör. 'notifications') okur; ariaLabel verin (ör. 'Bildirimler')",
|
|
276
|
+
guide: "platform_guide('mobile')"
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
if (legacyNodes.length > 0) {
|
|
280
|
+
findings.push({
|
|
281
|
+
code: MOBILE_RULES['MOB-007'].id,
|
|
282
|
+
severity: 'warn',
|
|
283
|
+
target: legacyNodes.join(', '),
|
|
284
|
+
message: "sekme çubuğu ve çekmece menüsü katalog bileşeni değildir, çizer bu düğümleri yok sayar — sekmeler Ayarlar › Sekmeler'den (application_update properties.mobile.tabs), çekmece 'drawer' widget'ı + app-bar.menuTarget"
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
if (fileIdSources.length > 0) {
|
|
288
|
+
const sample = fileIdSources[0]?.expression ?? '';
|
|
289
|
+
const object = sample.replace(/\??\.value(\s*\}\})$/, '$1');
|
|
290
|
+
findings.push({
|
|
291
|
+
code: MOBILE_RULES['MOB-011'].id,
|
|
292
|
+
severity: 'warn',
|
|
293
|
+
target: fileIdSources.map(source => source.target).join(', '),
|
|
294
|
+
message: `${[...new Set(fileIdSources.map(source => `'${source.expression}'`))].join(', ')} '.value' ile bitiyor — kayıttan okunan FILE alanı { type: 'ref', value: <dosya id>, absolute_path: <url> } döner ve '.value' dosya KİMLİĞİDİR (GUID), görsel çizilmez. Nesnenin kendisini verin ('${object}') ya da '.absolute_path' yazın. Gateway alan tipini sayfa şemasından bilemez: yol FILE alanı değilse (ör. URL taşıyan metin alanı) uyarıyı yok sayın. Yazım platforma gitti.`,
|
|
295
|
+
guide: "platform_guide('mobile') §Dosya ve fotoğraf"
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const infoSteps = new Map();
|
|
299
|
+
const badPresentation = new Map();
|
|
300
|
+
walkSteps(input.steps, step => {
|
|
301
|
+
const type = String(step.type ?? '');
|
|
302
|
+
if (profile.steps.infoOnly.has(type))
|
|
303
|
+
hit(infoSteps, type, String(step.stepId ?? step.name ?? '?'));
|
|
304
|
+
// MOB-008: sunum biçimi yalnız `redirect`te anlamlı ve kümesi kapalı.
|
|
305
|
+
// Alan YOKSA bulgu yok — varsayılan `push` ve yazımların çoğu onu yazmaz.
|
|
306
|
+
if (type !== 'redirect')
|
|
307
|
+
return;
|
|
308
|
+
const args = asRecord(step.args);
|
|
309
|
+
const presentation = args?.presentation;
|
|
310
|
+
if (presentation === undefined || presentation === null || presentation === '')
|
|
311
|
+
return;
|
|
312
|
+
if (!REDIRECT_PRESENTATIONS.has(String(presentation))) {
|
|
313
|
+
hit(badPresentation, String(presentation), String(step.stepId ?? step.name ?? '?'));
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
for (const [value, stepIds] of badPresentation) {
|
|
317
|
+
findings.push({
|
|
318
|
+
code: MOBILE_RULES['MOB-008'].id,
|
|
319
|
+
severity: 'warn',
|
|
320
|
+
target: stepIds.join(', '),
|
|
321
|
+
message: `redirect.presentation '${value}' tanınmıyor — ${[...REDIRECT_PRESENTATIONS].join(' | ')} bekleniyor. Backend doğrulayıcısı 422 döner; geçse bile çizer 'push'a düşer.`,
|
|
322
|
+
guide: "platform_guide('mobile')"
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
for (const [type, stepIds] of infoSteps) {
|
|
326
|
+
findings.push({
|
|
327
|
+
code: MOBILE_RULES['MOB-005'].id,
|
|
328
|
+
severity: 'info',
|
|
329
|
+
target: stepIds.join(', '),
|
|
330
|
+
message: type === 'download'
|
|
331
|
+
? "mobil uygulamada 'download' tarayıcı indirmesi yapmaz: adres dönen yanıt Linking.openURL ile açılır, ikili gövde (JSON değil) önbelleğe yazılıp paylaşım sayfasında (expo-sharing) açılır"
|
|
332
|
+
: "mobil uygulamada 'setcookie' çerez yazmaz, cihaz deposuna (storage, expo-sqlite/kv-store) eşlenir — okuyan ifadeler aynı çalışır"
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
return findings;
|
|
336
|
+
}
|
|
337
|
+
function walkSteps(value, visit) {
|
|
338
|
+
if (!Array.isArray(value))
|
|
339
|
+
return;
|
|
340
|
+
for (const entry of value) {
|
|
341
|
+
const step = asRecord(entry);
|
|
342
|
+
if (!step)
|
|
343
|
+
continue;
|
|
344
|
+
visit(step);
|
|
345
|
+
walkSteps(step.steps, visit);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/** Bulguları `layoutWarnings[]` satırlarına çevirir. */
|
|
349
|
+
export function findingLines(findings) {
|
|
350
|
+
return findings.map(findingLine);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* MOB-014: yazılan değişkenlerden, profilin barındırıcısının depo vermediği saklama türünü taşıyanlar.
|
|
354
|
+
* Kararı platform adı değil profilin `state.storageKinds` ekseni verir; `profileFindings`'ten ayrı
|
|
355
|
+
* durur çünkü o yalnız mobil profilde bulgu üretir, bu kural kümenin boş olduğu profilde tetiklenir.
|
|
356
|
+
*/
|
|
357
|
+
export function stateStorageFindings(profile, entries) {
|
|
358
|
+
const ignored = entries.filter(entry => entry.storage !== undefined && !profile.state.storageKinds.has(entry.storage));
|
|
359
|
+
if (ignored.length === 0)
|
|
360
|
+
return [];
|
|
361
|
+
return [{
|
|
362
|
+
code: MOBILE_RULES['MOB-014'].id,
|
|
363
|
+
severity: MOBILE_RULES['MOB-014'].severity,
|
|
364
|
+
target: ignored.map(entry => `'${entry.key}'`).join(', '),
|
|
365
|
+
message: `saklama türü (${[...new Set(ignored.map(entry => entry.storage))].join(', ')}) ${profile.platform} uygulamasında uygulanmıyor: tür kayda yazıldı ama bu platformun çalışma zamanı depo vermez — state bellekte çalışır, sayfa yeniden açılınca varsayılana döner`,
|
|
366
|
+
guide: "platform_guide('mobile') §Kalıcı state"
|
|
367
|
+
}];
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Görselin boyu belli mi — mobil `imageFrameStyle` sırası: kabuktan (`fixed` + ölçülebilir `heightString`,
|
|
371
|
+
* ya da `fill`) ya da orandan (`aspectRatio` + `heightType: 'auto'` + genişlik `fill`/`fixed`+ölçü).
|
|
372
|
+
* Değilse görsel 96 dp taban boyla çizilir (MOB-012). Koşu bulgusu: `fixed`le kurulan görseller boşluk
|
|
373
|
+
* bıraktı, doğru kalıp yalnız bileşen açıklamasında yazıyordu (plans/mobil-plantcare-acigi g-10 adım 3).
|
|
374
|
+
*/
|
|
375
|
+
function imageHasDefiniteHeight(appearance) {
|
|
376
|
+
if (!appearance)
|
|
377
|
+
return false;
|
|
378
|
+
// Çizicinin `cssLength`'i: sayı, `Npx` ya da `N%` — birimsiz dize ölçülemez.
|
|
379
|
+
const measurable = (value) => typeof value === 'number' || (typeof value === 'string' && /^\d+(\.\d+)?(px|%)$/.test(value.trim()));
|
|
380
|
+
const heightType = appearance.heightType;
|
|
381
|
+
if (heightType === 'fill' || (heightType === 'fixed' && measurable(appearance.heightString)))
|
|
382
|
+
return true;
|
|
383
|
+
const definiteWidth = appearance.widthType === 'fill' || (appearance.widthType === 'fixed' && measurable(appearance.widthString));
|
|
384
|
+
return Number(appearance.aspectRatio) > 0 && heightType === 'auto' && definiteWidth;
|
|
385
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { ENGINE_STEP_TYPES, STATE_STORAGE_KINDS, STEP_ALIASES } from './profile.js';
|
|
2
|
+
import { PAGE_CONTAINER_ELEMENT_ID } from '../normalize.js';
|
|
3
|
+
/**
|
|
4
|
+
* Mobil uygulamanın kök kapsayıcısı. Web'in `pageContainerNode()`'undan üç farkı var:
|
|
5
|
+
*
|
|
6
|
+
* - `layout` prop'u YOK: mobil `container` şeması onu düşürür (`@ng-apps/ui-native`
|
|
7
|
+
* `components/container/schema.ts` `omitProps(['layout', …])`) — mobil kanvas yalnız flex akışıdır
|
|
8
|
+
* ve backend doğrulayıcısı mobil kataloğa bakar, yazılan `layout` bilinmeyen prop olurdu.
|
|
9
|
+
* - `direction: 'column'`, `wrap: false`: telefon ekranı dikey akar; web kökü satır + sarma ile
|
|
10
|
+
* 12 sütunu taklit eder.
|
|
11
|
+
* - `blocks: []` ve `heightType: 'fill'`: çizer blokları yok sayar (D3), kök ekranı doldurur.
|
|
12
|
+
*
|
|
13
|
+
* Mizanpaj sözleşmesi (plans/mobil-mizanpaj-dili 02): kök `size: shrink` (S-16: `grow` çocukları ekran
|
|
14
|
+
* boyunca eşit paya böler) ve `horizontalAlign: stretch` (Studio şablonu). Şablon kabuklu sayfanındır:
|
|
15
|
+
* üst güvenli alan ve 16 dp yatay kenar boşluğu kabuğun işidir (`safe-area top` + slot margin'i). Kabuksuz
|
|
16
|
+
* sayfayı `shellLessPageSkeleton` tamamlar — sayfanın layout'u ancak yazımda kesinleşir.
|
|
17
|
+
*/
|
|
18
|
+
export function mobileRootContainer() {
|
|
19
|
+
return {
|
|
20
|
+
elementId: PAGE_CONTAINER_ELEMENT_ID,
|
|
21
|
+
kind: 'component',
|
|
22
|
+
name: PAGE_CONTAINER_ELEMENT_ID,
|
|
23
|
+
type: 'container',
|
|
24
|
+
props: {
|
|
25
|
+
title: { type: 'static', data: '' },
|
|
26
|
+
direction: { type: 'static', data: 'column' },
|
|
27
|
+
horizontalAlign: { type: 'static', data: 'stretch' },
|
|
28
|
+
verticalAlign: { type: 'static', data: 'top' },
|
|
29
|
+
size: { type: 'static', data: 'shrink' },
|
|
30
|
+
gap: { type: 'static', data: 'medium' },
|
|
31
|
+
wrap: { type: 'static', data: false }
|
|
32
|
+
},
|
|
33
|
+
blocks: [],
|
|
34
|
+
states: { isSticky: false, isDroppable: true, isScrollable: true, isWrapper: true },
|
|
35
|
+
appearance: { heightType: 'fill', marginValue: 0, marginType: 'none', paddingType: 'none', paddingValue: 0 },
|
|
36
|
+
events: [],
|
|
37
|
+
validations: [],
|
|
38
|
+
stateBindings: [],
|
|
39
|
+
children: []
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Kabuksuz sayfanın iskeleti (S1/S4): kök yatay 16 padding taşır — `isWrapper` yalnız margin'i sıfırlar,
|
|
44
|
+
* kökün padding'i çizilir (01'de ölçüldü) — ve ilk çizilen öğe üst güvenli alan değilse tepeye
|
|
45
|
+
* `safe_area_top` konur. Gateway'in kurduğu köke uygulanır; notu döner.
|
|
46
|
+
*/
|
|
47
|
+
export function shellLessPageSkeleton(root) {
|
|
48
|
+
const children = Array.isArray(root.children) ? root.children : [];
|
|
49
|
+
const addsSafeArea = topSafeAreaMissing(children);
|
|
50
|
+
root.appearance = { ...root.appearance, paddingType: 'custom', paddingValues: { top: 0, right: 16, bottom: 0, left: 16 } };
|
|
51
|
+
root.children = addsSafeArea ? [topSafeArea(), ...children] : children;
|
|
52
|
+
return [`mobil kabuksuz sayfa: köke yatay 16 padding yazıldı${addsSafeArea ? " ve tepeye üst güvenli alan (safe_area_top, edges:'top') eklendi" : ''} — ekran kenarı boşluğu ve durum çubuğu payı (mizanpaj S1/S4). Kabuklu sayfada ikisi kabuğun işidir: layoutId verin`];
|
|
53
|
+
}
|
|
54
|
+
/** Ekrana sabit katmanlar akışta değildir; üst payı onlar vermez (`topSafeAreaMissing`, Studio kanvası). */
|
|
55
|
+
export const OVERLAY_TYPES = ['drawer', 'fab', 'floating-container', 'modal', 'toast'];
|
|
56
|
+
/** `safe-area`'nın `edges`'i; yazılmamışsa `all` (Studio `KNOWN_SAFE_EDGES`). */
|
|
57
|
+
export function safeAreaEdges(node) {
|
|
58
|
+
const edges = node.props?.edges;
|
|
59
|
+
const value = edges && typeof edges === 'object' ? edges.data : edges;
|
|
60
|
+
return typeof value === 'string' && value !== '' ? value : 'all';
|
|
61
|
+
}
|
|
62
|
+
/** Akışta çizilen ilk öğe (kaplama katmanları ve yüzen düğümler atlanır). */
|
|
63
|
+
export function firstDrawn(nodes) {
|
|
64
|
+
return nodes.find(node => !OVERLAY_TYPES.includes(String(node.type ?? '')) && node.states?.isFloating !== true);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* İlk çizilen öğe üst payı veriyor mu — Studio'nun `topSafeAreaMissing`'iyle aynı anlam
|
|
68
|
+
* (`apps/studio/src/features/mobile/mobile-native-canvas.functions.ts`): `edges` yazılmamışsa `all`dır,
|
|
69
|
+
* yalnız `bottom` üst payı vermez; ilk öğe çocuklu bir kapsa onun ilk öğesine bakılır. Boş sayfa da pay alır.
|
|
70
|
+
*/
|
|
71
|
+
export function topSafeAreaMissing(nodes, mode = 'skeleton') {
|
|
72
|
+
const first = firstDrawn(nodes);
|
|
73
|
+
if (!first)
|
|
74
|
+
return true;
|
|
75
|
+
if (first.type === 'safe-area')
|
|
76
|
+
return safeAreaEdges(first) === 'bottom';
|
|
77
|
+
const children = Array.isArray(first.children) ? first.children : [];
|
|
78
|
+
if (children.length > 0)
|
|
79
|
+
return topSafeAreaMissing(children, mode);
|
|
80
|
+
// Studio: boş kap hiçbir şey çizmez, korunacak içerik de yoktur — uyarı yok. İskelet ise payı yine kurar.
|
|
81
|
+
return mode === 'studio' ? first.states?.isDroppable !== true : true;
|
|
82
|
+
}
|
|
83
|
+
/** Studio'nun yeni sayfa şablonundaki payla aynı (`new-page-schema.functions.ts`). */
|
|
84
|
+
function topSafeArea() {
|
|
85
|
+
return {
|
|
86
|
+
elementId: 'safe_area_top',
|
|
87
|
+
kind: 'component',
|
|
88
|
+
name: 'safe_area_top',
|
|
89
|
+
type: 'safe-area',
|
|
90
|
+
props: { edges: { type: 'static', data: 'top' } },
|
|
91
|
+
blocks: [],
|
|
92
|
+
states: { isSticky: false, isDroppable: true, isScrollable: false, isWrapper: false },
|
|
93
|
+
appearance: { heightType: 'auto', widthType: 'fill' },
|
|
94
|
+
events: [],
|
|
95
|
+
validations: [],
|
|
96
|
+
stateBindings: [],
|
|
97
|
+
children: []
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Mobil profil = taşınabilir alt küme (`plans/mobil-uygulama-expo/01-mimari-tasarim.md` §5, §7).
|
|
102
|
+
* Yazım platforma YİNE gider (D3: şema aynı); alt kümenin dışı `MOB-*` bulgusu olarak uyarılır.
|
|
103
|
+
*/
|
|
104
|
+
export const mobileProfile = Object.freeze({
|
|
105
|
+
platform: 'mobile',
|
|
106
|
+
catalogType: 'mobile',
|
|
107
|
+
layout: {
|
|
108
|
+
modes: new Set(['flex']),
|
|
109
|
+
blocksMeaningful: false,
|
|
110
|
+
rootContainer: mobileRootContainer,
|
|
111
|
+
orderBy: 'array-index'
|
|
112
|
+
},
|
|
113
|
+
appearance: {
|
|
114
|
+
ignoredKeys: new Set(['className', 'tooltip', 'tooltipPosition', 'zIndex']),
|
|
115
|
+
sizeUnits: new Set(['px', '%'])
|
|
116
|
+
},
|
|
117
|
+
style: { freeStyle: false, hoverStyle: false, resolver: 'native' },
|
|
118
|
+
steps: {
|
|
119
|
+
registered: new Set([...ENGINE_STEP_TYPES, 'alert', 'confirm', 'redirect', 'navigate', 'storage', 'setcookie', 'download', 'share', 'haptic', 'location', 'notification-permission', 'notification-schedule', 'notification-cancel']),
|
|
120
|
+
aliases: STEP_ALIASES,
|
|
121
|
+
infoOnly: new Set(['download', 'setcookie'])
|
|
122
|
+
},
|
|
123
|
+
shell: {
|
|
124
|
+
components: new Set(['app-bar']),
|
|
125
|
+
suggestions: {
|
|
126
|
+
navigations: "sekmeler: application_update ile properties.mobile.tabs (Ayarlar › Sekmeler); üst çubuk için 'app-bar'",
|
|
127
|
+
sidebar: "çekmece: 'drawer' widget'ı + app-bar.leading:'menu' ve menuTarget",
|
|
128
|
+
header: "'app-bar' (başlık, leading: auto | none | back | menu, sağ eylemler)"
|
|
129
|
+
},
|
|
130
|
+
legacyShellNodes: new Set(['tab-bar', 'drawer-menu'])
|
|
131
|
+
},
|
|
132
|
+
externalComponents: false,
|
|
133
|
+
state: { storageKinds: new Set(STATE_STORAGE_KINDS) },
|
|
134
|
+
capture: {
|
|
135
|
+
target: 'studio-mobile',
|
|
136
|
+
devices: {
|
|
137
|
+
phone: { width: 390, height: 844, dpr: 2 },
|
|
138
|
+
tablet: { width: 820, height: 1180, dpr: 2 }
|
|
139
|
+
},
|
|
140
|
+
defaultDevice: 'phone'
|
|
141
|
+
}
|
|
142
|
+
});
|