app-studio 0.6.57 → 0.6.59
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/dist/app-studio.cjs.development.js +202 -33
- 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 +202 -33
- package/dist/app-studio.esm.js.map +1 -1
- package/dist/app-studio.umd.development.js +202 -33
- 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 +2 -1
- package/docs/README.md +2 -2
- package/package.json +1 -1
|
@@ -900,7 +900,8 @@ const ThemeContext = /*#__PURE__*/React.createContext({
|
|
|
900
900
|
getColor: () => '',
|
|
901
901
|
getColorHex: () => '',
|
|
902
902
|
getColorRGBA: () => '',
|
|
903
|
-
getColorScheme: () =>
|
|
903
|
+
getColorScheme: () => undefined,
|
|
904
|
+
getContrastColor: () => 'black',
|
|
904
905
|
theme: {},
|
|
905
906
|
colors: {
|
|
906
907
|
main: defaultLightColors,
|
|
@@ -1292,20 +1293,107 @@ const ThemeProvider = _ref => {
|
|
|
1292
1293
|
if (!override) colorCache.set(cacheKey, rgba);
|
|
1293
1294
|
return rgba;
|
|
1294
1295
|
}, [themeMode, colorCache, resolveColorTokenForMode]);
|
|
1295
|
-
const getColorScheme = React.useCallback(override => {
|
|
1296
|
-
|
|
1297
|
-
|
|
1296
|
+
const getColorScheme = React.useCallback((name, override) => {
|
|
1297
|
+
if (!name || typeof name !== 'string') return undefined;
|
|
1298
|
+
const effectiveMode = override?.themeMode ?? themeMode;
|
|
1299
|
+
const effectiveTheme = override?.theme ? deepMerge(mergedTheme, override.theme) : mergedTheme;
|
|
1300
|
+
// Resolve theme.* tokens to get the underlying color token
|
|
1301
|
+
let colorToken = name;
|
|
1302
|
+
if (name.startsWith(THEME_PREFIX)) {
|
|
1303
|
+
const themeKey = name.substring(THEME_PREFIX.length);
|
|
1304
|
+
const themeValue = effectiveTheme[themeKey];
|
|
1305
|
+
if (typeof themeValue === 'string') {
|
|
1306
|
+
colorToken = themeValue;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
// Handle light.* or dark.* prefixes
|
|
1310
|
+
if (colorToken.startsWith('light.') || colorToken.startsWith('dark.')) {
|
|
1311
|
+
const prefixLength = colorToken.startsWith('light.') ? 6 : 5;
|
|
1312
|
+
colorToken = `${COLOR_PREFIX}${colorToken.substring(prefixLength)}`;
|
|
1313
|
+
}
|
|
1314
|
+
// Extract color scheme from color.* tokens (e.g., color.blue.500 -> 'blue')
|
|
1315
|
+
if (colorToken.startsWith(COLOR_PREFIX)) {
|
|
1316
|
+
const keys = colorToken.substring(COLOR_PREFIX.length).split('.');
|
|
1317
|
+
if (keys.length >= 1) {
|
|
1318
|
+
return keys[0]; // Return the color scheme name (e.g., 'blue', 'pink')
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
// Handle hex or rgba colors by finding the closest match in the palette
|
|
1322
|
+
const normalizedInput = normalizeToHex(colorToken).toLowerCase();
|
|
1323
|
+
if (normalizedInput.startsWith('#')) {
|
|
1324
|
+
const colorsToUse = themeColors[effectiveMode];
|
|
1325
|
+
const palette = deepMerge(colorsToUse.palette, override?.colors?.palette || {});
|
|
1326
|
+
const main = deepMerge(colorsToUse.main, override?.colors?.main || {});
|
|
1327
|
+
// First check main colors for exact match
|
|
1328
|
+
for (const [colorName, colorValue] of Object.entries(main)) {
|
|
1329
|
+
if (typeof colorValue === 'string') {
|
|
1330
|
+
const normalizedPalette = normalizeToHex(colorValue).toLowerCase();
|
|
1331
|
+
if (normalizedPalette === normalizedInput) {
|
|
1332
|
+
return colorName;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
// Then check palette colors for exact match
|
|
1337
|
+
for (const [colorName, shades] of Object.entries(palette)) {
|
|
1338
|
+
if (typeof shades === 'object' && shades !== null) {
|
|
1339
|
+
for (const [, shadeValue] of Object.entries(shades)) {
|
|
1340
|
+
if (typeof shadeValue === 'string') {
|
|
1341
|
+
const normalizedPalette = normalizeToHex(shadeValue).toLowerCase();
|
|
1342
|
+
if (normalizedPalette === normalizedInput) {
|
|
1343
|
+
return colorName;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return undefined;
|
|
1351
|
+
}, [mergedTheme, themeMode, themeColors]);
|
|
1352
|
+
const getContrastColor = React.useCallback((name, override) => {
|
|
1353
|
+
if (!name || typeof name !== 'string') return 'black';
|
|
1354
|
+
const effectiveMode = override?.themeMode ?? themeMode;
|
|
1355
|
+
// First resolve the color to a hex value
|
|
1356
|
+
let hexColor;
|
|
1357
|
+
// Check if it's already a hex or rgb color
|
|
1358
|
+
if (name.startsWith('#') || name.startsWith('rgb')) {
|
|
1359
|
+
hexColor = normalizeToHex(name);
|
|
1360
|
+
} else {
|
|
1361
|
+
// Resolve the token to get the actual color value
|
|
1362
|
+
const resolved = resolveColorTokenForMode(name, effectiveMode, override);
|
|
1363
|
+
hexColor = normalizeToHex(resolved);
|
|
1364
|
+
}
|
|
1365
|
+
// If we couldn't get a valid hex, default to black
|
|
1366
|
+
if (!hexColor.startsWith('#') || hexColor.length < 7) {
|
|
1367
|
+
return 'black';
|
|
1368
|
+
}
|
|
1369
|
+
// Extract RGB values
|
|
1370
|
+
const hex = hexColor.slice(1);
|
|
1371
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
1372
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
1373
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
1374
|
+
// Calculate relative luminance using the sRGB formula
|
|
1375
|
+
// https://www.w3.org/TR/WCAG20/#relativeluminancedef
|
|
1376
|
+
const toLinear = c => {
|
|
1377
|
+
const sRGB = c / 255;
|
|
1378
|
+
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
|
1379
|
+
};
|
|
1380
|
+
const luminance = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
1381
|
+
// Use threshold of 0.179 (WCAG recommendation)
|
|
1382
|
+
// Return white for dark colors, black for light colors
|
|
1383
|
+
return luminance > 0.179 ? 'black' : 'white';
|
|
1384
|
+
}, [themeMode, resolveColorTokenForMode]);
|
|
1298
1385
|
// --- Memoize Context Value ---
|
|
1299
1386
|
const contextValue = React.useMemo(() => ({
|
|
1300
1387
|
getColor,
|
|
1301
1388
|
getColorHex,
|
|
1302
1389
|
getColorRGBA,
|
|
1303
1390
|
getColorScheme,
|
|
1391
|
+
getContrastColor,
|
|
1304
1392
|
theme: mergedTheme,
|
|
1305
1393
|
colors: currentColors,
|
|
1306
1394
|
themeMode,
|
|
1307
1395
|
setThemeMode
|
|
1308
|
-
}), [getColor, getColorHex, getColorRGBA, getColorScheme, mergedTheme, currentColors, themeMode]);
|
|
1396
|
+
}), [getColor, getColorHex, getColorRGBA, getColorScheme, getContrastColor, mergedTheme, currentColors, themeMode]);
|
|
1309
1397
|
return /*#__PURE__*/React__default.createElement(ThemeContext.Provider, {
|
|
1310
1398
|
value: contextValue
|
|
1311
1399
|
}, /*#__PURE__*/React__default.createElement("style", null, generateCSSVariables(mergedTheme, themeColors.light, themeColors.dark)), /*#__PURE__*/React__default.createElement("div", {
|
|
@@ -1375,6 +1463,33 @@ const debounce = (func, wait) => {
|
|
|
1375
1463
|
timeout = setTimeout(() => func(...args), wait);
|
|
1376
1464
|
};
|
|
1377
1465
|
};
|
|
1466
|
+
// Helper to compute breakpoint from width
|
|
1467
|
+
const getBreakpointFromWidth = (width, breakpoints) => {
|
|
1468
|
+
// console.log('[ResponsiveProvider] Computing breakpoint for width:', width);
|
|
1469
|
+
// console.log('[ResponsiveProvider] Breakpoints config:', breakpoints);
|
|
1470
|
+
const sortedBreakpoints = Object.entries(breakpoints).sort((_ref, _ref2) => {
|
|
1471
|
+
let [, a] = _ref;
|
|
1472
|
+
let [, b] = _ref2;
|
|
1473
|
+
return b - a;
|
|
1474
|
+
}); // Sort descending by min value
|
|
1475
|
+
// console.log('[ResponsiveProvider] Sorted breakpoints:', sortedBreakpoints);
|
|
1476
|
+
for (const [name, minWidth] of sortedBreakpoints) {
|
|
1477
|
+
if (width >= minWidth) {
|
|
1478
|
+
// console.log(
|
|
1479
|
+
// '[ResponsiveProvider] ✓ Match found:',
|
|
1480
|
+
// name,
|
|
1481
|
+
// 'for width',
|
|
1482
|
+
// width,
|
|
1483
|
+
// '>= minWidth',
|
|
1484
|
+
// minWidth
|
|
1485
|
+
// );
|
|
1486
|
+
return name;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
const fallback = sortedBreakpoints[sortedBreakpoints.length - 1]?.[0] || 'xs';
|
|
1490
|
+
// console.log('[ResponsiveProvider] No match, using fallback:', fallback);
|
|
1491
|
+
return fallback;
|
|
1492
|
+
};
|
|
1378
1493
|
// Create the Context with default values
|
|
1379
1494
|
const ResponsiveContext = /*#__PURE__*/React.createContext({
|
|
1380
1495
|
breakpoints: defaultBreakpointsConfig,
|
|
@@ -1388,41 +1503,83 @@ const ResponsiveContext = /*#__PURE__*/React.createContext({
|
|
|
1388
1503
|
});
|
|
1389
1504
|
// Custom Hook to Access the Responsive Context
|
|
1390
1505
|
const useResponsiveContext = () => React.useContext(ResponsiveContext);
|
|
1391
|
-
const ResponsiveProvider =
|
|
1506
|
+
const ResponsiveProvider = _ref3 => {
|
|
1392
1507
|
let {
|
|
1393
1508
|
breakpoints = defaultBreakpointsConfig,
|
|
1394
1509
|
devices = defaultDeviceConfig,
|
|
1395
1510
|
children
|
|
1396
|
-
} =
|
|
1397
|
-
const [screen, setScreen] = React.useState(
|
|
1511
|
+
} = _ref3;
|
|
1512
|
+
const [screen, setScreen] = React.useState(() => {
|
|
1513
|
+
// Initialize with correct breakpoint instead of hardcoded 'xs'
|
|
1514
|
+
if (typeof window !== 'undefined') {
|
|
1515
|
+
return getBreakpointFromWidth(window.innerWidth, breakpoints);
|
|
1516
|
+
}
|
|
1517
|
+
return 'xs';
|
|
1518
|
+
});
|
|
1398
1519
|
const [orientation, setOrientation] = React.useState('portrait');
|
|
1520
|
+
const [size, setSize] = React.useState({
|
|
1521
|
+
width: typeof window !== 'undefined' ? window.innerWidth : 0,
|
|
1522
|
+
height: typeof window !== 'undefined' ? window.innerHeight : 0
|
|
1523
|
+
});
|
|
1399
1524
|
const mediaQueries = React.useMemo(() => getMediaQueries(breakpoints), [breakpoints]);
|
|
1400
1525
|
React.useEffect(() => {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1526
|
+
// console.log('[ResponsiveProvider] useEffect running - initial setup');
|
|
1527
|
+
// Set initial screen size immediately based on window width
|
|
1528
|
+
const initialScreen = getBreakpointFromWidth(window.innerWidth, breakpoints);
|
|
1529
|
+
// console.log(
|
|
1530
|
+
// '[ResponsiveProvider] Setting initial screen to:',
|
|
1531
|
+
// initialScreen
|
|
1532
|
+
// );
|
|
1533
|
+
setScreen(initialScreen);
|
|
1534
|
+
const handleResize = () => {
|
|
1535
|
+
const newWidth = window.innerWidth;
|
|
1536
|
+
const newHeight = window.innerHeight;
|
|
1537
|
+
// console.log('[ResponsiveProvider] Resize event - new dimensions:', {
|
|
1538
|
+
// width: newWidth,
|
|
1539
|
+
// height: newHeight,
|
|
1540
|
+
// });
|
|
1541
|
+
setSize({
|
|
1542
|
+
width: newWidth,
|
|
1543
|
+
height: newHeight
|
|
1544
|
+
});
|
|
1545
|
+
// Update screen on resize
|
|
1546
|
+
const newScreen = getBreakpointFromWidth(newWidth, breakpoints);
|
|
1547
|
+
// console.log('[ResponsiveProvider] Setting screen to:', newScreen);
|
|
1548
|
+
setScreen(newScreen);
|
|
1549
|
+
};
|
|
1550
|
+
const debouncedResize = debounce(handleResize, 100);
|
|
1551
|
+
window.addEventListener('resize', debouncedResize);
|
|
1552
|
+
// Set up orientation listener
|
|
1409
1553
|
const orientationMql = window.matchMedia('(orientation: landscape)');
|
|
1410
1554
|
const onOrientationChange = () => setOrientation(orientationMql.matches ? 'landscape' : 'portrait');
|
|
1411
|
-
orientationMql.
|
|
1555
|
+
if (orientationMql.addEventListener) {
|
|
1556
|
+
orientationMql.addEventListener('change', onOrientationChange);
|
|
1557
|
+
} else {
|
|
1558
|
+
orientationMql.addListener(onOrientationChange);
|
|
1559
|
+
}
|
|
1412
1560
|
onOrientationChange();
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1561
|
+
return () => {
|
|
1562
|
+
window.removeEventListener('resize', debouncedResize);
|
|
1563
|
+
if (orientationMql.removeEventListener) {
|
|
1564
|
+
orientationMql.removeEventListener('change', onOrientationChange);
|
|
1565
|
+
} else {
|
|
1566
|
+
orientationMql.removeListener(onOrientationChange);
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
}, [breakpoints]); // Removed mediaQueries dep since we now use direct width comparison
|
|
1570
|
+
const value = React.useMemo(() => {
|
|
1571
|
+
const contextValue = {
|
|
1572
|
+
breakpoints,
|
|
1573
|
+
devices,
|
|
1574
|
+
mediaQueries,
|
|
1575
|
+
currentWidth: size.width,
|
|
1576
|
+
currentHeight: size.height,
|
|
1577
|
+
currentBreakpoint: screen,
|
|
1578
|
+
currentDevice: determineCurrentDevice(screen, devices),
|
|
1579
|
+
orientation
|
|
1580
|
+
};
|
|
1581
|
+
return contextValue;
|
|
1582
|
+
}, [breakpoints, devices, mediaQueries, size, screen, orientation]);
|
|
1426
1583
|
return /*#__PURE__*/React__default.createElement(ResponsiveContext.Provider, {
|
|
1427
1584
|
value: value
|
|
1428
1585
|
}, children);
|
|
@@ -2598,7 +2755,7 @@ const extractUtilityClasses = (props, getColor, mediaQueries, devices, manager)
|
|
|
2598
2755
|
if (props.blend === true) {
|
|
2599
2756
|
setBlend(props, computedStyles);
|
|
2600
2757
|
Object.keys(props).forEach(property => {
|
|
2601
|
-
if (props[property]?.color === undefined && property.startsWith('_')
|
|
2758
|
+
if (props[property]?.color === undefined && (property.startsWith('_') || property === 'on' || property === 'media')) {
|
|
2602
2759
|
setBlend(props[property], props[property]);
|
|
2603
2760
|
}
|
|
2604
2761
|
});
|
|
@@ -2737,7 +2894,13 @@ const Element = /*#__PURE__*/React__default.memo(/*#__PURE__*/React.forwardRef((
|
|
|
2737
2894
|
} = props;
|
|
2738
2895
|
let blend = initialBlend;
|
|
2739
2896
|
if (blend !== false && props.color === undefined && typeof props.children === 'string' && (as === 'span' || as === 'div' || as === 'sub' || as === 'sup')) {
|
|
2740
|
-
|
|
2897
|
+
const otherMediaProps = {
|
|
2898
|
+
...(props.on || {}),
|
|
2899
|
+
...(props.media || {})
|
|
2900
|
+
};
|
|
2901
|
+
if (otherMediaProps.color === undefined) {
|
|
2902
|
+
blend = true;
|
|
2903
|
+
}
|
|
2741
2904
|
}
|
|
2742
2905
|
const elementRef = React.useRef(null);
|
|
2743
2906
|
const setRef = React.useCallback(node => {
|
|
@@ -2763,6 +2926,10 @@ const Element = /*#__PURE__*/React__default.memo(/*#__PURE__*/React.forwardRef((
|
|
|
2763
2926
|
manager
|
|
2764
2927
|
} = useStyleRegistry();
|
|
2765
2928
|
const [isVisible, setIsVisible] = React.useState(false);
|
|
2929
|
+
console.log({
|
|
2930
|
+
mediaQueries,
|
|
2931
|
+
devices
|
|
2932
|
+
});
|
|
2766
2933
|
React.useEffect(() => {
|
|
2767
2934
|
if (!animateIn) {
|
|
2768
2935
|
setIsVisible(true);
|
|
@@ -5024,13 +5191,15 @@ const useResponsive = () => {
|
|
|
5024
5191
|
devices
|
|
5025
5192
|
} = context;
|
|
5026
5193
|
const on = s => devices[s] ? devices[s].includes(screen) : s === screen;
|
|
5027
|
-
|
|
5194
|
+
const result = {
|
|
5028
5195
|
...context,
|
|
5029
5196
|
screen,
|
|
5030
5197
|
orientation,
|
|
5031
5198
|
on,
|
|
5032
5199
|
is: on
|
|
5033
5200
|
};
|
|
5201
|
+
// console.log('[useResponsive] Hook called, returning:', { screen, orientation, 'on(mobile)': on('mobile') });
|
|
5202
|
+
return result;
|
|
5034
5203
|
};
|
|
5035
5204
|
|
|
5036
5205
|
// Helper to check if element is a Window object (works across iframes)
|
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|