app-studio 0.6.53 → 0.6.55
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/codemod/transforms/to-app-studio.ts +2 -2
- package/dist/app-studio.cjs.development.js +119 -4
- package/dist/app-studio.cjs.development.js.map +1 -1
- package/dist/app-studio.cjs.production.min.js +1 -1
- package/dist/app-studio.esm.js +119 -4
- package/dist/app-studio.esm.js.map +1 -1
- package/dist/app-studio.umd.development.js +119 -4
- package/dist/app-studio.umd.development.js.map +1 -1
- package/dist/app-studio.umd.production.min.js +1 -1
- package/dist/providers/Theme.d.ts +1 -0
- package/package.json +1 -1
|
@@ -353,7 +353,7 @@ export function addImportStatement(root, j, importNames, fromModule) {
|
|
|
353
353
|
|
|
354
354
|
function getTemplateLiteralValue(node) {
|
|
355
355
|
if (node.type !== 'TemplateLiteral') {
|
|
356
|
-
console.log({ node });
|
|
356
|
+
// console.log({ node });
|
|
357
357
|
throw new Error('Node is not a TemplateLiteral');
|
|
358
358
|
}
|
|
359
359
|
|
|
@@ -557,7 +557,7 @@ export function transformCode(
|
|
|
557
557
|
} else if (typeof value === 'number') {
|
|
558
558
|
valueLiteral = j.numericLiteral(value);
|
|
559
559
|
} else {
|
|
560
|
-
console.log({ value, key }, typeof value);
|
|
560
|
+
// console.log({ value, key }, typeof value);
|
|
561
561
|
valueLiteral = value;
|
|
562
562
|
}
|
|
563
563
|
|
|
@@ -898,6 +898,7 @@ const defaultDarkColorConfig = {
|
|
|
898
898
|
// --- Create Theme Context ---
|
|
899
899
|
const ThemeContext = /*#__PURE__*/React.createContext({
|
|
900
900
|
getColor: () => '',
|
|
901
|
+
getColorHex: () => '',
|
|
901
902
|
theme: {},
|
|
902
903
|
colors: {
|
|
903
904
|
main: defaultLightColors,
|
|
@@ -952,6 +953,51 @@ const convertToRgba = (color, alpha) => {
|
|
|
952
953
|
console.warn(`Unable to convert color "${color}" to rgba format`);
|
|
953
954
|
return color;
|
|
954
955
|
};
|
|
956
|
+
const clamp255 = value => Math.max(0, Math.min(255, value));
|
|
957
|
+
const toTwoHex = value => clamp255(Math.round(value)).toString(16).padStart(2, '0');
|
|
958
|
+
const normalizeToHex = color => {
|
|
959
|
+
if (!color || typeof color !== 'string') return String(color);
|
|
960
|
+
const trimmed = color.trim();
|
|
961
|
+
if (trimmed === TRANSPARENT) return '#00000000';
|
|
962
|
+
if (trimmed.startsWith('#')) {
|
|
963
|
+
const hex = trimmed.slice(1);
|
|
964
|
+
if (hex.length === 3) {
|
|
965
|
+
const r = hex[0] + hex[0];
|
|
966
|
+
const g = hex[1] + hex[1];
|
|
967
|
+
const b = hex[2] + hex[2];
|
|
968
|
+
return `#${r}${g}${b}`.toLowerCase();
|
|
969
|
+
}
|
|
970
|
+
if (hex.length === 4) {
|
|
971
|
+
const r = hex[0] + hex[0];
|
|
972
|
+
const g = hex[1] + hex[1];
|
|
973
|
+
const b = hex[2] + hex[2];
|
|
974
|
+
const a = hex[3] + hex[3];
|
|
975
|
+
return `#${r}${g}${b}${a}`.toLowerCase();
|
|
976
|
+
}
|
|
977
|
+
if (hex.length === 6 || hex.length === 8) {
|
|
978
|
+
return `#${hex}`.toLowerCase();
|
|
979
|
+
}
|
|
980
|
+
return trimmed;
|
|
981
|
+
}
|
|
982
|
+
if (trimmed.startsWith('rgb')) {
|
|
983
|
+
const match = trimmed.match(/rgba?\(([^)]+)\)/i);
|
|
984
|
+
if (!match) return trimmed;
|
|
985
|
+
const parts = match[1].split(',').map(p => p.trim()).filter(Boolean);
|
|
986
|
+
const r = Number(parts[0]);
|
|
987
|
+
const g = Number(parts[1]);
|
|
988
|
+
const b = Number(parts[2]);
|
|
989
|
+
const a = parts.length >= 4 ? Number(parts[3]) : 1;
|
|
990
|
+
if ([r, g, b].some(v => Number.isNaN(v)) || Number.isNaN(a)) return trimmed;
|
|
991
|
+
const rr = toTwoHex(r);
|
|
992
|
+
const gg = toTwoHex(g);
|
|
993
|
+
const bb = toTwoHex(b);
|
|
994
|
+
const alpha = Math.max(0, Math.min(1, a));
|
|
995
|
+
if (alpha >= 1) return `#${rr}${gg}${bb}`.toLowerCase();
|
|
996
|
+
const aa = toTwoHex(alpha * 255);
|
|
997
|
+
return `#${rr}${gg}${bb}${aa}`.toLowerCase();
|
|
998
|
+
}
|
|
999
|
+
return trimmed;
|
|
1000
|
+
};
|
|
955
1001
|
// --- Deep Merge Function (remains the same, assuming it works as intended) ---
|
|
956
1002
|
// Consider using a library like `lodash.merge` or `deepmerge` if complexity grows
|
|
957
1003
|
// or edge cases (like merging arrays) need different handling.
|
|
@@ -1006,6 +1052,60 @@ const ThemeProvider = _ref => {
|
|
|
1006
1052
|
dark: deepMerge(defaultDarkColorConfig, darkOverride)
|
|
1007
1053
|
}), [lightOverride, darkOverride]);
|
|
1008
1054
|
const currentColors = React.useMemo(() => themeColors[themeMode], [themeColors, themeMode]);
|
|
1055
|
+
const resolveColorTokenForMode = React.useCallback(function (token, mode, override, depth) {
|
|
1056
|
+
if (depth === void 0) {
|
|
1057
|
+
depth = 0;
|
|
1058
|
+
}
|
|
1059
|
+
if (!token || typeof token !== 'string') return String(token);
|
|
1060
|
+
if (token === TRANSPARENT) return token;
|
|
1061
|
+
if (depth > 25) return token;
|
|
1062
|
+
const effectiveTheme = override?.theme ? deepMerge(mergedTheme, override.theme) : mergedTheme;
|
|
1063
|
+
// Resolve theme.* tokens recursively
|
|
1064
|
+
if (token.startsWith(THEME_PREFIX)) {
|
|
1065
|
+
const themeKey = token.substring(THEME_PREFIX.length);
|
|
1066
|
+
const themeValue = effectiveTheme[themeKey];
|
|
1067
|
+
if (typeof themeValue === 'string') {
|
|
1068
|
+
return resolveColorTokenForMode(themeValue, mode, override, depth + 1);
|
|
1069
|
+
}
|
|
1070
|
+
return token;
|
|
1071
|
+
}
|
|
1072
|
+
// Resolve explicit mode paths like "light.blue.500" / "dark.blue.500"
|
|
1073
|
+
if (token.startsWith('light.') || token.startsWith('dark.')) {
|
|
1074
|
+
const explicitMode = token.startsWith('light.') ? 'light' : 'dark';
|
|
1075
|
+
const prefixLength = token.startsWith('light.') ? 6 : 5;
|
|
1076
|
+
const modifiedName = `${COLOR_PREFIX}${token.substring(prefixLength)}`;
|
|
1077
|
+
return resolveColorTokenForMode(modifiedName, explicitMode, override, depth + 1);
|
|
1078
|
+
}
|
|
1079
|
+
// Resolve color.* tokens
|
|
1080
|
+
if (token.startsWith(COLOR_PREFIX)) {
|
|
1081
|
+
const keys = token.substring(COLOR_PREFIX.length).split('.');
|
|
1082
|
+
const colorsToUse = themeColors[mode];
|
|
1083
|
+
const palette = deepMerge(colorsToUse.palette, override?.colors?.palette || {});
|
|
1084
|
+
const main = deepMerge(colorsToUse.main, override?.colors?.main || {});
|
|
1085
|
+
if (keys.length === 3) {
|
|
1086
|
+
// e.g. color.blue.500.200 (alpha)
|
|
1087
|
+
const [colorName, variant, alphaStr] = keys;
|
|
1088
|
+
const base = palette?.[colorName]?.[variant];
|
|
1089
|
+
const alpha = parseInt(alphaStr, 10);
|
|
1090
|
+
if (typeof base === 'string' && !isNaN(alpha)) {
|
|
1091
|
+
return convertToRgba(base, alpha);
|
|
1092
|
+
}
|
|
1093
|
+
return token;
|
|
1094
|
+
}
|
|
1095
|
+
if (keys.length === 2) {
|
|
1096
|
+
const [colorName, variant] = keys;
|
|
1097
|
+
const value = palette?.[colorName]?.[variant];
|
|
1098
|
+
return typeof value === 'string' ? value : token;
|
|
1099
|
+
}
|
|
1100
|
+
if (keys.length === 1) {
|
|
1101
|
+
const [colorName] = keys;
|
|
1102
|
+
const value = main?.[colorName];
|
|
1103
|
+
return typeof value === 'string' ? value : token;
|
|
1104
|
+
}
|
|
1105
|
+
return token;
|
|
1106
|
+
}
|
|
1107
|
+
return token;
|
|
1108
|
+
}, [mergedTheme, themeColors]);
|
|
1009
1109
|
// --- Helper function to resolve color tokens to actual values ---
|
|
1010
1110
|
const resolveColorToken = React.useCallback(token => {
|
|
1011
1111
|
if (!token || typeof token !== 'string') return String(token);
|
|
@@ -1125,14 +1225,29 @@ const ThemeProvider = _ref => {
|
|
|
1125
1225
|
if (needCache) colorCache.set(cacheKey, resolvedColor);
|
|
1126
1226
|
return resolvedColor;
|
|
1127
1227
|
}, [mergedTheme, themeColors, themeMode, colorCache, strict]);
|
|
1228
|
+
const getColorHex = React.useCallback((name, override) => {
|
|
1229
|
+
if (!name || typeof name !== 'string') return String(name);
|
|
1230
|
+
if (name === TRANSPARENT) return '#00000000';
|
|
1231
|
+
const effectiveMode = override?.themeMode ?? themeMode;
|
|
1232
|
+
const cacheKey = `${name}-${effectiveMode}-hex`;
|
|
1233
|
+
// Cache only when no override object is provided
|
|
1234
|
+
if (!override && colorCache.has(cacheKey)) {
|
|
1235
|
+
return colorCache.get(cacheKey);
|
|
1236
|
+
}
|
|
1237
|
+
const resolved = resolveColorTokenForMode(name, effectiveMode, override);
|
|
1238
|
+
const hex = normalizeToHex(resolved);
|
|
1239
|
+
if (!override) colorCache.set(cacheKey, hex);
|
|
1240
|
+
return hex;
|
|
1241
|
+
}, [themeMode, colorCache, resolveColorTokenForMode]);
|
|
1128
1242
|
// --- Memoize Context Value ---
|
|
1129
1243
|
const contextValue = React.useMemo(() => ({
|
|
1130
1244
|
getColor,
|
|
1245
|
+
getColorHex,
|
|
1131
1246
|
theme: mergedTheme,
|
|
1132
1247
|
colors: currentColors,
|
|
1133
1248
|
themeMode,
|
|
1134
1249
|
setThemeMode
|
|
1135
|
-
}), [getColor, mergedTheme, currentColors, themeMode]);
|
|
1250
|
+
}), [getColor, getColorHex, mergedTheme, currentColors, themeMode]);
|
|
1136
1251
|
return /*#__PURE__*/React__default.createElement(ThemeContext.Provider, {
|
|
1137
1252
|
value: contextValue
|
|
1138
1253
|
}, /*#__PURE__*/React__default.createElement("style", null, generateCSSVariables(mergedTheme, themeColors.light, themeColors.dark)), /*#__PURE__*/React__default.createElement("div", {
|
|
@@ -1399,7 +1514,7 @@ const cssProperties = /*#__PURE__*/new Set([
|
|
|
1399
1514
|
// Typography
|
|
1400
1515
|
'fontFamily', 'fontSize', 'fontWeight', 'lineHeight', 'letterSpacing', 'textAlign', 'textDecoration', 'textTransform', 'whiteSpace', 'wordBreak', 'wordSpacing', 'wordWrap',
|
|
1401
1516
|
// Colors and Backgrounds
|
|
1402
|
-
'color', 'backgroundColor', 'background', 'backgroundImage', 'backgroundSize', 'backgroundPosition', 'backgroundRepeat', 'backgroundClip', 'WebkitBackgroundClip', '
|
|
1517
|
+
'color', 'backgroundColor', 'background', 'backgroundImage', 'backgroundSize', 'backgroundPosition', 'backgroundRepeat', 'backgroundClip', 'textFillColor', 'WebkitTextFillColor', 'WebkitBackgroundClip', 'WebkitTextStroke', 'WebkitTextStrokeColor', 'WebkitTextStrokeWidth', 'WebkitTextStrokeStyle', 'opacity',
|
|
1403
1518
|
// Borders
|
|
1404
1519
|
'border', 'borderWidth', 'borderStyle', 'borderColor', 'borderRadius', 'borderTop', 'borderRight', 'borderBottom', 'borderLeft', 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomLeftRadius', 'borderBottomRightRadius',
|
|
1405
1520
|
// Effects
|
|
@@ -2854,7 +2969,7 @@ const TextView = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
2854
2969
|
style,
|
|
2855
2970
|
...props
|
|
2856
2971
|
} = _ref;
|
|
2857
|
-
console.log('props', props, children);
|
|
2972
|
+
// console.log('props', props, children);
|
|
2858
2973
|
// For sub/sup text, use inline display
|
|
2859
2974
|
const noLineBreak = isSub || isSup ? {
|
|
2860
2975
|
display: 'inline'
|
|
@@ -2890,7 +3005,7 @@ const TextView = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
|
|
|
2890
3005
|
top: isSup ? '-0.5em' : undefined
|
|
2891
3006
|
}, views?.sup), finalChildren);
|
|
2892
3007
|
}
|
|
2893
|
-
console.log('commonProps', commonProps, children);
|
|
3008
|
+
// console.log('commonProps', commonProps, children);
|
|
2894
3009
|
return /*#__PURE__*/React__default.createElement(Element, Object.assign({}, commonProps, {
|
|
2895
3010
|
ref: ref
|
|
2896
3011
|
}), finalChildren);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-studio.cjs.development.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-studio.cjs.development.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|