harlley commited on
Commit
cd4ccbf
·
1 Parent(s): 9014640

fix format and lint

Browse files
biome.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
3
+ "linter": {
4
+ "enabled": true,
5
+ "rules": {
6
+ "recommended": true
7
+ }
8
+ },
9
+ "formatter": {
10
+ "enabled": true,
11
+ "indentStyle": "space",
12
+ "indentWidth": 2
13
+ },
14
+ "files": {
15
+ "includes": [
16
+ "src/**/*.ts",
17
+ "src/**/*.tsx"
18
+ ],
19
+ "ignoreUnknown": true
20
+ },
21
+ "overrides": [
22
+ {
23
+ "includes": [
24
+ "src/components/ui/**"
25
+ ],
26
+ "linter": {
27
+ "enabled": false
28
+ }
29
+ }
30
+ ]
31
+ }
eslint.config.js DELETED
@@ -1,23 +0,0 @@
1
- import js from '@eslint/js'
2
- import globals from 'globals'
3
- import reactHooks from 'eslint-plugin-react-hooks'
4
- import reactRefresh from 'eslint-plugin-react-refresh'
5
- import tseslint from 'typescript-eslint'
6
- import { defineConfig, globalIgnores } from 'eslint/config'
7
-
8
- export default defineConfig([
9
- globalIgnores(['dist']),
10
- {
11
- files: ['**/*.{ts,tsx}'],
12
- extends: [
13
- js.configs.recommended,
14
- tseslint.configs.recommended,
15
- reactHooks.configs.flat.recommended,
16
- reactRefresh.configs.vite,
17
- ],
18
- languageOptions: {
19
- ecmaVersion: 2020,
20
- globals: globals.browser,
21
- },
22
- },
23
- ])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
package-lock.json CHANGED
@@ -25,17 +25,13 @@
25
  "zustand": "^5.0.2"
26
  },
27
  "devDependencies": {
28
- "@eslint/js": "^9.39.1",
29
  "@types/node": "^24.10.1",
30
  "@types/react": "^19.2.5",
31
  "@types/react-dom": "^19.2.3",
32
  "@vitejs/plugin-react": "^5.1.1",
33
- "eslint": "^9.39.1",
34
- "eslint-plugin-react-hooks": "^7.0.1",
35
- "eslint-plugin-react-refresh": "^0.4.24",
36
  "globals": "^16.5.0",
37
  "typescript": "~5.9.3",
38
- "typescript-eslint": "^8.46.4",
39
  "vite": "^7.2.4"
40
  }
41
  },
@@ -492,6 +488,169 @@
492
  }
493
  }
494
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  "node_modules/@dotenvx/dotenvx": {
496
  "version": "1.51.2",
497
  "license": "BSD-3-Clause",
@@ -635,141 +794,6 @@
635
  "node": ">=18"
636
  }
637
  },
638
- "node_modules/@eslint-community/eslint-utils": {
639
- "version": "4.9.0",
640
- "dev": true,
641
- "license": "MIT",
642
- "dependencies": {
643
- "eslint-visitor-keys": "^3.4.3"
644
- },
645
- "engines": {
646
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
647
- },
648
- "funding": {
649
- "url": "https://opencollective.com/eslint"
650
- },
651
- "peerDependencies": {
652
- "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
653
- }
654
- },
655
- "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
656
- "version": "3.4.3",
657
- "dev": true,
658
- "license": "Apache-2.0",
659
- "engines": {
660
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
661
- },
662
- "funding": {
663
- "url": "https://opencollective.com/eslint"
664
- }
665
- },
666
- "node_modules/@eslint-community/regexpp": {
667
- "version": "4.12.2",
668
- "dev": true,
669
- "license": "MIT",
670
- "engines": {
671
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
672
- }
673
- },
674
- "node_modules/@eslint/config-array": {
675
- "version": "0.21.1",
676
- "dev": true,
677
- "license": "Apache-2.0",
678
- "dependencies": {
679
- "@eslint/object-schema": "^2.1.7",
680
- "debug": "^4.3.1",
681
- "minimatch": "^3.1.2"
682
- },
683
- "engines": {
684
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
685
- }
686
- },
687
- "node_modules/@eslint/config-helpers": {
688
- "version": "0.4.2",
689
- "dev": true,
690
- "license": "Apache-2.0",
691
- "dependencies": {
692
- "@eslint/core": "^0.17.0"
693
- },
694
- "engines": {
695
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
696
- }
697
- },
698
- "node_modules/@eslint/core": {
699
- "version": "0.17.0",
700
- "dev": true,
701
- "license": "Apache-2.0",
702
- "dependencies": {
703
- "@types/json-schema": "^7.0.15"
704
- },
705
- "engines": {
706
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
707
- }
708
- },
709
- "node_modules/@eslint/eslintrc": {
710
- "version": "3.3.3",
711
- "dev": true,
712
- "license": "MIT",
713
- "dependencies": {
714
- "ajv": "^6.12.4",
715
- "debug": "^4.3.2",
716
- "espree": "^10.0.1",
717
- "globals": "^14.0.0",
718
- "ignore": "^5.2.0",
719
- "import-fresh": "^3.2.1",
720
- "js-yaml": "^4.1.1",
721
- "minimatch": "^3.1.2",
722
- "strip-json-comments": "^3.1.1"
723
- },
724
- "engines": {
725
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
726
- },
727
- "funding": {
728
- "url": "https://opencollective.com/eslint"
729
- }
730
- },
731
- "node_modules/@eslint/eslintrc/node_modules/globals": {
732
- "version": "14.0.0",
733
- "dev": true,
734
- "license": "MIT",
735
- "engines": {
736
- "node": ">=18"
737
- },
738
- "funding": {
739
- "url": "https://github.com/sponsors/sindresorhus"
740
- }
741
- },
742
- "node_modules/@eslint/js": {
743
- "version": "9.39.2",
744
- "dev": true,
745
- "license": "MIT",
746
- "engines": {
747
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
748
- },
749
- "funding": {
750
- "url": "https://eslint.org/donate"
751
- }
752
- },
753
- "node_modules/@eslint/object-schema": {
754
- "version": "2.1.7",
755
- "dev": true,
756
- "license": "Apache-2.0",
757
- "engines": {
758
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
759
- }
760
- },
761
- "node_modules/@eslint/plugin-kit": {
762
- "version": "0.4.1",
763
- "dev": true,
764
- "license": "Apache-2.0",
765
- "dependencies": {
766
- "@eslint/core": "^0.17.0",
767
- "levn": "^0.4.1"
768
- },
769
- "engines": {
770
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
771
- }
772
- },
773
  "node_modules/@floating-ui/core": {
774
  "version": "1.7.3",
775
  "license": "MIT",
@@ -834,50 +858,6 @@
834
  "sharp": "^0.34.1"
835
  }
836
  },
837
- "node_modules/@humanfs/core": {
838
- "version": "0.19.1",
839
- "dev": true,
840
- "license": "Apache-2.0",
841
- "engines": {
842
- "node": ">=18.18.0"
843
- }
844
- },
845
- "node_modules/@humanfs/node": {
846
- "version": "0.16.7",
847
- "dev": true,
848
- "license": "Apache-2.0",
849
- "dependencies": {
850
- "@humanfs/core": "^0.19.1",
851
- "@humanwhocodes/retry": "^0.4.0"
852
- },
853
- "engines": {
854
- "node": ">=18.18.0"
855
- }
856
- },
857
- "node_modules/@humanwhocodes/module-importer": {
858
- "version": "1.0.1",
859
- "dev": true,
860
- "license": "Apache-2.0",
861
- "engines": {
862
- "node": ">=12.22"
863
- },
864
- "funding": {
865
- "type": "github",
866
- "url": "https://github.com/sponsors/nzakas"
867
- }
868
- },
869
- "node_modules/@humanwhocodes/retry": {
870
- "version": "0.4.3",
871
- "dev": true,
872
- "license": "Apache-2.0",
873
- "engines": {
874
- "node": ">=18.18"
875
- },
876
- "funding": {
877
- "type": "github",
878
- "url": "https://github.com/sponsors/nzakas"
879
- }
880
- },
881
  "node_modules/@img/colour": {
882
  "version": "1.0.0",
883
  "license": "MIT",
@@ -1421,11 +1401,6 @@
1421
  "version": "1.0.8",
1422
  "license": "MIT"
1423
  },
1424
- "node_modules/@types/json-schema": {
1425
- "version": "7.0.15",
1426
- "dev": true,
1427
- "license": "MIT"
1428
- },
1429
  "node_modules/@types/node": {
1430
  "version": "24.10.4",
1431
  "license": "MIT",
@@ -1453,247 +1428,6 @@
1453
  "version": "2.0.6",
1454
  "license": "MIT"
1455
  },
1456
- "node_modules/@typescript-eslint/eslint-plugin": {
1457
- "version": "8.50.1",
1458
- "dev": true,
1459
- "license": "MIT",
1460
- "dependencies": {
1461
- "@eslint-community/regexpp": "^4.10.0",
1462
- "@typescript-eslint/scope-manager": "8.50.1",
1463
- "@typescript-eslint/type-utils": "8.50.1",
1464
- "@typescript-eslint/utils": "8.50.1",
1465
- "@typescript-eslint/visitor-keys": "8.50.1",
1466
- "ignore": "^7.0.0",
1467
- "natural-compare": "^1.4.0",
1468
- "ts-api-utils": "^2.1.0"
1469
- },
1470
- "engines": {
1471
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1472
- },
1473
- "funding": {
1474
- "type": "opencollective",
1475
- "url": "https://opencollective.com/typescript-eslint"
1476
- },
1477
- "peerDependencies": {
1478
- "@typescript-eslint/parser": "^8.50.1",
1479
- "eslint": "^8.57.0 || ^9.0.0",
1480
- "typescript": ">=4.8.4 <6.0.0"
1481
- }
1482
- },
1483
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
1484
- "version": "7.0.5",
1485
- "dev": true,
1486
- "license": "MIT",
1487
- "engines": {
1488
- "node": ">= 4"
1489
- }
1490
- },
1491
- "node_modules/@typescript-eslint/parser": {
1492
- "version": "8.50.1",
1493
- "dev": true,
1494
- "license": "MIT",
1495
- "dependencies": {
1496
- "@typescript-eslint/scope-manager": "8.50.1",
1497
- "@typescript-eslint/types": "8.50.1",
1498
- "@typescript-eslint/typescript-estree": "8.50.1",
1499
- "@typescript-eslint/visitor-keys": "8.50.1",
1500
- "debug": "^4.3.4"
1501
- },
1502
- "engines": {
1503
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1504
- },
1505
- "funding": {
1506
- "type": "opencollective",
1507
- "url": "https://opencollective.com/typescript-eslint"
1508
- },
1509
- "peerDependencies": {
1510
- "eslint": "^8.57.0 || ^9.0.0",
1511
- "typescript": ">=4.8.4 <6.0.0"
1512
- }
1513
- },
1514
- "node_modules/@typescript-eslint/project-service": {
1515
- "version": "8.50.1",
1516
- "dev": true,
1517
- "license": "MIT",
1518
- "dependencies": {
1519
- "@typescript-eslint/tsconfig-utils": "^8.50.1",
1520
- "@typescript-eslint/types": "^8.50.1",
1521
- "debug": "^4.3.4"
1522
- },
1523
- "engines": {
1524
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1525
- },
1526
- "funding": {
1527
- "type": "opencollective",
1528
- "url": "https://opencollective.com/typescript-eslint"
1529
- },
1530
- "peerDependencies": {
1531
- "typescript": ">=4.8.4 <6.0.0"
1532
- }
1533
- },
1534
- "node_modules/@typescript-eslint/scope-manager": {
1535
- "version": "8.50.1",
1536
- "dev": true,
1537
- "license": "MIT",
1538
- "dependencies": {
1539
- "@typescript-eslint/types": "8.50.1",
1540
- "@typescript-eslint/visitor-keys": "8.50.1"
1541
- },
1542
- "engines": {
1543
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1544
- },
1545
- "funding": {
1546
- "type": "opencollective",
1547
- "url": "https://opencollective.com/typescript-eslint"
1548
- }
1549
- },
1550
- "node_modules/@typescript-eslint/tsconfig-utils": {
1551
- "version": "8.50.1",
1552
- "dev": true,
1553
- "license": "MIT",
1554
- "engines": {
1555
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1556
- },
1557
- "funding": {
1558
- "type": "opencollective",
1559
- "url": "https://opencollective.com/typescript-eslint"
1560
- },
1561
- "peerDependencies": {
1562
- "typescript": ">=4.8.4 <6.0.0"
1563
- }
1564
- },
1565
- "node_modules/@typescript-eslint/type-utils": {
1566
- "version": "8.50.1",
1567
- "dev": true,
1568
- "license": "MIT",
1569
- "dependencies": {
1570
- "@typescript-eslint/types": "8.50.1",
1571
- "@typescript-eslint/typescript-estree": "8.50.1",
1572
- "@typescript-eslint/utils": "8.50.1",
1573
- "debug": "^4.3.4",
1574
- "ts-api-utils": "^2.1.0"
1575
- },
1576
- "engines": {
1577
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1578
- },
1579
- "funding": {
1580
- "type": "opencollective",
1581
- "url": "https://opencollective.com/typescript-eslint"
1582
- },
1583
- "peerDependencies": {
1584
- "eslint": "^8.57.0 || ^9.0.0",
1585
- "typescript": ">=4.8.4 <6.0.0"
1586
- }
1587
- },
1588
- "node_modules/@typescript-eslint/types": {
1589
- "version": "8.50.1",
1590
- "dev": true,
1591
- "license": "MIT",
1592
- "engines": {
1593
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1594
- },
1595
- "funding": {
1596
- "type": "opencollective",
1597
- "url": "https://opencollective.com/typescript-eslint"
1598
- }
1599
- },
1600
- "node_modules/@typescript-eslint/typescript-estree": {
1601
- "version": "8.50.1",
1602
- "dev": true,
1603
- "license": "MIT",
1604
- "dependencies": {
1605
- "@typescript-eslint/project-service": "8.50.1",
1606
- "@typescript-eslint/tsconfig-utils": "8.50.1",
1607
- "@typescript-eslint/types": "8.50.1",
1608
- "@typescript-eslint/visitor-keys": "8.50.1",
1609
- "debug": "^4.3.4",
1610
- "minimatch": "^9.0.4",
1611
- "semver": "^7.6.0",
1612
- "tinyglobby": "^0.2.15",
1613
- "ts-api-utils": "^2.1.0"
1614
- },
1615
- "engines": {
1616
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1617
- },
1618
- "funding": {
1619
- "type": "opencollective",
1620
- "url": "https://opencollective.com/typescript-eslint"
1621
- },
1622
- "peerDependencies": {
1623
- "typescript": ">=4.8.4 <6.0.0"
1624
- }
1625
- },
1626
- "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
1627
- "version": "9.0.5",
1628
- "dev": true,
1629
- "license": "ISC",
1630
- "dependencies": {
1631
- "brace-expansion": "^2.0.1"
1632
- },
1633
- "engines": {
1634
- "node": ">=16 || 14 >=14.17"
1635
- },
1636
- "funding": {
1637
- "url": "https://github.com/sponsors/isaacs"
1638
- }
1639
- },
1640
- "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules/brace-expansion": {
1641
- "version": "2.0.2",
1642
- "dev": true,
1643
- "license": "MIT",
1644
- "dependencies": {
1645
- "balanced-match": "^1.0.0"
1646
- }
1647
- },
1648
- "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
1649
- "version": "7.7.3",
1650
- "dev": true,
1651
- "license": "ISC",
1652
- "bin": {
1653
- "semver": "bin/semver.js"
1654
- },
1655
- "engines": {
1656
- "node": ">=10"
1657
- }
1658
- },
1659
- "node_modules/@typescript-eslint/utils": {
1660
- "version": "8.50.1",
1661
- "dev": true,
1662
- "license": "MIT",
1663
- "dependencies": {
1664
- "@eslint-community/eslint-utils": "^4.7.0",
1665
- "@typescript-eslint/scope-manager": "8.50.1",
1666
- "@typescript-eslint/types": "8.50.1",
1667
- "@typescript-eslint/typescript-estree": "8.50.1"
1668
- },
1669
- "engines": {
1670
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1671
- },
1672
- "funding": {
1673
- "type": "opencollective",
1674
- "url": "https://opencollective.com/typescript-eslint"
1675
- },
1676
- "peerDependencies": {
1677
- "eslint": "^8.57.0 || ^9.0.0",
1678
- "typescript": ">=4.8.4 <6.0.0"
1679
- }
1680
- },
1681
- "node_modules/@typescript-eslint/visitor-keys": {
1682
- "version": "8.50.1",
1683
- "dev": true,
1684
- "license": "MIT",
1685
- "dependencies": {
1686
- "@typescript-eslint/types": "8.50.1",
1687
- "eslint-visitor-keys": "^4.2.1"
1688
- },
1689
- "engines": {
1690
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1691
- },
1692
- "funding": {
1693
- "type": "opencollective",
1694
- "url": "https://opencollective.com/typescript-eslint"
1695
- }
1696
- },
1697
  "node_modules/@vitejs/plugin-react": {
1698
  "version": "5.1.2",
1699
  "dev": true,
@@ -1724,25 +1458,6 @@
1724
  "node": ">= 0.6"
1725
  }
1726
  },
1727
- "node_modules/acorn": {
1728
- "version": "8.15.0",
1729
- "dev": true,
1730
- "license": "MIT",
1731
- "bin": {
1732
- "acorn": "bin/acorn"
1733
- },
1734
- "engines": {
1735
- "node": ">=0.4.0"
1736
- }
1737
- },
1738
- "node_modules/acorn-jsx": {
1739
- "version": "5.3.2",
1740
- "dev": true,
1741
- "license": "MIT",
1742
- "peerDependencies": {
1743
- "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
1744
- }
1745
- },
1746
  "node_modules/agent-base": {
1747
  "version": "7.1.4",
1748
  "license": "MIT",
@@ -1750,21 +1465,6 @@
1750
  "node": ">= 14"
1751
  }
1752
  },
1753
- "node_modules/ajv": {
1754
- "version": "6.12.6",
1755
- "dev": true,
1756
- "license": "MIT",
1757
- "dependencies": {
1758
- "fast-deep-equal": "^3.1.1",
1759
- "fast-json-stable-stringify": "^2.0.0",
1760
- "json-schema-traverse": "^0.4.1",
1761
- "uri-js": "^4.2.2"
1762
- },
1763
- "funding": {
1764
- "type": "github",
1765
- "url": "https://github.com/sponsors/epoberezkin"
1766
- }
1767
- },
1768
  "node_modules/ajv-formats": {
1769
  "version": "3.0.1",
1770
  "license": "MIT",
@@ -1842,11 +1542,6 @@
1842
  "node": ">=4"
1843
  }
1844
  },
1845
- "node_modules/balanced-match": {
1846
- "version": "1.0.2",
1847
- "dev": true,
1848
- "license": "MIT"
1849
- },
1850
  "node_modules/baseline-browser-mapping": {
1851
  "version": "2.9.11",
1852
  "license": "Apache-2.0",
@@ -1880,15 +1575,6 @@
1880
  "version": "3.2.0",
1881
  "license": "MIT"
1882
  },
1883
- "node_modules/brace-expansion": {
1884
- "version": "1.1.12",
1885
- "dev": true,
1886
- "license": "MIT",
1887
- "dependencies": {
1888
- "balanced-match": "^1.0.0",
1889
- "concat-map": "0.0.1"
1890
- }
1891
- },
1892
  "node_modules/braces": {
1893
  "version": "3.0.3",
1894
  "license": "MIT",
@@ -2000,21 +1686,6 @@
2000
  ],
2001
  "license": "CC-BY-4.0"
2002
  },
2003
- "node_modules/chalk": {
2004
- "version": "4.1.2",
2005
- "dev": true,
2006
- "license": "MIT",
2007
- "dependencies": {
2008
- "ansi-styles": "^4.1.0",
2009
- "supports-color": "^7.1.0"
2010
- },
2011
- "engines": {
2012
- "node": ">=10"
2013
- },
2014
- "funding": {
2015
- "url": "https://github.com/chalk/chalk?sponsor=1"
2016
- }
2017
- },
2018
  "node_modules/chownr": {
2019
  "version": "3.0.0",
2020
  "license": "BlueOak-1.0.0",
@@ -2160,11 +1831,6 @@
2160
  "node": ">=20"
2161
  }
2162
  },
2163
- "node_modules/concat-map": {
2164
- "version": "0.0.1",
2165
- "dev": true,
2166
- "license": "MIT"
2167
- },
2168
  "node_modules/content-disposition": {
2169
  "version": "1.0.1",
2170
  "license": "MIT",
@@ -2301,11 +1967,6 @@
2301
  }
2302
  }
2303
  },
2304
- "node_modules/deep-is": {
2305
- "version": "0.1.4",
2306
- "dev": true,
2307
- "license": "MIT"
2308
- },
2309
  "node_modules/deepmerge": {
2310
  "version": "4.3.1",
2311
  "license": "MIT",
@@ -2571,179 +2232,15 @@
2571
  "url": "https://github.com/sponsors/sindresorhus"
2572
  }
2573
  },
2574
- "node_modules/eslint": {
2575
- "version": "9.39.2",
2576
- "dev": true,
2577
- "license": "MIT",
2578
- "dependencies": {
2579
- "@eslint-community/eslint-utils": "^4.8.0",
2580
- "@eslint-community/regexpp": "^4.12.1",
2581
- "@eslint/config-array": "^0.21.1",
2582
- "@eslint/config-helpers": "^0.4.2",
2583
- "@eslint/core": "^0.17.0",
2584
- "@eslint/eslintrc": "^3.3.1",
2585
- "@eslint/js": "9.39.2",
2586
- "@eslint/plugin-kit": "^0.4.1",
2587
- "@humanfs/node": "^0.16.6",
2588
- "@humanwhocodes/module-importer": "^1.0.1",
2589
- "@humanwhocodes/retry": "^0.4.2",
2590
- "@types/estree": "^1.0.6",
2591
- "ajv": "^6.12.4",
2592
- "chalk": "^4.0.0",
2593
- "cross-spawn": "^7.0.6",
2594
- "debug": "^4.3.2",
2595
- "escape-string-regexp": "^4.0.0",
2596
- "eslint-scope": "^8.4.0",
2597
- "eslint-visitor-keys": "^4.2.1",
2598
- "espree": "^10.4.0",
2599
- "esquery": "^1.5.0",
2600
- "esutils": "^2.0.2",
2601
- "fast-deep-equal": "^3.1.3",
2602
- "file-entry-cache": "^8.0.0",
2603
- "find-up": "^5.0.0",
2604
- "glob-parent": "^6.0.2",
2605
- "ignore": "^5.2.0",
2606
- "imurmurhash": "^0.1.4",
2607
- "is-glob": "^4.0.0",
2608
- "json-stable-stringify-without-jsonify": "^1.0.1",
2609
- "lodash.merge": "^4.6.2",
2610
- "minimatch": "^3.1.2",
2611
- "natural-compare": "^1.4.0",
2612
- "optionator": "^0.9.3"
2613
- },
2614
- "bin": {
2615
- "eslint": "bin/eslint.js"
2616
- },
2617
- "engines": {
2618
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2619
- },
2620
- "funding": {
2621
- "url": "https://eslint.org/donate"
2622
- },
2623
- "peerDependencies": {
2624
- "jiti": "*"
2625
- },
2626
- "peerDependenciesMeta": {
2627
- "jiti": {
2628
- "optional": true
2629
- }
2630
- }
2631
- },
2632
- "node_modules/eslint-plugin-react-hooks": {
2633
- "version": "7.0.1",
2634
- "dev": true,
2635
- "license": "MIT",
2636
- "dependencies": {
2637
- "@babel/core": "^7.24.4",
2638
- "@babel/parser": "^7.24.4",
2639
- "hermes-parser": "^0.25.1",
2640
- "zod": "^3.25.0 || ^4.0.0",
2641
- "zod-validation-error": "^3.5.0 || ^4.0.0"
2642
- },
2643
- "engines": {
2644
- "node": ">=18"
2645
- },
2646
- "peerDependencies": {
2647
- "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
2648
- }
2649
- },
2650
- "node_modules/eslint-plugin-react-refresh": {
2651
- "version": "0.4.26",
2652
- "dev": true,
2653
- "license": "MIT",
2654
- "peerDependencies": {
2655
- "eslint": ">=8.40"
2656
- }
2657
- },
2658
- "node_modules/eslint-scope": {
2659
- "version": "8.4.0",
2660
- "dev": true,
2661
- "license": "BSD-2-Clause",
2662
- "dependencies": {
2663
- "esrecurse": "^4.3.0",
2664
- "estraverse": "^5.2.0"
2665
- },
2666
- "engines": {
2667
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2668
- },
2669
- "funding": {
2670
- "url": "https://opencollective.com/eslint"
2671
- }
2672
- },
2673
- "node_modules/eslint-visitor-keys": {
2674
- "version": "4.2.1",
2675
- "dev": true,
2676
- "license": "Apache-2.0",
2677
- "engines": {
2678
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2679
- },
2680
- "funding": {
2681
- "url": "https://opencollective.com/eslint"
2682
- }
2683
- },
2684
- "node_modules/espree": {
2685
- "version": "10.4.0",
2686
- "dev": true,
2687
- "license": "BSD-2-Clause",
2688
- "dependencies": {
2689
- "acorn": "^8.15.0",
2690
- "acorn-jsx": "^5.3.2",
2691
- "eslint-visitor-keys": "^4.2.1"
2692
- },
2693
- "engines": {
2694
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2695
- },
2696
- "funding": {
2697
- "url": "https://opencollective.com/eslint"
2698
- }
2699
- },
2700
- "node_modules/esprima": {
2701
- "version": "4.0.1",
2702
- "license": "BSD-2-Clause",
2703
- "bin": {
2704
- "esparse": "bin/esparse.js",
2705
- "esvalidate": "bin/esvalidate.js"
2706
- },
2707
- "engines": {
2708
- "node": ">=4"
2709
- }
2710
- },
2711
- "node_modules/esquery": {
2712
- "version": "1.6.0",
2713
- "dev": true,
2714
- "license": "BSD-3-Clause",
2715
- "dependencies": {
2716
- "estraverse": "^5.1.0"
2717
- },
2718
- "engines": {
2719
- "node": ">=0.10"
2720
- }
2721
- },
2722
- "node_modules/esrecurse": {
2723
- "version": "4.3.0",
2724
- "dev": true,
2725
- "license": "BSD-2-Clause",
2726
- "dependencies": {
2727
- "estraverse": "^5.2.0"
2728
- },
2729
- "engines": {
2730
- "node": ">=4.0"
2731
- }
2732
- },
2733
- "node_modules/estraverse": {
2734
- "version": "5.3.0",
2735
- "dev": true,
2736
- "license": "BSD-2-Clause",
2737
- "engines": {
2738
- "node": ">=4.0"
2739
- }
2740
- },
2741
- "node_modules/esutils": {
2742
- "version": "2.0.3",
2743
- "dev": true,
2744
  "license": "BSD-2-Clause",
 
 
 
 
2745
  "engines": {
2746
- "node": ">=0.10.0"
2747
  }
2748
  },
2749
  "node_modules/etag": {
@@ -2883,16 +2380,6 @@
2883
  "node": ">= 6"
2884
  }
2885
  },
2886
- "node_modules/fast-json-stable-stringify": {
2887
- "version": "2.1.0",
2888
- "dev": true,
2889
- "license": "MIT"
2890
- },
2891
- "node_modules/fast-levenshtein": {
2892
- "version": "2.0.6",
2893
- "dev": true,
2894
- "license": "MIT"
2895
- },
2896
  "node_modules/fast-uri": {
2897
  "version": "3.1.0",
2898
  "funding": [
@@ -2963,17 +2450,6 @@
2963
  "url": "https://github.com/sponsors/sindresorhus"
2964
  }
2965
  },
2966
- "node_modules/file-entry-cache": {
2967
- "version": "8.0.0",
2968
- "dev": true,
2969
- "license": "MIT",
2970
- "dependencies": {
2971
- "flat-cache": "^4.0.0"
2972
- },
2973
- "engines": {
2974
- "node": ">=16.0.0"
2975
- }
2976
- },
2977
  "node_modules/fill-range": {
2978
  "version": "7.1.1",
2979
  "license": "MIT",
@@ -3003,42 +2479,10 @@
3003
  "url": "https://opencollective.com/express"
3004
  }
3005
  },
3006
- "node_modules/find-up": {
3007
- "version": "5.0.0",
3008
- "dev": true,
3009
- "license": "MIT",
3010
- "dependencies": {
3011
- "locate-path": "^6.0.0",
3012
- "path-exists": "^4.0.0"
3013
- },
3014
- "engines": {
3015
- "node": ">=10"
3016
- },
3017
- "funding": {
3018
- "url": "https://github.com/sponsors/sindresorhus"
3019
- }
3020
- },
3021
- "node_modules/flat-cache": {
3022
- "version": "4.0.1",
3023
- "dev": true,
3024
- "license": "MIT",
3025
- "dependencies": {
3026
- "flatted": "^3.2.9",
3027
- "keyv": "^4.5.4"
3028
- },
3029
- "engines": {
3030
- "node": ">=16"
3031
- }
3032
- },
3033
  "node_modules/flatbuffers": {
3034
  "version": "25.9.23",
3035
  "license": "Apache-2.0"
3036
  },
3037
- "node_modules/flatted": {
3038
- "version": "3.3.3",
3039
- "dev": true,
3040
- "license": "ISC"
3041
- },
3042
  "node_modules/formdata-polyfill": {
3043
  "version": "4.0.10",
3044
  "license": "MIT",
@@ -3182,17 +2626,6 @@
3182
  "url": "https://github.com/sponsors/sindresorhus"
3183
  }
3184
  },
3185
- "node_modules/glob-parent": {
3186
- "version": "6.0.2",
3187
- "dev": true,
3188
- "license": "ISC",
3189
- "dependencies": {
3190
- "is-glob": "^4.0.3"
3191
- },
3192
- "engines": {
3193
- "node": ">=10.13.0"
3194
- }
3195
- },
3196
  "node_modules/global-agent": {
3197
  "version": "3.0.0",
3198
  "license": "BSD-3-Clause",
@@ -3268,14 +2701,6 @@
3268
  "version": "1.0.9",
3269
  "license": "ISC"
3270
  },
3271
- "node_modules/has-flag": {
3272
- "version": "4.0.0",
3273
- "dev": true,
3274
- "license": "MIT",
3275
- "engines": {
3276
- "node": ">=8"
3277
- }
3278
- },
3279
  "node_modules/has-property-descriptors": {
3280
  "version": "1.0.2",
3281
  "license": "MIT",
@@ -3310,19 +2735,6 @@
3310
  "version": "4.0.3",
3311
  "license": "MIT"
3312
  },
3313
- "node_modules/hermes-estree": {
3314
- "version": "0.25.1",
3315
- "dev": true,
3316
- "license": "MIT"
3317
- },
3318
- "node_modules/hermes-parser": {
3319
- "version": "0.25.1",
3320
- "dev": true,
3321
- "license": "MIT",
3322
- "dependencies": {
3323
- "hermes-estree": "0.25.1"
3324
- }
3325
- },
3326
  "node_modules/hono": {
3327
  "version": "4.11.2",
3328
  "license": "MIT",
@@ -3402,14 +2814,6 @@
3402
  "url": "https://github.com/sponsors/sindresorhus"
3403
  }
3404
  },
3405
- "node_modules/imurmurhash": {
3406
- "version": "0.1.4",
3407
- "dev": true,
3408
- "license": "MIT",
3409
- "engines": {
3410
- "node": ">=0.8.19"
3411
- }
3412
- },
3413
  "node_modules/inherits": {
3414
  "version": "2.0.4",
3415
  "license": "ISC"
@@ -3618,29 +3022,14 @@
3618
  "node": ">=6"
3619
  }
3620
  },
3621
- "node_modules/json-buffer": {
3622
- "version": "3.0.1",
3623
- "dev": true,
3624
- "license": "MIT"
3625
- },
3626
  "node_modules/json-parse-even-better-errors": {
3627
  "version": "2.3.1",
3628
  "license": "MIT"
3629
  },
3630
- "node_modules/json-schema-traverse": {
3631
- "version": "0.4.1",
3632
- "dev": true,
3633
- "license": "MIT"
3634
- },
3635
  "node_modules/json-schema-typed": {
3636
  "version": "8.0.2",
3637
  "license": "BSD-2-Clause"
3638
  },
3639
- "node_modules/json-stable-stringify-without-jsonify": {
3640
- "version": "1.0.1",
3641
- "dev": true,
3642
- "license": "MIT"
3643
- },
3644
  "node_modules/json-stringify-safe": {
3645
  "version": "5.0.1",
3646
  "license": "ISC"
@@ -3665,14 +3054,6 @@
3665
  "graceful-fs": "^4.1.6"
3666
  }
3667
  },
3668
- "node_modules/keyv": {
3669
- "version": "4.5.4",
3670
- "dev": true,
3671
- "license": "MIT",
3672
- "dependencies": {
3673
- "json-buffer": "3.0.1"
3674
- }
3675
- },
3676
  "node_modules/kleur": {
3677
  "version": "4.1.5",
3678
  "license": "MIT",
@@ -3680,18 +3061,6 @@
3680
  "node": ">=6"
3681
  }
3682
  },
3683
- "node_modules/levn": {
3684
- "version": "0.4.1",
3685
- "dev": true,
3686
- "license": "MIT",
3687
- "dependencies": {
3688
- "prelude-ls": "^1.2.1",
3689
- "type-check": "~0.4.0"
3690
- },
3691
- "engines": {
3692
- "node": ">= 0.8.0"
3693
- }
3694
- },
3695
  "node_modules/lightningcss": {
3696
  "version": "1.30.2",
3697
  "license": "MPL-2.0",
@@ -3741,25 +3110,6 @@
3741
  "version": "1.2.4",
3742
  "license": "MIT"
3743
  },
3744
- "node_modules/locate-path": {
3745
- "version": "6.0.0",
3746
- "dev": true,
3747
- "license": "MIT",
3748
- "dependencies": {
3749
- "p-locate": "^5.0.0"
3750
- },
3751
- "engines": {
3752
- "node": ">=10"
3753
- },
3754
- "funding": {
3755
- "url": "https://github.com/sponsors/sindresorhus"
3756
- }
3757
- },
3758
- "node_modules/lodash.merge": {
3759
- "version": "4.6.2",
3760
- "dev": true,
3761
- "license": "MIT"
3762
- },
3763
  "node_modules/log-symbols": {
3764
  "version": "6.0.0",
3765
  "license": "MIT",
@@ -3920,17 +3270,6 @@
3920
  "url": "https://github.com/sponsors/sindresorhus"
3921
  }
3922
  },
3923
- "node_modules/minimatch": {
3924
- "version": "3.1.2",
3925
- "dev": true,
3926
- "license": "ISC",
3927
- "dependencies": {
3928
- "brace-expansion": "^1.1.7"
3929
- },
3930
- "engines": {
3931
- "node": "*"
3932
- }
3933
- },
3934
  "node_modules/minimist": {
3935
  "version": "1.2.8",
3936
  "license": "MIT",
@@ -4024,11 +3363,6 @@
4024
  "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
4025
  }
4026
  },
4027
- "node_modules/natural-compare": {
4028
- "version": "1.4.0",
4029
- "dev": true,
4030
- "license": "MIT"
4031
- },
4032
  "node_modules/negotiator": {
4033
  "version": "1.0.0",
4034
  "license": "MIT",
@@ -4211,22 +3545,6 @@
4211
  "url": "https://github.com/sponsors/sindresorhus"
4212
  }
4213
  },
4214
- "node_modules/optionator": {
4215
- "version": "0.9.4",
4216
- "dev": true,
4217
- "license": "MIT",
4218
- "dependencies": {
4219
- "deep-is": "^0.1.3",
4220
- "fast-levenshtein": "^2.0.6",
4221
- "levn": "^0.4.1",
4222
- "prelude-ls": "^1.2.1",
4223
- "type-check": "^0.4.0",
4224
- "word-wrap": "^1.2.5"
4225
- },
4226
- "engines": {
4227
- "node": ">= 0.8.0"
4228
- }
4229
- },
4230
  "node_modules/ora": {
4231
  "version": "8.2.0",
4232
  "license": "MIT",
@@ -4262,34 +3580,6 @@
4262
  "version": "1.4.3",
4263
  "license": "MIT"
4264
  },
4265
- "node_modules/p-limit": {
4266
- "version": "3.1.0",
4267
- "dev": true,
4268
- "license": "MIT",
4269
- "dependencies": {
4270
- "yocto-queue": "^0.1.0"
4271
- },
4272
- "engines": {
4273
- "node": ">=10"
4274
- },
4275
- "funding": {
4276
- "url": "https://github.com/sponsors/sindresorhus"
4277
- }
4278
- },
4279
- "node_modules/p-locate": {
4280
- "version": "5.0.0",
4281
- "dev": true,
4282
- "license": "MIT",
4283
- "dependencies": {
4284
- "p-limit": "^3.0.2"
4285
- },
4286
- "engines": {
4287
- "node": ">=10"
4288
- },
4289
- "funding": {
4290
- "url": "https://github.com/sponsors/sindresorhus"
4291
- }
4292
- },
4293
  "node_modules/package-manager-detector": {
4294
  "version": "1.6.0",
4295
  "license": "MIT"
@@ -4341,14 +3631,6 @@
4341
  "version": "1.0.1",
4342
  "license": "MIT"
4343
  },
4344
- "node_modules/path-exists": {
4345
- "version": "4.0.0",
4346
- "dev": true,
4347
- "license": "MIT",
4348
- "engines": {
4349
- "node": ">=8"
4350
- }
4351
- },
4352
  "node_modules/path-key": {
4353
  "version": "3.1.1",
4354
  "license": "MIT",
@@ -4432,14 +3714,6 @@
4432
  "url": "https://github.com/sponsors/sindresorhus"
4433
  }
4434
  },
4435
- "node_modules/prelude-ls": {
4436
- "version": "1.2.1",
4437
- "dev": true,
4438
- "license": "MIT",
4439
- "engines": {
4440
- "node": ">= 0.8.0"
4441
- }
4442
- },
4443
  "node_modules/pretty-ms": {
4444
  "version": "9.3.0",
4445
  "license": "MIT",
@@ -4504,14 +3778,6 @@
4504
  "node": ">= 0.10"
4505
  }
4506
  },
4507
- "node_modules/punycode": {
4508
- "version": "2.3.1",
4509
- "dev": true,
4510
- "license": "MIT",
4511
- "engines": {
4512
- "node": ">=6"
4513
- }
4514
- },
4515
  "node_modules/qs": {
4516
  "version": "6.14.0",
4517
  "license": "BSD-3-Clause",
@@ -5154,28 +4420,6 @@
5154
  "url": "https://github.com/sponsors/sindresorhus"
5155
  }
5156
  },
5157
- "node_modules/strip-json-comments": {
5158
- "version": "3.1.1",
5159
- "dev": true,
5160
- "license": "MIT",
5161
- "engines": {
5162
- "node": ">=8"
5163
- },
5164
- "funding": {
5165
- "url": "https://github.com/sponsors/sindresorhus"
5166
- }
5167
- },
5168
- "node_modules/supports-color": {
5169
- "version": "7.2.0",
5170
- "dev": true,
5171
- "license": "MIT",
5172
- "dependencies": {
5173
- "has-flag": "^4.0.0"
5174
- },
5175
- "engines": {
5176
- "node": ">=8"
5177
- }
5178
- },
5179
  "node_modules/tabbable": {
5180
  "version": "6.3.0",
5181
  "license": "MIT"
@@ -5293,17 +4537,6 @@
5293
  "node": ">=16"
5294
  }
5295
  },
5296
- "node_modules/ts-api-utils": {
5297
- "version": "2.1.0",
5298
- "dev": true,
5299
- "license": "MIT",
5300
- "engines": {
5301
- "node": ">=18.12"
5302
- },
5303
- "peerDependencies": {
5304
- "typescript": ">=4.8.4"
5305
- }
5306
- },
5307
  "node_modules/ts-morph": {
5308
  "version": "26.0.0",
5309
  "license": "MIT",
@@ -5335,17 +4568,6 @@
5335
  "url": "https://github.com/sponsors/Wombosvideo"
5336
  }
5337
  },
5338
- "node_modules/type-check": {
5339
- "version": "0.4.0",
5340
- "dev": true,
5341
- "license": "MIT",
5342
- "dependencies": {
5343
- "prelude-ls": "^1.2.1"
5344
- },
5345
- "engines": {
5346
- "node": ">= 0.8.0"
5347
- }
5348
- },
5349
  "node_modules/type-fest": {
5350
  "version": "5.3.1",
5351
  "license": "(MIT OR CC0-1.0)",
@@ -5383,28 +4605,6 @@
5383
  "node": ">=14.17"
5384
  }
5385
  },
5386
- "node_modules/typescript-eslint": {
5387
- "version": "8.50.1",
5388
- "dev": true,
5389
- "license": "MIT",
5390
- "dependencies": {
5391
- "@typescript-eslint/eslint-plugin": "8.50.1",
5392
- "@typescript-eslint/parser": "8.50.1",
5393
- "@typescript-eslint/typescript-estree": "8.50.1",
5394
- "@typescript-eslint/utils": "8.50.1"
5395
- },
5396
- "engines": {
5397
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
5398
- },
5399
- "funding": {
5400
- "type": "opencollective",
5401
- "url": "https://opencollective.com/typescript-eslint"
5402
- },
5403
- "peerDependencies": {
5404
- "eslint": "^8.57.0 || ^9.0.0",
5405
- "typescript": ">=4.8.4 <6.0.0"
5406
- }
5407
- },
5408
  "node_modules/undici-types": {
5409
  "version": "7.16.0",
5410
  "license": "MIT"
@@ -5468,14 +4668,6 @@
5468
  "browserslist": ">= 4.21.0"
5469
  }
5470
  },
5471
- "node_modules/uri-js": {
5472
- "version": "4.4.1",
5473
- "dev": true,
5474
- "license": "BSD-2-Clause",
5475
- "dependencies": {
5476
- "punycode": "^2.1.0"
5477
- }
5478
- },
5479
  "node_modules/use-sync-external-store": {
5480
  "version": "1.6.0",
5481
  "license": "MIT",
@@ -5586,14 +4778,6 @@
5586
  "node": ">= 8"
5587
  }
5588
  },
5589
- "node_modules/word-wrap": {
5590
- "version": "1.2.5",
5591
- "dev": true,
5592
- "license": "MIT",
5593
- "engines": {
5594
- "node": ">=0.10.0"
5595
- }
5596
- },
5597
  "node_modules/wrap-ansi": {
5598
  "version": "6.2.0",
5599
  "license": "MIT",
@@ -5727,17 +4911,6 @@
5727
  "node": ">=8"
5728
  }
5729
  },
5730
- "node_modules/yocto-queue": {
5731
- "version": "0.1.0",
5732
- "dev": true,
5733
- "license": "MIT",
5734
- "engines": {
5735
- "node": ">=10"
5736
- },
5737
- "funding": {
5738
- "url": "https://github.com/sponsors/sindresorhus"
5739
- }
5740
- },
5741
  "node_modules/yoctocolors": {
5742
  "version": "2.1.2",
5743
  "license": "MIT",
@@ -5772,17 +4945,6 @@
5772
  "zod": "^3.25 || ^4"
5773
  }
5774
  },
5775
- "node_modules/zod-validation-error": {
5776
- "version": "4.0.2",
5777
- "dev": true,
5778
- "license": "MIT",
5779
- "engines": {
5780
- "node": ">=18.0.0"
5781
- },
5782
- "peerDependencies": {
5783
- "zod": "^3.25.0 || ^4.0.0"
5784
- }
5785
- },
5786
  "node_modules/zustand": {
5787
  "version": "5.0.9",
5788
  "license": "MIT",
 
25
  "zustand": "^5.0.2"
26
  },
27
  "devDependencies": {
28
+ "@biomejs/biome": "^2.3.10",
29
  "@types/node": "^24.10.1",
30
  "@types/react": "^19.2.5",
31
  "@types/react-dom": "^19.2.3",
32
  "@vitejs/plugin-react": "^5.1.1",
 
 
 
33
  "globals": "^16.5.0",
34
  "typescript": "~5.9.3",
 
35
  "vite": "^7.2.4"
36
  }
37
  },
 
488
  }
489
  }
490
  },
491
+ "node_modules/@biomejs/biome": {
492
+ "version": "2.3.10",
493
+ "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.10.tgz",
494
+ "integrity": "sha512-/uWSUd1MHX2fjqNLHNL6zLYWBbrJeG412/8H7ESuK8ewoRoMPUgHDebqKrPTx/5n6f17Xzqc9hdg3MEqA5hXnQ==",
495
+ "dev": true,
496
+ "license": "MIT OR Apache-2.0",
497
+ "bin": {
498
+ "biome": "bin/biome"
499
+ },
500
+ "engines": {
501
+ "node": ">=14.21.3"
502
+ },
503
+ "funding": {
504
+ "type": "opencollective",
505
+ "url": "https://opencollective.com/biome"
506
+ },
507
+ "optionalDependencies": {
508
+ "@biomejs/cli-darwin-arm64": "2.3.10",
509
+ "@biomejs/cli-darwin-x64": "2.3.10",
510
+ "@biomejs/cli-linux-arm64": "2.3.10",
511
+ "@biomejs/cli-linux-arm64-musl": "2.3.10",
512
+ "@biomejs/cli-linux-x64": "2.3.10",
513
+ "@biomejs/cli-linux-x64-musl": "2.3.10",
514
+ "@biomejs/cli-win32-arm64": "2.3.10",
515
+ "@biomejs/cli-win32-x64": "2.3.10"
516
+ }
517
+ },
518
+ "node_modules/@biomejs/cli-darwin-arm64": {
519
+ "version": "2.3.10",
520
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.10.tgz",
521
+ "integrity": "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w==",
522
+ "cpu": [
523
+ "arm64"
524
+ ],
525
+ "dev": true,
526
+ "license": "MIT OR Apache-2.0",
527
+ "optional": true,
528
+ "os": [
529
+ "darwin"
530
+ ],
531
+ "engines": {
532
+ "node": ">=14.21.3"
533
+ }
534
+ },
535
+ "node_modules/@biomejs/cli-darwin-x64": {
536
+ "version": "2.3.10",
537
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.10.tgz",
538
+ "integrity": "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg==",
539
+ "cpu": [
540
+ "x64"
541
+ ],
542
+ "dev": true,
543
+ "license": "MIT OR Apache-2.0",
544
+ "optional": true,
545
+ "os": [
546
+ "darwin"
547
+ ],
548
+ "engines": {
549
+ "node": ">=14.21.3"
550
+ }
551
+ },
552
+ "node_modules/@biomejs/cli-linux-arm64": {
553
+ "version": "2.3.10",
554
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.10.tgz",
555
+ "integrity": "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA==",
556
+ "cpu": [
557
+ "arm64"
558
+ ],
559
+ "dev": true,
560
+ "license": "MIT OR Apache-2.0",
561
+ "optional": true,
562
+ "os": [
563
+ "linux"
564
+ ],
565
+ "engines": {
566
+ "node": ">=14.21.3"
567
+ }
568
+ },
569
+ "node_modules/@biomejs/cli-linux-arm64-musl": {
570
+ "version": "2.3.10",
571
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.10.tgz",
572
+ "integrity": "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A==",
573
+ "cpu": [
574
+ "arm64"
575
+ ],
576
+ "dev": true,
577
+ "license": "MIT OR Apache-2.0",
578
+ "optional": true,
579
+ "os": [
580
+ "linux"
581
+ ],
582
+ "engines": {
583
+ "node": ">=14.21.3"
584
+ }
585
+ },
586
+ "node_modules/@biomejs/cli-linux-x64": {
587
+ "version": "2.3.10",
588
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.10.tgz",
589
+ "integrity": "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw==",
590
+ "cpu": [
591
+ "x64"
592
+ ],
593
+ "dev": true,
594
+ "license": "MIT OR Apache-2.0",
595
+ "optional": true,
596
+ "os": [
597
+ "linux"
598
+ ],
599
+ "engines": {
600
+ "node": ">=14.21.3"
601
+ }
602
+ },
603
+ "node_modules/@biomejs/cli-linux-x64-musl": {
604
+ "version": "2.3.10",
605
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.10.tgz",
606
+ "integrity": "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g==",
607
+ "cpu": [
608
+ "x64"
609
+ ],
610
+ "dev": true,
611
+ "license": "MIT OR Apache-2.0",
612
+ "optional": true,
613
+ "os": [
614
+ "linux"
615
+ ],
616
+ "engines": {
617
+ "node": ">=14.21.3"
618
+ }
619
+ },
620
+ "node_modules/@biomejs/cli-win32-arm64": {
621
+ "version": "2.3.10",
622
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.10.tgz",
623
+ "integrity": "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ==",
624
+ "cpu": [
625
+ "arm64"
626
+ ],
627
+ "dev": true,
628
+ "license": "MIT OR Apache-2.0",
629
+ "optional": true,
630
+ "os": [
631
+ "win32"
632
+ ],
633
+ "engines": {
634
+ "node": ">=14.21.3"
635
+ }
636
+ },
637
+ "node_modules/@biomejs/cli-win32-x64": {
638
+ "version": "2.3.10",
639
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.10.tgz",
640
+ "integrity": "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ==",
641
+ "cpu": [
642
+ "x64"
643
+ ],
644
+ "dev": true,
645
+ "license": "MIT OR Apache-2.0",
646
+ "optional": true,
647
+ "os": [
648
+ "win32"
649
+ ],
650
+ "engines": {
651
+ "node": ">=14.21.3"
652
+ }
653
+ },
654
  "node_modules/@dotenvx/dotenvx": {
655
  "version": "1.51.2",
656
  "license": "BSD-3-Clause",
 
794
  "node": ">=18"
795
  }
796
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
797
  "node_modules/@floating-ui/core": {
798
  "version": "1.7.3",
799
  "license": "MIT",
 
858
  "sharp": "^0.34.1"
859
  }
860
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  "node_modules/@img/colour": {
862
  "version": "1.0.0",
863
  "license": "MIT",
 
1401
  "version": "1.0.8",
1402
  "license": "MIT"
1403
  },
 
 
 
 
 
1404
  "node_modules/@types/node": {
1405
  "version": "24.10.4",
1406
  "license": "MIT",
 
1428
  "version": "2.0.6",
1429
  "license": "MIT"
1430
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1431
  "node_modules/@vitejs/plugin-react": {
1432
  "version": "5.1.2",
1433
  "dev": true,
 
1458
  "node": ">= 0.6"
1459
  }
1460
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1461
  "node_modules/agent-base": {
1462
  "version": "7.1.4",
1463
  "license": "MIT",
 
1465
  "node": ">= 14"
1466
  }
1467
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1468
  "node_modules/ajv-formats": {
1469
  "version": "3.0.1",
1470
  "license": "MIT",
 
1542
  "node": ">=4"
1543
  }
1544
  },
 
 
 
 
 
1545
  "node_modules/baseline-browser-mapping": {
1546
  "version": "2.9.11",
1547
  "license": "Apache-2.0",
 
1575
  "version": "3.2.0",
1576
  "license": "MIT"
1577
  },
 
 
 
 
 
 
 
 
 
1578
  "node_modules/braces": {
1579
  "version": "3.0.3",
1580
  "license": "MIT",
 
1686
  ],
1687
  "license": "CC-BY-4.0"
1688
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1689
  "node_modules/chownr": {
1690
  "version": "3.0.0",
1691
  "license": "BlueOak-1.0.0",
 
1831
  "node": ">=20"
1832
  }
1833
  },
 
 
 
 
 
1834
  "node_modules/content-disposition": {
1835
  "version": "1.0.1",
1836
  "license": "MIT",
 
1967
  }
1968
  }
1969
  },
 
 
 
 
 
1970
  "node_modules/deepmerge": {
1971
  "version": "4.3.1",
1972
  "license": "MIT",
 
2232
  "url": "https://github.com/sponsors/sindresorhus"
2233
  }
2234
  },
2235
+ "node_modules/esprima": {
2236
+ "version": "4.0.1",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2237
  "license": "BSD-2-Clause",
2238
+ "bin": {
2239
+ "esparse": "bin/esparse.js",
2240
+ "esvalidate": "bin/esvalidate.js"
2241
+ },
2242
  "engines": {
2243
+ "node": ">=4"
2244
  }
2245
  },
2246
  "node_modules/etag": {
 
2380
  "node": ">= 6"
2381
  }
2382
  },
 
 
 
 
 
 
 
 
 
 
2383
  "node_modules/fast-uri": {
2384
  "version": "3.1.0",
2385
  "funding": [
 
2450
  "url": "https://github.com/sponsors/sindresorhus"
2451
  }
2452
  },
 
 
 
 
 
 
 
 
 
 
 
2453
  "node_modules/fill-range": {
2454
  "version": "7.1.1",
2455
  "license": "MIT",
 
2479
  "url": "https://opencollective.com/express"
2480
  }
2481
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2482
  "node_modules/flatbuffers": {
2483
  "version": "25.9.23",
2484
  "license": "Apache-2.0"
2485
  },
 
 
 
 
 
2486
  "node_modules/formdata-polyfill": {
2487
  "version": "4.0.10",
2488
  "license": "MIT",
 
2626
  "url": "https://github.com/sponsors/sindresorhus"
2627
  }
2628
  },
 
 
 
 
 
 
 
 
 
 
 
2629
  "node_modules/global-agent": {
2630
  "version": "3.0.0",
2631
  "license": "BSD-3-Clause",
 
2701
  "version": "1.0.9",
2702
  "license": "ISC"
2703
  },
 
 
 
 
 
 
 
 
2704
  "node_modules/has-property-descriptors": {
2705
  "version": "1.0.2",
2706
  "license": "MIT",
 
2735
  "version": "4.0.3",
2736
  "license": "MIT"
2737
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
2738
  "node_modules/hono": {
2739
  "version": "4.11.2",
2740
  "license": "MIT",
 
2814
  "url": "https://github.com/sponsors/sindresorhus"
2815
  }
2816
  },
 
 
 
 
 
 
 
 
2817
  "node_modules/inherits": {
2818
  "version": "2.0.4",
2819
  "license": "ISC"
 
3022
  "node": ">=6"
3023
  }
3024
  },
 
 
 
 
 
3025
  "node_modules/json-parse-even-better-errors": {
3026
  "version": "2.3.1",
3027
  "license": "MIT"
3028
  },
 
 
 
 
 
3029
  "node_modules/json-schema-typed": {
3030
  "version": "8.0.2",
3031
  "license": "BSD-2-Clause"
3032
  },
 
 
 
 
 
3033
  "node_modules/json-stringify-safe": {
3034
  "version": "5.0.1",
3035
  "license": "ISC"
 
3054
  "graceful-fs": "^4.1.6"
3055
  }
3056
  },
 
 
 
 
 
 
 
 
3057
  "node_modules/kleur": {
3058
  "version": "4.1.5",
3059
  "license": "MIT",
 
3061
  "node": ">=6"
3062
  }
3063
  },
 
 
 
 
 
 
 
 
 
 
 
 
3064
  "node_modules/lightningcss": {
3065
  "version": "1.30.2",
3066
  "license": "MPL-2.0",
 
3110
  "version": "1.2.4",
3111
  "license": "MIT"
3112
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3113
  "node_modules/log-symbols": {
3114
  "version": "6.0.0",
3115
  "license": "MIT",
 
3270
  "url": "https://github.com/sponsors/sindresorhus"
3271
  }
3272
  },
 
 
 
 
 
 
 
 
 
 
 
3273
  "node_modules/minimist": {
3274
  "version": "1.2.8",
3275
  "license": "MIT",
 
3363
  "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
3364
  }
3365
  },
 
 
 
 
 
3366
  "node_modules/negotiator": {
3367
  "version": "1.0.0",
3368
  "license": "MIT",
 
3545
  "url": "https://github.com/sponsors/sindresorhus"
3546
  }
3547
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3548
  "node_modules/ora": {
3549
  "version": "8.2.0",
3550
  "license": "MIT",
 
3580
  "version": "1.4.3",
3581
  "license": "MIT"
3582
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3583
  "node_modules/package-manager-detector": {
3584
  "version": "1.6.0",
3585
  "license": "MIT"
 
3631
  "version": "1.0.1",
3632
  "license": "MIT"
3633
  },
 
 
 
 
 
 
 
 
3634
  "node_modules/path-key": {
3635
  "version": "3.1.1",
3636
  "license": "MIT",
 
3714
  "url": "https://github.com/sponsors/sindresorhus"
3715
  }
3716
  },
 
 
 
 
 
 
 
 
3717
  "node_modules/pretty-ms": {
3718
  "version": "9.3.0",
3719
  "license": "MIT",
 
3778
  "node": ">= 0.10"
3779
  }
3780
  },
 
 
 
 
 
 
 
 
3781
  "node_modules/qs": {
3782
  "version": "6.14.0",
3783
  "license": "BSD-3-Clause",
 
4420
  "url": "https://github.com/sponsors/sindresorhus"
4421
  }
4422
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4423
  "node_modules/tabbable": {
4424
  "version": "6.3.0",
4425
  "license": "MIT"
 
4537
  "node": ">=16"
4538
  }
4539
  },
 
 
 
 
 
 
 
 
 
 
 
4540
  "node_modules/ts-morph": {
4541
  "version": "26.0.0",
4542
  "license": "MIT",
 
4568
  "url": "https://github.com/sponsors/Wombosvideo"
4569
  }
4570
  },
 
 
 
 
 
 
 
 
 
 
 
4571
  "node_modules/type-fest": {
4572
  "version": "5.3.1",
4573
  "license": "(MIT OR CC0-1.0)",
 
4605
  "node": ">=14.17"
4606
  }
4607
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4608
  "node_modules/undici-types": {
4609
  "version": "7.16.0",
4610
  "license": "MIT"
 
4668
  "browserslist": ">= 4.21.0"
4669
  }
4670
  },
 
 
 
 
 
 
 
 
4671
  "node_modules/use-sync-external-store": {
4672
  "version": "1.6.0",
4673
  "license": "MIT",
 
4778
  "node": ">= 8"
4779
  }
4780
  },
 
 
 
 
 
 
 
 
4781
  "node_modules/wrap-ansi": {
4782
  "version": "6.2.0",
4783
  "license": "MIT",
 
4911
  "node": ">=8"
4912
  }
4913
  },
 
 
 
 
 
 
 
 
 
 
 
4914
  "node_modules/yoctocolors": {
4915
  "version": "2.1.2",
4916
  "license": "MIT",
 
4945
  "zod": "^3.25 || ^4"
4946
  }
4947
  },
 
 
 
 
 
 
 
 
 
 
 
4948
  "node_modules/zustand": {
4949
  "version": "5.0.9",
4950
  "license": "MIT",
package.json CHANGED
@@ -6,8 +6,11 @@
6
  "scripts": {
7
  "dev": "vite",
8
  "build": "tsc -b && vite build",
9
- "lint": "eslint .",
10
- "preview": "vite preview"
 
 
 
11
  },
12
  "dependencies": {
13
  "@base-ui/react": "^1.0.0",
@@ -27,17 +30,13 @@
27
  "zustand": "^5.0.2"
28
  },
29
  "devDependencies": {
30
- "@eslint/js": "^9.39.1",
31
  "@types/node": "^24.10.1",
32
  "@types/react": "^19.2.5",
33
  "@types/react-dom": "^19.2.3",
34
  "@vitejs/plugin-react": "^5.1.1",
35
- "eslint": "^9.39.1",
36
- "eslint-plugin-react-hooks": "^7.0.1",
37
- "eslint-plugin-react-refresh": "^0.4.24",
38
  "globals": "^16.5.0",
39
  "typescript": "~5.9.3",
40
- "typescript-eslint": "^8.46.4",
41
  "vite": "^7.2.4"
42
  }
43
  }
 
6
  "scripts": {
7
  "dev": "vite",
8
  "build": "tsc -b && vite build",
9
+ "preview": "vite preview",
10
+ "typecheck": "tsc --noEmit",
11
+ "lint": "biome lint ./src",
12
+ "format": "biome format --write ./src",
13
+ "check": "biome check --write ./src"
14
  },
15
  "dependencies": {
16
  "@base-ui/react": "^1.0.0",
 
30
  "zustand": "^5.0.2"
31
  },
32
  "devDependencies": {
33
+ "@biomejs/biome": "^2.3.10",
34
  "@types/node": "^24.10.1",
35
  "@types/react": "^19.2.5",
36
  "@types/react-dom": "^19.2.3",
37
  "@vitejs/plugin-react": "^5.1.1",
 
 
 
38
  "globals": "^16.5.0",
39
  "typescript": "~5.9.3",
 
40
  "vite": "^7.2.4"
41
  }
42
  }
src/App.tsx CHANGED
@@ -1,7 +1,7 @@
1
  import { AgenticInterface } from "@/components/AgenticInterface";
2
 
3
  export function App() {
4
- return <AgenticInterface />;
5
  }
6
 
7
- export default App;
 
1
  import { AgenticInterface } from "@/components/AgenticInterface";
2
 
3
  export function App() {
4
+ return <AgenticInterface />;
5
  }
6
 
7
+ export default App;
src/components/AgenticInterface.tsx CHANGED
@@ -1,18 +1,18 @@
1
- import { useColorStore } from "@/store/useColorStore"
2
- import { useTranslator } from "@/hooks/useTranslator"
3
- import { ChatSidebar } from "./ChatSidebar"
4
- import { ColorVisualizer } from "./ColorVisualizer"
5
- import { ColorControlForm } from "./ColorControlForm"
6
- import type { Message } from "./MessageBubble"
7
 
8
  const MESSAGES: readonly Message[] = [
9
  { id: 1, text: "Hi, how can I help you?", sender: "bot" },
10
  { id: 2, text: "Change the square color to green", sender: "user" },
11
- ]
12
 
13
  export function AgenticInterface() {
14
- const { squareColor, setSquareColor } = useColorStore()
15
- const { translate, isLoading } = useTranslator()
16
 
17
  return (
18
  <div className="flex flex-col md:flex-row h-screen w-screen overflow-hidden bg-background text-foreground font-sans selection:bg-primary/30">
@@ -34,5 +34,5 @@ export function AgenticInterface() {
34
  </div>
35
  </main>
36
  </div>
37
- )
38
  }
 
1
+ import { useTranslator } from "@/hooks/useTranslator";
2
+ import { useColorStore } from "@/store/useColorStore";
3
+ import { ChatSidebar } from "./ChatSidebar";
4
+ import { ColorControlForm } from "./ColorControlForm";
5
+ import { ColorVisualizer } from "./ColorVisualizer";
6
+ import type { Message } from "./MessageBubble";
7
 
8
  const MESSAGES: readonly Message[] = [
9
  { id: 1, text: "Hi, how can I help you?", sender: "bot" },
10
  { id: 2, text: "Change the square color to green", sender: "user" },
11
+ ];
12
 
13
  export function AgenticInterface() {
14
+ const { squareColor, setSquareColor } = useColorStore();
15
+ const { translate, isLoading } = useTranslator();
16
 
17
  return (
18
  <div className="flex flex-col md:flex-row h-screen w-screen overflow-hidden bg-background text-foreground font-sans selection:bg-primary/30">
 
34
  </div>
35
  </main>
36
  </div>
37
+ );
38
  }
src/components/ChatSidebar.tsx CHANGED
@@ -1,15 +1,15 @@
1
- import { IconSend } from "@tabler/icons-react"
2
  import {
3
  InputGroup,
4
- InputGroupInput,
5
  InputGroupButton,
6
- } from "@/components/ui/input-group"
7
- import { SidebarHeader } from "./SidebarHeader"
8
- import { MessageBubble, type Message } from "./MessageBubble"
 
9
 
10
  type ChatSidebarProps = {
11
- messages: readonly Message[]
12
- }
13
 
14
  export function ChatSidebar({ messages }: ChatSidebarProps) {
15
  return (
@@ -38,6 +38,5 @@ export function ChatSidebar({ messages }: ChatSidebarProps) {
38
  </InputGroup>
39
  </footer>
40
  </aside>
41
- )
42
  }
43
-
 
1
+ import { IconSend } from "@tabler/icons-react";
2
  import {
3
  InputGroup,
 
4
  InputGroupButton,
5
+ InputGroupInput,
6
+ } from "@/components/ui/input-group";
7
+ import { type Message, MessageBubble } from "./MessageBubble";
8
+ import { SidebarHeader } from "./SidebarHeader";
9
 
10
  type ChatSidebarProps = {
11
+ messages: readonly Message[];
12
+ };
13
 
14
  export function ChatSidebar({ messages }: ChatSidebarProps) {
15
  return (
 
38
  </InputGroup>
39
  </footer>
40
  </aside>
41
+ );
42
  }
 
src/components/ColorControlForm.tsx CHANGED
@@ -1,49 +1,49 @@
1
- import { useState, type FormEvent } from "react"
2
- import { IconArrowRight, IconLoader2 } from "@tabler/icons-react"
3
  import {
4
  InputGroup,
5
- InputGroupInput,
6
  InputGroupButton,
7
- } from "@/components/ui/input-group"
 
8
  import {
9
  Select,
10
  SelectContent,
11
  SelectItem,
12
  SelectTrigger,
13
  SelectValue,
14
- } from "@/components/ui/select"
15
  import {
16
  DEFAULT_SOURCE_LANG,
17
- SOURCE_LANGUAGES,
18
  isSourceLanguage,
19
- sourceLanguageLabel,
20
  type SourceLanguage,
21
- } from "@/lib/translationLanguages"
 
22
 
23
  type ColorControlFormProps = {
24
- onColorChange: (color: string) => void
25
- translate: (text: string, srcLang: string) => Promise<string>
26
- isLoading: boolean
27
- }
28
 
29
  export function ColorControlForm({
30
  onColorChange,
31
  translate,
32
  isLoading,
33
  }: ColorControlFormProps) {
34
- const [inputValue, setInputValue] = useState("")
35
  const [sourceLang, setSourceLang] =
36
- useState<SourceLanguage>(DEFAULT_SOURCE_LANG)
37
 
38
  const handleSubmit = async (e?: FormEvent) => {
39
- e?.preventDefault()
40
- const trimmedInput = inputValue.trim()
41
- if (!trimmedInput) return
42
 
43
- const translated = await translate(trimmedInput, sourceLang)
44
- onColorChange(translated.trim().toLowerCase())
45
- setInputValue("")
46
- }
47
 
48
  return (
49
  <form onSubmit={handleSubmit} className="w-full space-y-3">
@@ -54,7 +54,7 @@ export function ColorControlForm({
54
  <Select
55
  value={sourceLang}
56
  onValueChange={(value) => {
57
- if (isSourceLanguage(value)) setSourceLang(value)
58
  }}
59
  >
60
  <SelectTrigger className="w-full justify-between">
@@ -93,6 +93,5 @@ export function ColorControlForm({
93
  </InputGroupButton>
94
  </InputGroup>
95
  </form>
96
- )
97
  }
98
-
 
1
+ import { IconArrowRight, IconLoader2 } from "@tabler/icons-react";
2
+ import { type FormEvent, useState } from "react";
3
  import {
4
  InputGroup,
 
5
  InputGroupButton,
6
+ InputGroupInput,
7
+ } from "@/components/ui/input-group";
8
  import {
9
  Select,
10
  SelectContent,
11
  SelectItem,
12
  SelectTrigger,
13
  SelectValue,
14
+ } from "@/components/ui/select";
15
  import {
16
  DEFAULT_SOURCE_LANG,
 
17
  isSourceLanguage,
18
+ SOURCE_LANGUAGES,
19
  type SourceLanguage,
20
+ sourceLanguageLabel,
21
+ } from "@/lib/translationLanguages";
22
 
23
  type ColorControlFormProps = {
24
+ onColorChange: (color: string) => void;
25
+ translate: (text: string, srcLang: string) => Promise<string>;
26
+ isLoading: boolean;
27
+ };
28
 
29
  export function ColorControlForm({
30
  onColorChange,
31
  translate,
32
  isLoading,
33
  }: ColorControlFormProps) {
34
+ const [inputValue, setInputValue] = useState("");
35
  const [sourceLang, setSourceLang] =
36
+ useState<SourceLanguage>(DEFAULT_SOURCE_LANG);
37
 
38
  const handleSubmit = async (e?: FormEvent) => {
39
+ e?.preventDefault();
40
+ const trimmedInput = inputValue.trim();
41
+ if (!trimmedInput) return;
42
 
43
+ const translated = await translate(trimmedInput, sourceLang);
44
+ onColorChange(translated.trim().toLowerCase());
45
+ setInputValue("");
46
+ };
47
 
48
  return (
49
  <form onSubmit={handleSubmit} className="w-full space-y-3">
 
54
  <Select
55
  value={sourceLang}
56
  onValueChange={(value) => {
57
+ if (isSourceLanguage(value)) setSourceLang(value);
58
  }}
59
  >
60
  <SelectTrigger className="w-full justify-between">
 
93
  </InputGroupButton>
94
  </InputGroup>
95
  </form>
96
+ );
97
  }
 
src/components/ColorVisualizer.tsx CHANGED
@@ -1,6 +1,6 @@
1
  type ColorVisualizerProps = {
2
- color: string
3
- }
4
 
5
  export function ColorVisualizer({ color }: ColorVisualizerProps) {
6
  return (
@@ -20,6 +20,5 @@ export function ColorVisualizer({ color }: ColorVisualizerProps) {
20
  <div className="w-1/2 h-1/2 bg-white/10 rounded-full blur-3xl animate-pulse" />
21
  </div>
22
  </div>
23
- )
24
  }
25
-
 
1
  type ColorVisualizerProps = {
2
+ color: string;
3
+ };
4
 
5
  export function ColorVisualizer({ color }: ColorVisualizerProps) {
6
  return (
 
20
  <div className="w-1/2 h-1/2 bg-white/10 rounded-full blur-3xl animate-pulse" />
21
  </div>
22
  </div>
23
+ );
24
  }
 
src/components/MessageBubble.tsx CHANGED
@@ -1,19 +1,19 @@
1
- import { IconUser, IconRobot } from "@tabler/icons-react"
2
 
3
- type MessageSender = "user" | "bot"
4
 
5
  type Message = {
6
- id: number
7
- text: string
8
- sender: MessageSender
9
- }
10
 
11
  type MessageBubbleProps = {
12
- message: Message
13
- }
14
 
15
  export function MessageBubble({ message }: MessageBubbleProps) {
16
- const isUser = message.sender === "user"
17
 
18
  return (
19
  <div className={`flex flex-col ${isUser ? "items-end" : "items-start"}`}>
@@ -39,8 +39,7 @@ export function MessageBubble({ message }: MessageBubbleProps) {
39
  {message.text}
40
  </div>
41
  </div>
42
- )
43
  }
44
 
45
- export type { Message, MessageSender }
46
-
 
1
+ import { IconRobot, IconUser } from "@tabler/icons-react";
2
 
3
+ type MessageSender = "user" | "bot";
4
 
5
  type Message = {
6
+ id: number;
7
+ text: string;
8
+ sender: MessageSender;
9
+ };
10
 
11
  type MessageBubbleProps = {
12
+ message: Message;
13
+ };
14
 
15
  export function MessageBubble({ message }: MessageBubbleProps) {
16
+ const isUser = message.sender === "user";
17
 
18
  return (
19
  <div className={`flex flex-col ${isUser ? "items-end" : "items-start"}`}>
 
39
  {message.text}
40
  </div>
41
  </div>
42
+ );
43
  }
44
 
45
+ export type { Message, MessageSender };
 
src/components/SidebarHeader.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { IconVolume } from "@tabler/icons-react"
2
 
3
  export function SidebarHeader() {
4
  return (
@@ -16,6 +16,5 @@ export function SidebarHeader() {
16
  </div>
17
  </div>
18
  </header>
19
- )
20
  }
21
-
 
1
+ import { IconVolume } from "@tabler/icons-react";
2
 
3
  export function SidebarHeader() {
4
  return (
 
16
  </div>
17
  </div>
18
  </header>
19
+ );
20
  }
 
src/components/component-example.tsx CHANGED
@@ -1,9 +1,35 @@
1
- import * as React from "react"
2
-
3
  import {
4
- Example,
5
- ExampleWrapper,
6
- } from "@/components/example"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import {
8
  AlertDialog,
9
  AlertDialogAction,
@@ -15,9 +41,9 @@ import {
15
  AlertDialogMedia,
16
  AlertDialogTitle,
17
  AlertDialogTrigger,
18
- } from "@/components/ui/alert-dialog"
19
- import { Badge } from "@/components/ui/badge"
20
- import { Button } from "@/components/ui/button"
21
  import {
22
  Card,
23
  CardAction,
@@ -26,7 +52,7 @@ import {
26
  CardFooter,
27
  CardHeader,
28
  CardTitle,
29
- } from "@/components/ui/card"
30
  import {
31
  Combobox,
32
  ComboboxContent,
@@ -34,7 +60,7 @@ import {
34
  ComboboxInput,
35
  ComboboxItem,
36
  ComboboxList,
37
- } from "@/components/ui/combobox"
38
  import {
39
  DropdownMenu,
40
  DropdownMenuCheckboxItem,
@@ -51,9 +77,9 @@ import {
51
  DropdownMenuSubContent,
52
  DropdownMenuSubTrigger,
53
  DropdownMenuTrigger,
54
- } from "@/components/ui/dropdown-menu"
55
- import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
56
- import { Input } from "@/components/ui/input"
57
  import {
58
  Select,
59
  SelectContent,
@@ -61,9 +87,8 @@ import {
61
  SelectItem,
62
  SelectTrigger,
63
  SelectValue,
64
- } from "@/components/ui/select"
65
- import { Textarea } from "@/components/ui/textarea"
66
- import { IconPlus, IconBluetooth, IconDotsVertical, IconFile, IconFolder, IconFolderOpen, IconFileCode, IconDots, IconFolderSearch, IconDeviceFloppy, IconDownload, IconEye, IconLayout, IconPalette, IconSun, IconMoon, IconDeviceDesktop, IconUser, IconCreditCard, IconSettings, IconKeyboard, IconLanguage, IconBell, IconMail, IconShield, IconHelpCircle, IconFileText, IconLogout } from "@tabler/icons-react"
67
 
68
  export function ComponentExample() {
69
  return (
@@ -71,7 +96,7 @@ export function ComponentExample() {
71
  <CardExample />
72
  <FormExample />
73
  </ExampleWrapper>
74
- )
75
  }
76
 
77
  function CardExample() {
@@ -81,7 +106,7 @@ function CardExample() {
81
  <div className="bg-primary absolute inset-0 z-30 aspect-video opacity-50 mix-blend-color" />
82
  <img
83
  src="https://images.unsplash.com/photo-1604076850742-4c7221f3101b?q=80&w=1887&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
84
- alt="Photo by mymind on Unsplash"
85
  title="Photo by mymind on Unsplash"
86
  className="relative z-20 aspect-video w-full object-cover brightness-60 grayscale"
87
  />
@@ -102,8 +127,7 @@ function CardExample() {
102
  <AlertDialogContent size="sm">
103
  <AlertDialogHeader>
104
  <AlertDialogMedia>
105
- <IconBluetooth
106
- />
107
  </AlertDialogMedia>
108
  <AlertDialogTitle>Allow accessory to connect?</AlertDialogTitle>
109
  <AlertDialogDescription>
@@ -123,7 +147,7 @@ function CardExample() {
123
  </CardFooter>
124
  </Card>
125
  </Example>
126
- )
127
  }
128
 
129
  const frameworks = [
@@ -132,22 +156,22 @@ const frameworks = [
132
  "Nuxt.js",
133
  "Remix",
134
  "Astro",
135
- ] as const
136
 
137
  const roleItems = [
138
  { label: "Developer", value: "developer" },
139
  { label: "Designer", value: "designer" },
140
  { label: "Manager", value: "manager" },
141
  { label: "Other", value: "other" },
142
- ]
143
 
144
  function FormExample() {
145
  const [notifications, setNotifications] = React.useState({
146
  email: true,
147
  sms: false,
148
  push: true,
149
- })
150
- const [theme, setTheme] = React.useState("light")
151
 
152
  return (
153
  <Example title="Form">
@@ -160,29 +184,25 @@ function FormExample() {
160
  <DropdownMenuTrigger
161
  render={<Button variant="ghost" size="icon" />}
162
  >
163
- <IconDotsVertical
164
- />
165
  <span className="sr-only">More options</span>
166
  </DropdownMenuTrigger>
167
  <DropdownMenuContent align="end" className="w-56">
168
  <DropdownMenuGroup>
169
  <DropdownMenuLabel>File</DropdownMenuLabel>
170
  <DropdownMenuItem>
171
- <IconFile
172
- />
173
  New File
174
  <DropdownMenuShortcut>⌘N</DropdownMenuShortcut>
175
  </DropdownMenuItem>
176
  <DropdownMenuItem>
177
- <IconFolder
178
- />
179
  New Folder
180
  <DropdownMenuShortcut>⇧⌘N</DropdownMenuShortcut>
181
  </DropdownMenuItem>
182
  <DropdownMenuSub>
183
  <DropdownMenuSubTrigger>
184
- <IconFolderOpen
185
- />
186
  Open Recent
187
  </DropdownMenuSubTrigger>
188
  <DropdownMenuPortal>
@@ -190,31 +210,26 @@ function FormExample() {
190
  <DropdownMenuGroup>
191
  <DropdownMenuLabel>Recent Projects</DropdownMenuLabel>
192
  <DropdownMenuItem>
193
- <IconFileCode
194
- />
195
  Project Alpha
196
  </DropdownMenuItem>
197
  <DropdownMenuItem>
198
- <IconFileCode
199
- />
200
  Project Beta
201
  </DropdownMenuItem>
202
  <DropdownMenuSub>
203
  <DropdownMenuSubTrigger>
204
- <IconDots
205
- />
206
  More Projects
207
  </DropdownMenuSubTrigger>
208
  <DropdownMenuPortal>
209
  <DropdownMenuSubContent>
210
  <DropdownMenuItem>
211
- <IconFileCode
212
- />
213
  Project Gamma
214
  </DropdownMenuItem>
215
  <DropdownMenuItem>
216
- <IconFileCode
217
- />
218
  Project Delta
219
  </DropdownMenuItem>
220
  </DropdownMenuSubContent>
@@ -224,8 +239,7 @@ function FormExample() {
224
  <DropdownMenuSeparator />
225
  <DropdownMenuGroup>
226
  <DropdownMenuItem>
227
- <IconFolderSearch
228
- />
229
  Browse...
230
  </DropdownMenuItem>
231
  </DropdownMenuGroup>
@@ -234,14 +248,12 @@ function FormExample() {
234
  </DropdownMenuSub>
235
  <DropdownMenuSeparator />
236
  <DropdownMenuItem>
237
- <IconDeviceFloppy
238
- />
239
  Save
240
  <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
241
  </DropdownMenuItem>
242
  <DropdownMenuItem>
243
- <IconDownload
244
- />
245
  Export
246
  <DropdownMenuShortcut>⇧⌘E</DropdownMenuShortcut>
247
  </DropdownMenuItem>
@@ -258,8 +270,7 @@ function FormExample() {
258
  })
259
  }
260
  >
261
- <IconEye
262
- />
263
  Show Sidebar
264
  </DropdownMenuCheckboxItem>
265
  <DropdownMenuCheckboxItem
@@ -271,14 +282,12 @@ function FormExample() {
271
  })
272
  }
273
  >
274
- <IconLayout
275
- />
276
  Show Status Bar
277
  </DropdownMenuCheckboxItem>
278
  <DropdownMenuSub>
279
  <DropdownMenuSubTrigger>
280
- <IconPalette
281
- />
282
  Theme
283
  </DropdownMenuSubTrigger>
284
  <DropdownMenuPortal>
@@ -290,18 +299,15 @@ function FormExample() {
290
  onValueChange={setTheme}
291
  >
292
  <DropdownMenuRadioItem value="light">
293
- <IconSun
294
- />
295
  Light
296
  </DropdownMenuRadioItem>
297
  <DropdownMenuRadioItem value="dark">
298
- <IconMoon
299
- />
300
  Dark
301
  </DropdownMenuRadioItem>
302
  <DropdownMenuRadioItem value="system">
303
- <IconDeviceDesktop
304
- />
305
  System
306
  </DropdownMenuRadioItem>
307
  </DropdownMenuRadioGroup>
@@ -314,20 +320,17 @@ function FormExample() {
314
  <DropdownMenuGroup>
315
  <DropdownMenuLabel>Account</DropdownMenuLabel>
316
  <DropdownMenuItem>
317
- <IconUser
318
- />
319
  Profile
320
  <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
321
  </DropdownMenuItem>
322
  <DropdownMenuItem>
323
- <IconCreditCard
324
- />
325
  Billing
326
  </DropdownMenuItem>
327
  <DropdownMenuSub>
328
  <DropdownMenuSubTrigger>
329
- <IconSettings
330
- />
331
  Settings
332
  </DropdownMenuSubTrigger>
333
  <DropdownMenuPortal>
@@ -335,19 +338,16 @@ function FormExample() {
335
  <DropdownMenuGroup>
336
  <DropdownMenuLabel>Preferences</DropdownMenuLabel>
337
  <DropdownMenuItem>
338
- <IconKeyboard
339
- />
340
  Keyboard Shortcuts
341
  </DropdownMenuItem>
342
  <DropdownMenuItem>
343
- <IconLanguage
344
- />
345
  Language
346
  </DropdownMenuItem>
347
  <DropdownMenuSub>
348
  <DropdownMenuSubTrigger>
349
- <IconBell
350
- />
351
  Notifications
352
  </DropdownMenuSubTrigger>
353
  <DropdownMenuPortal>
@@ -365,8 +365,7 @@ function FormExample() {
365
  })
366
  }
367
  >
368
- <IconBell
369
- />
370
  Push Notifications
371
  </DropdownMenuCheckboxItem>
372
  <DropdownMenuCheckboxItem
@@ -378,8 +377,7 @@ function FormExample() {
378
  })
379
  }
380
  >
381
- <IconMail
382
- />
383
  Email Notifications
384
  </DropdownMenuCheckboxItem>
385
  </DropdownMenuGroup>
@@ -390,8 +388,7 @@ function FormExample() {
390
  <DropdownMenuSeparator />
391
  <DropdownMenuGroup>
392
  <DropdownMenuItem>
393
- <IconShield
394
- />
395
  Privacy & Security
396
  </DropdownMenuItem>
397
  </DropdownMenuGroup>
@@ -402,21 +399,18 @@ function FormExample() {
402
  <DropdownMenuSeparator />
403
  <DropdownMenuGroup>
404
  <DropdownMenuItem>
405
- <IconHelpCircle
406
- />
407
  Help & Support
408
  </DropdownMenuItem>
409
  <DropdownMenuItem>
410
- <IconFileText
411
- />
412
  Documentation
413
  </DropdownMenuItem>
414
  </DropdownMenuGroup>
415
  <DropdownMenuSeparator />
416
  <DropdownMenuGroup>
417
  <DropdownMenuItem variant="destructive">
418
- <IconLogout
419
- />
420
  Sign Out
421
  <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
422
  </DropdownMenuItem>
@@ -495,5 +489,5 @@ function FormExample() {
495
  </CardContent>
496
  </Card>
497
  </Example>
498
- )
499
  }
 
 
 
1
  import {
2
+ IconBell,
3
+ IconBluetooth,
4
+ IconCreditCard,
5
+ IconDeviceDesktop,
6
+ IconDeviceFloppy,
7
+ IconDots,
8
+ IconDotsVertical,
9
+ IconDownload,
10
+ IconEye,
11
+ IconFile,
12
+ IconFileCode,
13
+ IconFileText,
14
+ IconFolder,
15
+ IconFolderOpen,
16
+ IconFolderSearch,
17
+ IconHelpCircle,
18
+ IconKeyboard,
19
+ IconLanguage,
20
+ IconLayout,
21
+ IconLogout,
22
+ IconMail,
23
+ IconMoon,
24
+ IconPalette,
25
+ IconPlus,
26
+ IconSettings,
27
+ IconShield,
28
+ IconSun,
29
+ IconUser,
30
+ } from "@tabler/icons-react";
31
+ import * as React from "react";
32
+ import { Example, ExampleWrapper } from "@/components/example";
33
  import {
34
  AlertDialog,
35
  AlertDialogAction,
 
41
  AlertDialogMedia,
42
  AlertDialogTitle,
43
  AlertDialogTrigger,
44
+ } from "@/components/ui/alert-dialog";
45
+ import { Badge } from "@/components/ui/badge";
46
+ import { Button } from "@/components/ui/button";
47
  import {
48
  Card,
49
  CardAction,
 
52
  CardFooter,
53
  CardHeader,
54
  CardTitle,
55
+ } from "@/components/ui/card";
56
  import {
57
  Combobox,
58
  ComboboxContent,
 
60
  ComboboxInput,
61
  ComboboxItem,
62
  ComboboxList,
63
+ } from "@/components/ui/combobox";
64
  import {
65
  DropdownMenu,
66
  DropdownMenuCheckboxItem,
 
77
  DropdownMenuSubContent,
78
  DropdownMenuSubTrigger,
79
  DropdownMenuTrigger,
80
+ } from "@/components/ui/dropdown-menu";
81
+ import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
82
+ import { Input } from "@/components/ui/input";
83
  import {
84
  Select,
85
  SelectContent,
 
87
  SelectItem,
88
  SelectTrigger,
89
  SelectValue,
90
+ } from "@/components/ui/select";
91
+ import { Textarea } from "@/components/ui/textarea";
 
92
 
93
  export function ComponentExample() {
94
  return (
 
96
  <CardExample />
97
  <FormExample />
98
  </ExampleWrapper>
99
+ );
100
  }
101
 
102
  function CardExample() {
 
106
  <div className="bg-primary absolute inset-0 z-30 aspect-video opacity-50 mix-blend-color" />
107
  <img
108
  src="https://images.unsplash.com/photo-1604076850742-4c7221f3101b?q=80&w=1887&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
109
+ alt="Abstract colorful artwork by mymind on Unsplash"
110
  title="Photo by mymind on Unsplash"
111
  className="relative z-20 aspect-video w-full object-cover brightness-60 grayscale"
112
  />
 
127
  <AlertDialogContent size="sm">
128
  <AlertDialogHeader>
129
  <AlertDialogMedia>
130
+ <IconBluetooth />
 
131
  </AlertDialogMedia>
132
  <AlertDialogTitle>Allow accessory to connect?</AlertDialogTitle>
133
  <AlertDialogDescription>
 
147
  </CardFooter>
148
  </Card>
149
  </Example>
150
+ );
151
  }
152
 
153
  const frameworks = [
 
156
  "Nuxt.js",
157
  "Remix",
158
  "Astro",
159
+ ] as const;
160
 
161
  const roleItems = [
162
  { label: "Developer", value: "developer" },
163
  { label: "Designer", value: "designer" },
164
  { label: "Manager", value: "manager" },
165
  { label: "Other", value: "other" },
166
+ ];
167
 
168
  function FormExample() {
169
  const [notifications, setNotifications] = React.useState({
170
  email: true,
171
  sms: false,
172
  push: true,
173
+ });
174
+ const [theme, setTheme] = React.useState("light");
175
 
176
  return (
177
  <Example title="Form">
 
184
  <DropdownMenuTrigger
185
  render={<Button variant="ghost" size="icon" />}
186
  >
187
+ <IconDotsVertical />
 
188
  <span className="sr-only">More options</span>
189
  </DropdownMenuTrigger>
190
  <DropdownMenuContent align="end" className="w-56">
191
  <DropdownMenuGroup>
192
  <DropdownMenuLabel>File</DropdownMenuLabel>
193
  <DropdownMenuItem>
194
+ <IconFile />
 
195
  New File
196
  <DropdownMenuShortcut>⌘N</DropdownMenuShortcut>
197
  </DropdownMenuItem>
198
  <DropdownMenuItem>
199
+ <IconFolder />
 
200
  New Folder
201
  <DropdownMenuShortcut>⇧⌘N</DropdownMenuShortcut>
202
  </DropdownMenuItem>
203
  <DropdownMenuSub>
204
  <DropdownMenuSubTrigger>
205
+ <IconFolderOpen />
 
206
  Open Recent
207
  </DropdownMenuSubTrigger>
208
  <DropdownMenuPortal>
 
210
  <DropdownMenuGroup>
211
  <DropdownMenuLabel>Recent Projects</DropdownMenuLabel>
212
  <DropdownMenuItem>
213
+ <IconFileCode />
 
214
  Project Alpha
215
  </DropdownMenuItem>
216
  <DropdownMenuItem>
217
+ <IconFileCode />
 
218
  Project Beta
219
  </DropdownMenuItem>
220
  <DropdownMenuSub>
221
  <DropdownMenuSubTrigger>
222
+ <IconDots />
 
223
  More Projects
224
  </DropdownMenuSubTrigger>
225
  <DropdownMenuPortal>
226
  <DropdownMenuSubContent>
227
  <DropdownMenuItem>
228
+ <IconFileCode />
 
229
  Project Gamma
230
  </DropdownMenuItem>
231
  <DropdownMenuItem>
232
+ <IconFileCode />
 
233
  Project Delta
234
  </DropdownMenuItem>
235
  </DropdownMenuSubContent>
 
239
  <DropdownMenuSeparator />
240
  <DropdownMenuGroup>
241
  <DropdownMenuItem>
242
+ <IconFolderSearch />
 
243
  Browse...
244
  </DropdownMenuItem>
245
  </DropdownMenuGroup>
 
248
  </DropdownMenuSub>
249
  <DropdownMenuSeparator />
250
  <DropdownMenuItem>
251
+ <IconDeviceFloppy />
 
252
  Save
253
  <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
254
  </DropdownMenuItem>
255
  <DropdownMenuItem>
256
+ <IconDownload />
 
257
  Export
258
  <DropdownMenuShortcut>⇧⌘E</DropdownMenuShortcut>
259
  </DropdownMenuItem>
 
270
  })
271
  }
272
  >
273
+ <IconEye />
 
274
  Show Sidebar
275
  </DropdownMenuCheckboxItem>
276
  <DropdownMenuCheckboxItem
 
282
  })
283
  }
284
  >
285
+ <IconLayout />
 
286
  Show Status Bar
287
  </DropdownMenuCheckboxItem>
288
  <DropdownMenuSub>
289
  <DropdownMenuSubTrigger>
290
+ <IconPalette />
 
291
  Theme
292
  </DropdownMenuSubTrigger>
293
  <DropdownMenuPortal>
 
299
  onValueChange={setTheme}
300
  >
301
  <DropdownMenuRadioItem value="light">
302
+ <IconSun />
 
303
  Light
304
  </DropdownMenuRadioItem>
305
  <DropdownMenuRadioItem value="dark">
306
+ <IconMoon />
 
307
  Dark
308
  </DropdownMenuRadioItem>
309
  <DropdownMenuRadioItem value="system">
310
+ <IconDeviceDesktop />
 
311
  System
312
  </DropdownMenuRadioItem>
313
  </DropdownMenuRadioGroup>
 
320
  <DropdownMenuGroup>
321
  <DropdownMenuLabel>Account</DropdownMenuLabel>
322
  <DropdownMenuItem>
323
+ <IconUser />
 
324
  Profile
325
  <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
326
  </DropdownMenuItem>
327
  <DropdownMenuItem>
328
+ <IconCreditCard />
 
329
  Billing
330
  </DropdownMenuItem>
331
  <DropdownMenuSub>
332
  <DropdownMenuSubTrigger>
333
+ <IconSettings />
 
334
  Settings
335
  </DropdownMenuSubTrigger>
336
  <DropdownMenuPortal>
 
338
  <DropdownMenuGroup>
339
  <DropdownMenuLabel>Preferences</DropdownMenuLabel>
340
  <DropdownMenuItem>
341
+ <IconKeyboard />
 
342
  Keyboard Shortcuts
343
  </DropdownMenuItem>
344
  <DropdownMenuItem>
345
+ <IconLanguage />
 
346
  Language
347
  </DropdownMenuItem>
348
  <DropdownMenuSub>
349
  <DropdownMenuSubTrigger>
350
+ <IconBell />
 
351
  Notifications
352
  </DropdownMenuSubTrigger>
353
  <DropdownMenuPortal>
 
365
  })
366
  }
367
  >
368
+ <IconBell />
 
369
  Push Notifications
370
  </DropdownMenuCheckboxItem>
371
  <DropdownMenuCheckboxItem
 
377
  })
378
  }
379
  >
380
+ <IconMail />
 
381
  Email Notifications
382
  </DropdownMenuCheckboxItem>
383
  </DropdownMenuGroup>
 
388
  <DropdownMenuSeparator />
389
  <DropdownMenuGroup>
390
  <DropdownMenuItem>
391
+ <IconShield />
 
392
  Privacy & Security
393
  </DropdownMenuItem>
394
  </DropdownMenuGroup>
 
399
  <DropdownMenuSeparator />
400
  <DropdownMenuGroup>
401
  <DropdownMenuItem>
402
+ <IconHelpCircle />
 
403
  Help & Support
404
  </DropdownMenuItem>
405
  <DropdownMenuItem>
406
+ <IconFileText />
 
407
  Documentation
408
  </DropdownMenuItem>
409
  </DropdownMenuGroup>
410
  <DropdownMenuSeparator />
411
  <DropdownMenuGroup>
412
  <DropdownMenuItem variant="destructive">
413
+ <IconLogout />
 
414
  Sign Out
415
  <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
416
  </DropdownMenuItem>
 
489
  </CardContent>
490
  </Card>
491
  </Example>
492
+ );
493
  }
src/components/example.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { cn } from "@/lib/utils"
2
 
3
  function ExampleWrapper({ className, ...props }: React.ComponentProps<"div">) {
4
  return (
@@ -8,12 +8,12 @@ function ExampleWrapper({ className, ...props }: React.ComponentProps<"div">) {
8
  className={cn(
9
  "mx-auto grid min-h-screen w-full max-w-5xl min-w-0 content-center items-start gap-8 p-4 pt-2 sm:gap-12 sm:p-6 md:grid-cols-2 md:gap-8 lg:p-12 2xl:max-w-6xl",
10
 
11
- className
12
  )}
13
  {...props}
14
  />
15
  </div>
16
- )
17
  }
18
 
19
  function Example({
@@ -23,15 +23,15 @@ function Example({
23
  containerClassName,
24
  ...props
25
  }: React.ComponentProps<"div"> & {
26
- title: string
27
- containerClassName?: string
28
  }) {
29
  return (
30
  <div
31
  data-slot="example"
32
  className={cn(
33
  "mx-auto flex w-full max-w-lg min-w-0 flex-col gap-1 self-stretch lg:max-w-none",
34
- containerClassName
35
  )}
36
  {...props}
37
  >
@@ -42,13 +42,13 @@ function Example({
42
  data-slot="example-content"
43
  className={cn(
44
  "bg-background text-foreground flex min-w-0 flex-1 flex-col items-start gap-6 border border-dashed p-4 sm:p-6 *:[div:not([class*='w-'])]:w-full",
45
- className
46
  )}
47
  >
48
  {children}
49
  </div>
50
  </div>
51
- )
52
  }
53
 
54
- export { ExampleWrapper, Example }
 
1
+ import { cn } from "@/lib/utils";
2
 
3
  function ExampleWrapper({ className, ...props }: React.ComponentProps<"div">) {
4
  return (
 
8
  className={cn(
9
  "mx-auto grid min-h-screen w-full max-w-5xl min-w-0 content-center items-start gap-8 p-4 pt-2 sm:gap-12 sm:p-6 md:grid-cols-2 md:gap-8 lg:p-12 2xl:max-w-6xl",
10
 
11
+ className,
12
  )}
13
  {...props}
14
  />
15
  </div>
16
+ );
17
  }
18
 
19
  function Example({
 
23
  containerClassName,
24
  ...props
25
  }: React.ComponentProps<"div"> & {
26
+ title: string;
27
+ containerClassName?: string;
28
  }) {
29
  return (
30
  <div
31
  data-slot="example"
32
  className={cn(
33
  "mx-auto flex w-full max-w-lg min-w-0 flex-col gap-1 self-stretch lg:max-w-none",
34
+ containerClassName,
35
  )}
36
  {...props}
37
  >
 
42
  data-slot="example-content"
43
  className={cn(
44
  "bg-background text-foreground flex min-w-0 flex-1 flex-col items-start gap-6 border border-dashed p-4 sm:p-6 *:[div:not([class*='w-'])]:w-full",
45
+ className,
46
  )}
47
  >
48
  {children}
49
  </div>
50
  </div>
51
+ );
52
  }
53
 
54
+ export { ExampleWrapper, Example };
src/components/ui/alert-dialog.tsx CHANGED
@@ -1,23 +1,22 @@
1
- import * as React from "react"
2
- import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
3
-
4
- import { cn } from "@/lib/utils"
5
- import { Button } from "@/components/ui/button"
6
 
7
  function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
8
- return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
9
  }
10
 
11
  function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
12
  return (
13
  <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
14
- )
15
  }
16
 
17
  function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
18
  return (
19
  <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
20
- )
21
  }
22
 
23
  function AlertDialogOverlay({
@@ -29,11 +28,11 @@ function AlertDialogOverlay({
29
  data-slot="alert-dialog-overlay"
30
  className={cn(
31
  "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50",
32
- className
33
  )}
34
  {...props}
35
  />
36
- )
37
  }
38
 
39
  function AlertDialogContent({
@@ -41,7 +40,7 @@ function AlertDialogContent({
41
  size = "default",
42
  ...props
43
  }: AlertDialogPrimitive.Popup.Props & {
44
- size?: "default" | "sm"
45
  }) {
46
  return (
47
  <AlertDialogPortal>
@@ -51,12 +50,12 @@ function AlertDialogContent({
51
  data-size={size}
52
  className={cn(
53
  "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-background ring-foreground/5 gap-6 rounded-4xl p-6 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-md group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
54
- className
55
  )}
56
  {...props}
57
  />
58
  </AlertDialogPortal>
59
- )
60
  }
61
 
62
  function AlertDialogHeader({
@@ -66,10 +65,13 @@ function AlertDialogHeader({
66
  return (
67
  <div
68
  data-slot="alert-dialog-header"
69
- className={cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className)}
 
 
 
70
  {...props}
71
  />
72
- )
73
  }
74
 
75
  function AlertDialogFooter({
@@ -81,11 +83,11 @@ function AlertDialogFooter({
81
  data-slot="alert-dialog-footer"
82
  className={cn(
83
  "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
84
- className
85
  )}
86
  {...props}
87
  />
88
- )
89
  }
90
 
91
  function AlertDialogMedia({
@@ -95,10 +97,13 @@ function AlertDialogMedia({
95
  return (
96
  <div
97
  data-slot="alert-dialog-media"
98
- className={cn("bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-full sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8", className)}
 
 
 
99
  {...props}
100
  />
101
- )
102
  }
103
 
104
  function AlertDialogTitle({
@@ -108,10 +113,13 @@ function AlertDialogTitle({
108
  return (
109
  <AlertDialogPrimitive.Title
110
  data-slot="alert-dialog-title"
111
- className={cn("text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className)}
 
 
 
112
  {...props}
113
  />
114
- )
115
  }
116
 
117
  function AlertDialogDescription({
@@ -121,10 +129,13 @@ function AlertDialogDescription({
121
  return (
122
  <AlertDialogPrimitive.Description
123
  data-slot="alert-dialog-description"
124
- className={cn("text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3", className)}
 
 
 
125
  {...props}
126
  />
127
- )
128
  }
129
 
130
  function AlertDialogAction({
@@ -137,7 +148,7 @@ function AlertDialogAction({
137
  className={cn(className)}
138
  {...props}
139
  />
140
- )
141
  }
142
 
143
  function AlertDialogCancel({
@@ -154,7 +165,7 @@ function AlertDialogCancel({
154
  render={<Button variant={variant} size={size} />}
155
  {...props}
156
  />
157
- )
158
  }
159
 
160
  export {
@@ -170,4 +181,4 @@ export {
170
  AlertDialogPortal,
171
  AlertDialogTitle,
172
  AlertDialogTrigger,
173
- }
 
1
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
2
+ import * as React from "react";
3
+ import { Button } from "@/components/ui/button";
4
+ import { cn } from "@/lib/utils";
 
5
 
6
  function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
7
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
8
  }
9
 
10
  function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
11
  return (
12
  <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
13
+ );
14
  }
15
 
16
  function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
17
  return (
18
  <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
19
+ );
20
  }
21
 
22
  function AlertDialogOverlay({
 
28
  data-slot="alert-dialog-overlay"
29
  className={cn(
30
  "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50",
31
+ className,
32
  )}
33
  {...props}
34
  />
35
+ );
36
  }
37
 
38
  function AlertDialogContent({
 
40
  size = "default",
41
  ...props
42
  }: AlertDialogPrimitive.Popup.Props & {
43
+ size?: "default" | "sm";
44
  }) {
45
  return (
46
  <AlertDialogPortal>
 
50
  data-size={size}
51
  className={cn(
52
  "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-background ring-foreground/5 gap-6 rounded-4xl p-6 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-md group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
53
+ className,
54
  )}
55
  {...props}
56
  />
57
  </AlertDialogPortal>
58
+ );
59
  }
60
 
61
  function AlertDialogHeader({
 
65
  return (
66
  <div
67
  data-slot="alert-dialog-header"
68
+ className={cn(
69
+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
70
+ className,
71
+ )}
72
  {...props}
73
  />
74
+ );
75
  }
76
 
77
  function AlertDialogFooter({
 
83
  data-slot="alert-dialog-footer"
84
  className={cn(
85
  "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
86
+ className,
87
  )}
88
  {...props}
89
  />
90
+ );
91
  }
92
 
93
  function AlertDialogMedia({
 
97
  return (
98
  <div
99
  data-slot="alert-dialog-media"
100
+ className={cn(
101
+ "bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-full sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
102
+ className,
103
+ )}
104
  {...props}
105
  />
106
+ );
107
  }
108
 
109
  function AlertDialogTitle({
 
113
  return (
114
  <AlertDialogPrimitive.Title
115
  data-slot="alert-dialog-title"
116
+ className={cn(
117
+ "text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
118
+ className,
119
+ )}
120
  {...props}
121
  />
122
+ );
123
  }
124
 
125
  function AlertDialogDescription({
 
129
  return (
130
  <AlertDialogPrimitive.Description
131
  data-slot="alert-dialog-description"
132
+ className={cn(
133
+ "text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3",
134
+ className,
135
+ )}
136
  {...props}
137
  />
138
+ );
139
  }
140
 
141
  function AlertDialogAction({
 
148
  className={cn(className)}
149
  {...props}
150
  />
151
+ );
152
  }
153
 
154
  function AlertDialogCancel({
 
165
  render={<Button variant={variant} size={size} />}
166
  {...props}
167
  />
168
+ );
169
  }
170
 
171
  export {
 
181
  AlertDialogPortal,
182
  AlertDialogTitle,
183
  AlertDialogTrigger,
184
+ };
src/components/ui/badge.tsx CHANGED
@@ -1,8 +1,8 @@
1
- import { mergeProps } from "@base-ui/react/merge-props"
2
- import { useRender } from "@base-ui/react/use-render"
3
- import { cva, type VariantProps } from "class-variance-authority"
4
 
5
- import { cn } from "@/lib/utils"
6
 
7
  const badgeVariants = cva(
8
  "h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors overflow-hidden group/badge",
@@ -10,18 +10,22 @@ const badgeVariants = cva(
10
  variants: {
11
  variant: {
12
  default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
13
- secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
14
- destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
15
- outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground bg-input/30",
16
- ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
 
 
 
 
17
  link: "text-primary underline-offset-4 hover:underline",
18
  },
19
  },
20
  defaultVariants: {
21
  variant: "default",
22
  },
23
- }
24
- )
25
 
26
  function Badge({
27
  className,
@@ -35,14 +39,14 @@ function Badge({
35
  {
36
  className: cn(badgeVariants({ className, variant })),
37
  },
38
- props
39
  ),
40
  render,
41
  state: {
42
  slot: "badge",
43
  variant,
44
  },
45
- })
46
  }
47
 
48
- export { Badge, badgeVariants }
 
1
+ import { mergeProps } from "@base-ui/react/merge-props";
2
+ import { useRender } from "@base-ui/react/use-render";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
 
5
+ import { cn } from "@/lib/utils";
6
 
7
  const badgeVariants = cva(
8
  "h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors overflow-hidden group/badge",
 
10
  variants: {
11
  variant: {
12
  default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
13
+ secondary:
14
+ "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
15
+ destructive:
16
+ "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
17
+ outline:
18
+ "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground bg-input/30",
19
+ ghost:
20
+ "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
21
  link: "text-primary underline-offset-4 hover:underline",
22
  },
23
  },
24
  defaultVariants: {
25
  variant: "default",
26
  },
27
+ },
28
+ );
29
 
30
  function Badge({
31
  className,
 
39
  {
40
  className: cn(badgeVariants({ className, variant })),
41
  },
42
+ props,
43
  ),
44
  render,
45
  state: {
46
  slot: "badge",
47
  variant,
48
  },
49
+ });
50
  }
51
 
52
+ export { Badge, badgeVariants };
src/components/ui/button.tsx CHANGED
@@ -1,7 +1,7 @@
1
- import { Button as ButtonPrimitive } from "@base-ui/react/button"
2
- import { cva, type VariantProps } from "class-variance-authority"
3
 
4
- import { cn } from "@/lib/utils"
5
 
6
  const buttonVariants = cva(
7
  "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
@@ -9,14 +9,19 @@ const buttonVariants = cva(
9
  variants: {
10
  variant: {
11
  default: "bg-primary text-primary-foreground hover:bg-primary/80",
12
- outline: "border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
13
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
14
- ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
15
- destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
 
 
 
 
16
  link: "text-primary underline-offset-4 hover:underline",
17
  },
18
  size: {
19
- default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
 
20
  xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
21
  sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
22
  lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
@@ -30,8 +35,8 @@ const buttonVariants = cva(
30
  variant: "default",
31
  size: "default",
32
  },
33
- }
34
- )
35
 
36
  function Button({
37
  className,
@@ -45,7 +50,7 @@ function Button({
45
  className={cn(buttonVariants({ variant, size, className }))}
46
  {...props}
47
  />
48
- )
49
  }
50
 
51
- export { Button, buttonVariants }
 
1
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
 
4
+ import { cn } from "@/lib/utils";
5
 
6
  const buttonVariants = cva(
7
  "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
 
9
  variants: {
10
  variant: {
11
  default: "bg-primary text-primary-foreground hover:bg-primary/80",
12
+ outline:
13
+ "border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
14
+ secondary:
15
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
16
+ ghost:
17
+ "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
18
+ destructive:
19
+ "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
20
  link: "text-primary underline-offset-4 hover:underline",
21
  },
22
  size: {
23
+ default:
24
+ "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
25
  xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
26
  sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
27
  lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
 
35
  variant: "default",
36
  size: "default",
37
  },
38
+ },
39
+ );
40
 
41
  function Button({
42
  className,
 
50
  className={cn(buttonVariants({ variant, size, className }))}
51
  {...props}
52
  />
53
+ );
54
  }
55
 
56
+ export { Button, buttonVariants };
src/components/ui/card.tsx CHANGED
@@ -1,6 +1,6 @@
1
- import * as React from "react"
2
 
3
- import { cn } from "@/lib/utils"
4
 
5
  function Card({
6
  className,
@@ -11,10 +11,13 @@ function Card({
11
  <div
12
  data-slot="card"
13
  data-size={size}
14
- className={cn("ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-2xl py-6 text-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
 
 
 
15
  {...props}
16
  />
17
- )
18
  }
19
 
20
  function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -23,11 +26,11 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
23
  data-slot="card-header"
24
  className={cn(
25
  "gap-2 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
26
- className
27
  )}
28
  {...props}
29
  />
30
- )
31
  }
32
 
33
  function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
@@ -37,7 +40,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
37
  className={cn("text-base font-medium", className)}
38
  {...props}
39
  />
40
- )
41
  }
42
 
43
  function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
@@ -47,7 +50,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
47
  className={cn("text-muted-foreground text-sm", className)}
48
  {...props}
49
  />
50
- )
51
  }
52
 
53
  function CardAction({ className, ...props }: React.ComponentProps<"div">) {
@@ -56,11 +59,11 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
56
  data-slot="card-action"
57
  className={cn(
58
  "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
59
- className
60
  )}
61
  {...props}
62
  />
63
- )
64
  }
65
 
66
  function CardContent({ className, ...props }: React.ComponentProps<"div">) {
@@ -70,17 +73,20 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
70
  className={cn("px-6 group-data-[size=sm]/card:px-4", className)}
71
  {...props}
72
  />
73
- )
74
  }
75
 
76
  function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
77
  return (
78
  <div
79
  data-slot="card-footer"
80
- className={cn("rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4 flex items-center", className)}
 
 
 
81
  {...props}
82
  />
83
- )
84
  }
85
 
86
  export {
@@ -91,4 +97,4 @@ export {
91
  CardAction,
92
  CardDescription,
93
  CardContent,
94
- }
 
1
+ import * as React from "react";
2
 
3
+ import { cn } from "@/lib/utils";
4
 
5
  function Card({
6
  className,
 
11
  <div
12
  data-slot="card"
13
  data-size={size}
14
+ className={cn(
15
+ "ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-2xl py-6 text-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col",
16
+ className,
17
+ )}
18
  {...props}
19
  />
20
+ );
21
  }
22
 
23
  function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
 
26
  data-slot="card-header"
27
  className={cn(
28
  "gap-2 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
29
+ className,
30
  )}
31
  {...props}
32
  />
33
+ );
34
  }
35
 
36
  function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
 
40
  className={cn("text-base font-medium", className)}
41
  {...props}
42
  />
43
+ );
44
  }
45
 
46
  function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
 
50
  className={cn("text-muted-foreground text-sm", className)}
51
  {...props}
52
  />
53
+ );
54
  }
55
 
56
  function CardAction({ className, ...props }: React.ComponentProps<"div">) {
 
59
  data-slot="card-action"
60
  className={cn(
61
  "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
62
+ className,
63
  )}
64
  {...props}
65
  />
66
+ );
67
  }
68
 
69
  function CardContent({ className, ...props }: React.ComponentProps<"div">) {
 
73
  className={cn("px-6 group-data-[size=sm]/card:px-4", className)}
74
  {...props}
75
  />
76
+ );
77
  }
78
 
79
  function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
80
  return (
81
  <div
82
  data-slot="card-footer"
83
+ className={cn(
84
+ "rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4 flex items-center",
85
+ className,
86
+ )}
87
  {...props}
88
  />
89
+ );
90
  }
91
 
92
  export {
 
97
  CardAction,
98
  CardDescription,
99
  CardContent,
100
+ };
src/components/ui/combobox.tsx CHANGED
@@ -1,22 +1,21 @@
1
- "use client"
2
 
3
- import * as React from "react"
4
- import { Combobox as ComboboxPrimitive } from "@base-ui/react"
5
-
6
- import { cn } from "@/lib/utils"
7
- import { Button } from "@/components/ui/button"
8
  import {
9
  InputGroup,
10
  InputGroupAddon,
11
  InputGroupButton,
12
  InputGroupInput,
13
- } from "@/components/ui/input-group"
14
- import { IconChevronDown, IconX, IconCheck } from "@tabler/icons-react"
15
 
16
- const Combobox = ComboboxPrimitive.Root
17
 
18
  function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
19
- return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />
20
  }
21
 
22
  function ComboboxTrigger({
@@ -33,7 +32,7 @@ function ComboboxTrigger({
33
  {children}
34
  <IconChevronDown className="text-muted-foreground size-4 pointer-events-none" />
35
  </ComboboxPrimitive.Trigger>
36
- )
37
  }
38
 
39
  function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
@@ -46,7 +45,7 @@ function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
46
  >
47
  <IconX className="pointer-events-none" />
48
  </ComboboxPrimitive.Clear>
49
- )
50
  }
51
 
52
  function ComboboxInput({
@@ -57,8 +56,8 @@ function ComboboxInput({
57
  showClear = false,
58
  ...props
59
  }: ComboboxPrimitive.Input.Props & {
60
- showTrigger?: boolean
61
- showClear?: boolean
62
  }) {
63
  return (
64
  <InputGroup className={cn("w-auto", className)}>
@@ -81,7 +80,7 @@ function ComboboxInput({
81
  </InputGroupAddon>
82
  {children}
83
  </InputGroup>
84
- )
85
  }
86
 
87
  function ComboboxContent({
@@ -110,12 +109,15 @@ function ComboboxContent({
110
  <ComboboxPrimitive.Popup
111
  data-slot="combobox-content"
112
  data-chips={!!anchor}
113
- className={cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 *:data-[slot=input-group]:bg-input/30 max-h-72 min-w-36 overflow-hidden rounded-2xl shadow-2xl ring-1 duration-100 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-9 *:data-[slot=input-group]:border-none *:data-[slot=input-group]:shadow-none group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) data-[chips=true]:min-w-(--anchor-width)", className )}
 
 
 
114
  {...props}
115
  />
116
  </ComboboxPrimitive.Positioner>
117
  </ComboboxPrimitive.Portal>
118
- )
119
  }
120
 
121
  function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
@@ -124,11 +126,11 @@ function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
124
  data-slot="combobox-list"
125
  className={cn(
126
  "no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0 overflow-y-auto overscroll-contain",
127
- className
128
  )}
129
  {...props}
130
  />
131
- )
132
  }
133
 
134
  function ComboboxItem({
@@ -141,18 +143,20 @@ function ComboboxItem({
141
  data-slot="combobox-item"
142
  className={cn(
143
  "data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
144
- className
145
  )}
146
  {...props}
147
  >
148
  {children}
149
  <ComboboxPrimitive.ItemIndicator
150
- render={<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />}
 
 
151
  >
152
  <IconCheck className="pointer-events-none" />
153
  </ComboboxPrimitive.ItemIndicator>
154
  </ComboboxPrimitive.Item>
155
- )
156
  }
157
 
158
  function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
@@ -162,7 +166,7 @@ function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
162
  className={cn(className)}
163
  {...props}
164
  />
165
- )
166
  }
167
 
168
  function ComboboxLabel({
@@ -175,23 +179,26 @@ function ComboboxLabel({
175
  className={cn("text-muted-foreground px-3.5 py-2.5 text-xs", className)}
176
  {...props}
177
  />
178
- )
179
  }
180
 
181
  function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
182
  return (
183
  <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
184
- )
185
  }
186
 
187
  function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
188
  return (
189
  <ComboboxPrimitive.Empty
190
  data-slot="combobox-empty"
191
- className={cn("text-muted-foreground hidden w-full justify-center py-2 text-center text-sm group-data-empty/combobox-content:flex", className)}
 
 
 
192
  {...props}
193
  />
194
- )
195
  }
196
 
197
  function ComboboxSeparator({
@@ -204,7 +211,7 @@ function ComboboxSeparator({
204
  className={cn("bg-border/50 -mx-1 my-1 h-px", className)}
205
  {...props}
206
  />
207
- )
208
  }
209
 
210
  function ComboboxChips({
@@ -215,10 +222,13 @@ function ComboboxChips({
215
  return (
216
  <ComboboxPrimitive.Chips
217
  data-slot="combobox-chips"
218
- className={cn("bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-9 flex-wrap items-center gap-1.5 rounded-4xl border bg-clip-padding px-2.5 py-1.5 text-sm transition-colors focus-within:ring-[3px] has-aria-invalid:ring-[3px] has-data-[slot=combobox-chip]:px-1.5", className)}
 
 
 
219
  {...props}
220
  />
221
- )
222
  }
223
 
224
  function ComboboxChip({
@@ -227,14 +237,14 @@ function ComboboxChip({
227
  showRemove = true,
228
  ...props
229
  }: ComboboxPrimitive.Chip.Props & {
230
- showRemove?: boolean
231
  }) {
232
  return (
233
  <ComboboxPrimitive.Chip
234
  data-slot="combobox-chip"
235
  className={cn(
236
  "bg-muted-foreground/10 text-foreground flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-4xl px-2 text-xs font-medium whitespace-nowrap has-data-[slot=combobox-chip-remove]:pr-0 has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50",
237
- className
238
  )}
239
  {...props}
240
  >
@@ -249,7 +259,7 @@ function ComboboxChip({
249
  </ComboboxPrimitive.ChipRemove>
250
  )}
251
  </ComboboxPrimitive.Chip>
252
- )
253
  }
254
 
255
  function ComboboxChipsInput({
@@ -259,17 +269,14 @@ function ComboboxChipsInput({
259
  return (
260
  <ComboboxPrimitive.Input
261
  data-slot="combobox-chip-input"
262
- className={cn(
263
- "min-w-16 flex-1 outline-none",
264
- className
265
- )}
266
  {...props}
267
  />
268
- )
269
  }
270
 
271
  function useComboboxAnchor() {
272
- return React.useRef<HTMLDivElement | null>(null)
273
  }
274
 
275
  export {
@@ -289,4 +296,4 @@ export {
289
  ComboboxTrigger,
290
  ComboboxValue,
291
  useComboboxAnchor,
292
- }
 
1
+ "use client";
2
 
3
+ import { Combobox as ComboboxPrimitive } from "@base-ui/react";
4
+ import { IconCheck, IconChevronDown, IconX } from "@tabler/icons-react";
5
+ import * as React from "react";
6
+ import { Button } from "@/components/ui/button";
 
7
  import {
8
  InputGroup,
9
  InputGroupAddon,
10
  InputGroupButton,
11
  InputGroupInput,
12
+ } from "@/components/ui/input-group";
13
+ import { cn } from "@/lib/utils";
14
 
15
+ const Combobox = ComboboxPrimitive.Root;
16
 
17
  function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
18
+ return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />;
19
  }
20
 
21
  function ComboboxTrigger({
 
32
  {children}
33
  <IconChevronDown className="text-muted-foreground size-4 pointer-events-none" />
34
  </ComboboxPrimitive.Trigger>
35
+ );
36
  }
37
 
38
  function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
 
45
  >
46
  <IconX className="pointer-events-none" />
47
  </ComboboxPrimitive.Clear>
48
+ );
49
  }
50
 
51
  function ComboboxInput({
 
56
  showClear = false,
57
  ...props
58
  }: ComboboxPrimitive.Input.Props & {
59
+ showTrigger?: boolean;
60
+ showClear?: boolean;
61
  }) {
62
  return (
63
  <InputGroup className={cn("w-auto", className)}>
 
80
  </InputGroupAddon>
81
  {children}
82
  </InputGroup>
83
+ );
84
  }
85
 
86
  function ComboboxContent({
 
109
  <ComboboxPrimitive.Popup
110
  data-slot="combobox-content"
111
  data-chips={!!anchor}
112
+ className={cn(
113
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 *:data-[slot=input-group]:bg-input/30 max-h-72 min-w-36 overflow-hidden rounded-2xl shadow-2xl ring-1 duration-100 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-9 *:data-[slot=input-group]:border-none *:data-[slot=input-group]:shadow-none group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) data-[chips=true]:min-w-(--anchor-width)",
114
+ className,
115
+ )}
116
  {...props}
117
  />
118
  </ComboboxPrimitive.Positioner>
119
  </ComboboxPrimitive.Portal>
120
+ );
121
  }
122
 
123
  function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
 
126
  data-slot="combobox-list"
127
  className={cn(
128
  "no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0 overflow-y-auto overscroll-contain",
129
+ className,
130
  )}
131
  {...props}
132
  />
133
+ );
134
  }
135
 
136
  function ComboboxItem({
 
143
  data-slot="combobox-item"
144
  className={cn(
145
  "data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
146
+ className,
147
  )}
148
  {...props}
149
  >
150
  {children}
151
  <ComboboxPrimitive.ItemIndicator
152
+ render={
153
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
154
+ }
155
  >
156
  <IconCheck className="pointer-events-none" />
157
  </ComboboxPrimitive.ItemIndicator>
158
  </ComboboxPrimitive.Item>
159
+ );
160
  }
161
 
162
  function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
 
166
  className={cn(className)}
167
  {...props}
168
  />
169
+ );
170
  }
171
 
172
  function ComboboxLabel({
 
179
  className={cn("text-muted-foreground px-3.5 py-2.5 text-xs", className)}
180
  {...props}
181
  />
182
+ );
183
  }
184
 
185
  function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
186
  return (
187
  <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
188
+ );
189
  }
190
 
191
  function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
192
  return (
193
  <ComboboxPrimitive.Empty
194
  data-slot="combobox-empty"
195
+ className={cn(
196
+ "text-muted-foreground hidden w-full justify-center py-2 text-center text-sm group-data-empty/combobox-content:flex",
197
+ className,
198
+ )}
199
  {...props}
200
  />
201
+ );
202
  }
203
 
204
  function ComboboxSeparator({
 
211
  className={cn("bg-border/50 -mx-1 my-1 h-px", className)}
212
  {...props}
213
  />
214
+ );
215
  }
216
 
217
  function ComboboxChips({
 
222
  return (
223
  <ComboboxPrimitive.Chips
224
  data-slot="combobox-chips"
225
+ className={cn(
226
+ "bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-9 flex-wrap items-center gap-1.5 rounded-4xl border bg-clip-padding px-2.5 py-1.5 text-sm transition-colors focus-within:ring-[3px] has-aria-invalid:ring-[3px] has-data-[slot=combobox-chip]:px-1.5",
227
+ className,
228
+ )}
229
  {...props}
230
  />
231
+ );
232
  }
233
 
234
  function ComboboxChip({
 
237
  showRemove = true,
238
  ...props
239
  }: ComboboxPrimitive.Chip.Props & {
240
+ showRemove?: boolean;
241
  }) {
242
  return (
243
  <ComboboxPrimitive.Chip
244
  data-slot="combobox-chip"
245
  className={cn(
246
  "bg-muted-foreground/10 text-foreground flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-4xl px-2 text-xs font-medium whitespace-nowrap has-data-[slot=combobox-chip-remove]:pr-0 has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50",
247
+ className,
248
  )}
249
  {...props}
250
  >
 
259
  </ComboboxPrimitive.ChipRemove>
260
  )}
261
  </ComboboxPrimitive.Chip>
262
+ );
263
  }
264
 
265
  function ComboboxChipsInput({
 
269
  return (
270
  <ComboboxPrimitive.Input
271
  data-slot="combobox-chip-input"
272
+ className={cn("min-w-16 flex-1 outline-none", className)}
 
 
 
273
  {...props}
274
  />
275
+ );
276
  }
277
 
278
  function useComboboxAnchor() {
279
+ return React.useRef<HTMLDivElement | null>(null);
280
  }
281
 
282
  export {
 
296
  ComboboxTrigger,
297
  ComboboxValue,
298
  useComboboxAnchor,
299
+ };
src/components/ui/dropdown-menu.tsx CHANGED
@@ -1,19 +1,18 @@
1
- import * as React from "react"
2
- import { Menu as MenuPrimitive } from "@base-ui/react/menu"
3
-
4
- import { cn } from "@/lib/utils"
5
- import { IconChevronRight, IconCheck } from "@tabler/icons-react"
6
 
7
  function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
8
- return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
9
  }
10
 
11
  function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
12
- return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
13
  }
14
 
15
  function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
16
- return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
17
  }
18
 
19
  function DropdownMenuContent({
@@ -39,16 +38,19 @@ function DropdownMenuContent({
39
  >
40
  <MenuPrimitive.Popup
41
  data-slot="dropdown-menu-content"
42
- className={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 bg-popover text-popover-foreground min-w-48 rounded-2xl p-1 shadow-2xl ring-1 duration-100 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden", className )}
 
 
 
43
  {...props}
44
  />
45
  </MenuPrimitive.Positioner>
46
  </MenuPrimitive.Portal>
47
- )
48
  }
49
 
50
  function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
51
- return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
52
  }
53
 
54
  function DropdownMenuLabel({
@@ -56,16 +58,19 @@ function DropdownMenuLabel({
56
  inset,
57
  ...props
58
  }: MenuPrimitive.GroupLabel.Props & {
59
- inset?: boolean
60
  }) {
61
  return (
62
  <MenuPrimitive.GroupLabel
63
  data-slot="dropdown-menu-label"
64
  data-inset={inset}
65
- className={cn("text-muted-foreground px-3 py-2.5 text-xs data-[inset]:pl-8", className)}
 
 
 
66
  {...props}
67
  />
68
- )
69
  }
70
 
71
  function DropdownMenuItem({
@@ -74,8 +79,8 @@ function DropdownMenuItem({
74
  variant = "default",
75
  ...props
76
  }: MenuPrimitive.Item.Props & {
77
- inset?: boolean
78
- variant?: "default" | "destructive"
79
  }) {
80
  return (
81
  <MenuPrimitive.Item
@@ -84,15 +89,15 @@ function DropdownMenuItem({
84
  data-variant={variant}
85
  className={cn(
86
  "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-xl px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
87
- className
88
  )}
89
  {...props}
90
  />
91
- )
92
  }
93
 
94
  function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
95
- return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
96
  }
97
 
98
  function DropdownMenuSubTrigger({
@@ -101,7 +106,7 @@ function DropdownMenuSubTrigger({
101
  children,
102
  ...props
103
  }: MenuPrimitive.SubmenuTrigger.Props & {
104
- inset?: boolean
105
  }) {
106
  return (
107
  <MenuPrimitive.SubmenuTrigger
@@ -109,14 +114,14 @@ function DropdownMenuSubTrigger({
109
  data-inset={inset}
110
  className={cn(
111
  "focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-xl px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
112
- className
113
  )}
114
  {...props}
115
  >
116
  {children}
117
  <IconChevronRight className="ml-auto" />
118
  </MenuPrimitive.SubmenuTrigger>
119
- )
120
  }
121
 
122
  function DropdownMenuSubContent({
@@ -130,14 +135,17 @@ function DropdownMenuSubContent({
130
  return (
131
  <DropdownMenuContent
132
  data-slot="dropdown-menu-sub-content"
133
- className={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 bg-popover text-popover-foreground min-w-36 rounded-2xl p-1 shadow-2xl ring-1 duration-100 w-auto", className)}
 
 
 
134
  align={align}
135
  alignOffset={alignOffset}
136
  side={side}
137
  sideOffset={sideOffset}
138
  {...props}
139
  />
140
- )
141
  }
142
 
143
  function DropdownMenuCheckboxItem({
@@ -151,7 +159,7 @@ function DropdownMenuCheckboxItem({
151
  data-slot="dropdown-menu-checkbox-item"
152
  className={cn(
153
  "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
154
- className
155
  )}
156
  checked={checked}
157
  {...props}
@@ -161,13 +169,12 @@ function DropdownMenuCheckboxItem({
161
  data-slot="dropdown-menu-checkbox-item-indicator"
162
  >
163
  <MenuPrimitive.CheckboxItemIndicator>
164
- <IconCheck
165
- />
166
  </MenuPrimitive.CheckboxItemIndicator>
167
  </span>
168
  {children}
169
  </MenuPrimitive.CheckboxItem>
170
- )
171
  }
172
 
173
  function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
@@ -176,7 +183,7 @@ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
176
  data-slot="dropdown-menu-radio-group"
177
  {...props}
178
  />
179
- )
180
  }
181
 
182
  function DropdownMenuRadioItem({
@@ -189,7 +196,7 @@ function DropdownMenuRadioItem({
189
  data-slot="dropdown-menu-radio-item"
190
  className={cn(
191
  "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
192
- className
193
  )}
194
  {...props}
195
  >
@@ -198,13 +205,12 @@ function DropdownMenuRadioItem({
198
  data-slot="dropdown-menu-radio-item-indicator"
199
  >
200
  <MenuPrimitive.RadioItemIndicator>
201
- <IconCheck
202
- />
203
  </MenuPrimitive.RadioItemIndicator>
204
  </span>
205
  {children}
206
  </MenuPrimitive.RadioItem>
207
- )
208
  }
209
 
210
  function DropdownMenuSeparator({
@@ -217,7 +223,7 @@ function DropdownMenuSeparator({
217
  className={cn("bg-border/50 -mx-1 my-1 h-px", className)}
218
  {...props}
219
  />
220
- )
221
  }
222
 
223
  function DropdownMenuShortcut({
@@ -227,10 +233,13 @@ function DropdownMenuShortcut({
227
  return (
228
  <span
229
  data-slot="dropdown-menu-shortcut"
230
- className={cn("text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest", className)}
 
 
 
231
  {...props}
232
  />
233
- )
234
  }
235
 
236
  export {
@@ -249,4 +258,4 @@ export {
249
  DropdownMenuSub,
250
  DropdownMenuSubTrigger,
251
  DropdownMenuSubContent,
252
- }
 
1
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
2
+ import { IconCheck, IconChevronRight } from "@tabler/icons-react";
3
+ import * as React from "react";
4
+ import { cn } from "@/lib/utils";
 
5
 
6
  function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
7
+ return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
8
  }
9
 
10
  function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
11
+ return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
12
  }
13
 
14
  function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
15
+ return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
16
  }
17
 
18
  function DropdownMenuContent({
 
38
  >
39
  <MenuPrimitive.Popup
40
  data-slot="dropdown-menu-content"
41
+ className={cn(
42
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 bg-popover text-popover-foreground min-w-48 rounded-2xl p-1 shadow-2xl ring-1 duration-100 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden",
43
+ className,
44
+ )}
45
  {...props}
46
  />
47
  </MenuPrimitive.Positioner>
48
  </MenuPrimitive.Portal>
49
+ );
50
  }
51
 
52
  function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
53
+ return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
54
  }
55
 
56
  function DropdownMenuLabel({
 
58
  inset,
59
  ...props
60
  }: MenuPrimitive.GroupLabel.Props & {
61
+ inset?: boolean;
62
  }) {
63
  return (
64
  <MenuPrimitive.GroupLabel
65
  data-slot="dropdown-menu-label"
66
  data-inset={inset}
67
+ className={cn(
68
+ "text-muted-foreground px-3 py-2.5 text-xs data-[inset]:pl-8",
69
+ className,
70
+ )}
71
  {...props}
72
  />
73
+ );
74
  }
75
 
76
  function DropdownMenuItem({
 
79
  variant = "default",
80
  ...props
81
  }: MenuPrimitive.Item.Props & {
82
+ inset?: boolean;
83
+ variant?: "default" | "destructive";
84
  }) {
85
  return (
86
  <MenuPrimitive.Item
 
89
  data-variant={variant}
90
  className={cn(
91
  "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-xl px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
92
+ className,
93
  )}
94
  {...props}
95
  />
96
+ );
97
  }
98
 
99
  function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
100
+ return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
101
  }
102
 
103
  function DropdownMenuSubTrigger({
 
106
  children,
107
  ...props
108
  }: MenuPrimitive.SubmenuTrigger.Props & {
109
+ inset?: boolean;
110
  }) {
111
  return (
112
  <MenuPrimitive.SubmenuTrigger
 
114
  data-inset={inset}
115
  className={cn(
116
  "focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-xl px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
117
+ className,
118
  )}
119
  {...props}
120
  >
121
  {children}
122
  <IconChevronRight className="ml-auto" />
123
  </MenuPrimitive.SubmenuTrigger>
124
+ );
125
  }
126
 
127
  function DropdownMenuSubContent({
 
135
  return (
136
  <DropdownMenuContent
137
  data-slot="dropdown-menu-sub-content"
138
+ className={cn(
139
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 bg-popover text-popover-foreground min-w-36 rounded-2xl p-1 shadow-2xl ring-1 duration-100 w-auto",
140
+ className,
141
+ )}
142
  align={align}
143
  alignOffset={alignOffset}
144
  side={side}
145
  sideOffset={sideOffset}
146
  {...props}
147
  />
148
+ );
149
  }
150
 
151
  function DropdownMenuCheckboxItem({
 
159
  data-slot="dropdown-menu-checkbox-item"
160
  className={cn(
161
  "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
162
+ className,
163
  )}
164
  checked={checked}
165
  {...props}
 
169
  data-slot="dropdown-menu-checkbox-item-indicator"
170
  >
171
  <MenuPrimitive.CheckboxItemIndicator>
172
+ <IconCheck />
 
173
  </MenuPrimitive.CheckboxItemIndicator>
174
  </span>
175
  {children}
176
  </MenuPrimitive.CheckboxItem>
177
+ );
178
  }
179
 
180
  function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
 
183
  data-slot="dropdown-menu-radio-group"
184
  {...props}
185
  />
186
+ );
187
  }
188
 
189
  function DropdownMenuRadioItem({
 
196
  data-slot="dropdown-menu-radio-item"
197
  className={cn(
198
  "focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
199
+ className,
200
  )}
201
  {...props}
202
  >
 
205
  data-slot="dropdown-menu-radio-item-indicator"
206
  >
207
  <MenuPrimitive.RadioItemIndicator>
208
+ <IconCheck />
 
209
  </MenuPrimitive.RadioItemIndicator>
210
  </span>
211
  {children}
212
  </MenuPrimitive.RadioItem>
213
+ );
214
  }
215
 
216
  function DropdownMenuSeparator({
 
223
  className={cn("bg-border/50 -mx-1 my-1 h-px", className)}
224
  {...props}
225
  />
226
+ );
227
  }
228
 
229
  function DropdownMenuShortcut({
 
233
  return (
234
  <span
235
  data-slot="dropdown-menu-shortcut"
236
+ className={cn(
237
+ "text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest",
238
+ className,
239
+ )}
240
  {...props}
241
  />
242
+ );
243
  }
244
 
245
  export {
 
258
  DropdownMenuSub,
259
  DropdownMenuSubTrigger,
260
  DropdownMenuSubContent,
261
+ };
src/components/ui/field.tsx CHANGED
@@ -1,18 +1,20 @@
1
- import { useMemo } from "react"
2
- import { cva, type VariantProps } from "class-variance-authority"
3
-
4
- import { cn } from "@/lib/utils"
5
- import { Label } from "@/components/ui/label"
6
- import { Separator } from "@/components/ui/separator"
7
 
8
  function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
9
  return (
10
  <fieldset
11
  data-slot="field-set"
12
- className={cn("gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col", className)}
 
 
 
13
  {...props}
14
  />
15
- )
16
  }
17
 
18
  function FieldLegend({
@@ -24,10 +26,13 @@ function FieldLegend({
24
  <legend
25
  data-slot="field-legend"
26
  data-variant={variant}
27
- className={cn("mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base", className)}
 
 
 
28
  {...props}
29
  />
30
- )
31
  }
32
 
33
  function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
@@ -36,28 +41,30 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
36
  data-slot="field-group"
37
  className={cn(
38
  "gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4 group/field-group @container/field-group flex w-full flex-col",
39
- className
40
  )}
41
  {...props}
42
  />
43
- )
44
  }
45
 
46
- const fieldVariants = cva("data-[invalid=true]:text-destructive gap-3 group/field flex w-full", {
47
- variants: {
48
- orientation: {
49
- vertical:
50
- "flex-col [&>*]:w-full [&>.sr-only]:w-auto",
51
- horizontal:
52
- "flex-row items-center [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
53
- responsive:
54
- "flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
 
 
 
 
 
55
  },
56
  },
57
- defaultVariants: {
58
- orientation: "vertical",
59
- },
60
- })
61
 
62
  function Field({
63
  className,
@@ -72,7 +79,7 @@ function Field({
72
  className={cn(fieldVariants({ orientation }), className)}
73
  {...props}
74
  />
75
- )
76
  }
77
 
78
  function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
@@ -81,11 +88,11 @@ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
81
  data-slot="field-content"
82
  className={cn(
83
  "gap-1 group/field-content flex flex-1 flex-col leading-snug",
84
- className
85
  )}
86
  {...props}
87
  />
88
- )
89
  }
90
 
91
  function FieldLabel({
@@ -98,11 +105,11 @@ function FieldLabel({
98
  className={cn(
99
  "has-data-checked:bg-primary/5 has-data-checked:border-primary/50 dark:has-data-checked:bg-primary/10 gap-2 group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-xl has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4 group/field-label peer/field-label flex w-fit leading-snug",
100
  "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
101
- className
102
  )}
103
  {...props}
104
  />
105
- )
106
  }
107
 
108
  function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
@@ -111,11 +118,11 @@ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
111
  data-slot="field-label"
112
  className={cn(
113
  "gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug",
114
- className
115
  )}
116
  {...props}
117
  />
118
- )
119
  }
120
 
121
  function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
@@ -126,11 +133,11 @@ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
126
  "text-muted-foreground text-left text-sm [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
127
  "last:mt-0 nth-last-2:-mt-1",
128
  "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
129
- className
130
  )}
131
  {...props}
132
  />
133
- )
134
  }
135
 
136
  function FieldSeparator({
@@ -138,13 +145,16 @@ function FieldSeparator({
138
  className,
139
  ...props
140
  }: React.ComponentProps<"div"> & {
141
- children?: React.ReactNode
142
  }) {
143
  return (
144
  <div
145
  data-slot="field-separator"
146
  data-content={!!children}
147
- className={cn("-my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2 relative", className)}
 
 
 
148
  {...props}
149
  >
150
  <Separator className="absolute inset-0 top-1/2" />
@@ -157,7 +167,7 @@ function FieldSeparator({
157
  </span>
158
  )}
159
  </div>
160
- )
161
  }
162
 
163
  function FieldError({
@@ -166,37 +176,37 @@ function FieldError({
166
  errors,
167
  ...props
168
  }: React.ComponentProps<"div"> & {
169
- errors?: Array<{ message?: string } | undefined>
170
  }) {
171
  const content = useMemo(() => {
172
  if (children) {
173
- return children
174
  }
175
 
176
  if (!errors?.length) {
177
- return null
178
  }
179
 
180
  const uniqueErrors = [
181
  ...new Map(errors.map((error) => [error?.message, error])).values(),
182
- ]
183
 
184
  if (uniqueErrors?.length == 1) {
185
- return uniqueErrors[0]?.message
186
  }
187
 
188
  return (
189
  <ul className="ml-4 flex list-disc flex-col gap-1">
190
  {uniqueErrors.map(
191
  (error, index) =>
192
- error?.message && <li key={index}>{error.message}</li>
193
  )}
194
  </ul>
195
- )
196
- }, [children, errors])
197
 
198
  if (!content) {
199
- return null
200
  }
201
 
202
  return (
@@ -208,7 +218,7 @@ function FieldError({
208
  >
209
  {content}
210
  </div>
211
- )
212
  }
213
 
214
  export {
@@ -222,4 +232,4 @@ export {
222
  FieldSet,
223
  FieldContent,
224
  FieldTitle,
225
- }
 
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import { useMemo } from "react";
3
+ import { Label } from "@/components/ui/label";
4
+ import { Separator } from "@/components/ui/separator";
5
+ import { cn } from "@/lib/utils";
 
6
 
7
  function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
8
  return (
9
  <fieldset
10
  data-slot="field-set"
11
+ className={cn(
12
+ "gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col",
13
+ className,
14
+ )}
15
  {...props}
16
  />
17
+ );
18
  }
19
 
20
  function FieldLegend({
 
26
  <legend
27
  data-slot="field-legend"
28
  data-variant={variant}
29
+ className={cn(
30
+ "mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
31
+ className,
32
+ )}
33
  {...props}
34
  />
35
+ );
36
  }
37
 
38
  function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
 
41
  data-slot="field-group"
42
  className={cn(
43
  "gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4 group/field-group @container/field-group flex w-full flex-col",
44
+ className,
45
  )}
46
  {...props}
47
  />
48
+ );
49
  }
50
 
51
+ const fieldVariants = cva(
52
+ "data-[invalid=true]:text-destructive gap-3 group/field flex w-full",
53
+ {
54
+ variants: {
55
+ orientation: {
56
+ vertical: "flex-col [&>*]:w-full [&>.sr-only]:w-auto",
57
+ horizontal:
58
+ "flex-row items-center [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
59
+ responsive:
60
+ "flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
61
+ },
62
+ },
63
+ defaultVariants: {
64
+ orientation: "vertical",
65
  },
66
  },
67
+ );
 
 
 
68
 
69
  function Field({
70
  className,
 
79
  className={cn(fieldVariants({ orientation }), className)}
80
  {...props}
81
  />
82
+ );
83
  }
84
 
85
  function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
 
88
  data-slot="field-content"
89
  className={cn(
90
  "gap-1 group/field-content flex flex-1 flex-col leading-snug",
91
+ className,
92
  )}
93
  {...props}
94
  />
95
+ );
96
  }
97
 
98
  function FieldLabel({
 
105
  className={cn(
106
  "has-data-checked:bg-primary/5 has-data-checked:border-primary/50 dark:has-data-checked:bg-primary/10 gap-2 group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-xl has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4 group/field-label peer/field-label flex w-fit leading-snug",
107
  "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
108
+ className,
109
  )}
110
  {...props}
111
  />
112
+ );
113
  }
114
 
115
  function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
 
118
  data-slot="field-label"
119
  className={cn(
120
  "gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug",
121
+ className,
122
  )}
123
  {...props}
124
  />
125
+ );
126
  }
127
 
128
  function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
 
133
  "text-muted-foreground text-left text-sm [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
134
  "last:mt-0 nth-last-2:-mt-1",
135
  "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
136
+ className,
137
  )}
138
  {...props}
139
  />
140
+ );
141
  }
142
 
143
  function FieldSeparator({
 
145
  className,
146
  ...props
147
  }: React.ComponentProps<"div"> & {
148
+ children?: React.ReactNode;
149
  }) {
150
  return (
151
  <div
152
  data-slot="field-separator"
153
  data-content={!!children}
154
+ className={cn(
155
+ "-my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2 relative",
156
+ className,
157
+ )}
158
  {...props}
159
  >
160
  <Separator className="absolute inset-0 top-1/2" />
 
167
  </span>
168
  )}
169
  </div>
170
+ );
171
  }
172
 
173
  function FieldError({
 
176
  errors,
177
  ...props
178
  }: React.ComponentProps<"div"> & {
179
+ errors?: Array<{ message?: string } | undefined>;
180
  }) {
181
  const content = useMemo(() => {
182
  if (children) {
183
+ return children;
184
  }
185
 
186
  if (!errors?.length) {
187
+ return null;
188
  }
189
 
190
  const uniqueErrors = [
191
  ...new Map(errors.map((error) => [error?.message, error])).values(),
192
+ ];
193
 
194
  if (uniqueErrors?.length == 1) {
195
+ return uniqueErrors[0]?.message;
196
  }
197
 
198
  return (
199
  <ul className="ml-4 flex list-disc flex-col gap-1">
200
  {uniqueErrors.map(
201
  (error, index) =>
202
+ error?.message && <li key={index}>{error.message}</li>,
203
  )}
204
  </ul>
205
+ );
206
+ }, [children, errors]);
207
 
208
  if (!content) {
209
+ return null;
210
  }
211
 
212
  return (
 
218
  >
219
  {content}
220
  </div>
221
+ );
222
  }
223
 
224
  export {
 
232
  FieldSet,
233
  FieldContent,
234
  FieldTitle,
235
+ };
src/components/ui/input-group.tsx CHANGED
@@ -1,12 +1,11 @@
1
- "use client"
2
 
3
- import * as React from "react"
4
- import { cva, type VariantProps } from "class-variance-authority"
5
-
6
- import { cn } from "@/lib/utils"
7
- import { Button } from "@/components/ui/button"
8
- import { Input } from "@/components/ui/input"
9
- import { Textarea } from "@/components/ui/textarea"
10
 
11
  function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
12
  return (
@@ -15,11 +14,11 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
15
  role="group"
16
  className={cn(
17
  "border-input bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 h-9 rounded-4xl border transition-colors has-data-[align=block-end]:rounded-2xl has-data-[align=block-start]:rounded-2xl has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot][aria-invalid=true]]:ring-[3px] has-[textarea]:rounded-xl has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 [[data-slot=combobox-content]_&]:focus-within:border-inherit [[data-slot=combobox-content]_&]:focus-within:ring-0 group/input-group relative flex w-full min-w-0 items-center outline-none has-[>textarea]:h-auto",
18
- className
19
  )}
20
  {...props}
21
  />
22
- )
23
  }
24
 
25
  const inputGroupAddonVariants = cva(
@@ -27,8 +26,10 @@ const inputGroupAddonVariants = cva(
27
  {
28
  variants: {
29
  align: {
30
- "inline-start": "pl-3 has-[>button]:ml-[-0.25rem] has-[>kbd]:ml-[-0.15rem] order-first",
31
- "inline-end": "pr-3 has-[>button]:mr-[-0.25rem] has-[>kbd]:mr-[-0.15rem] order-last",
 
 
32
  "block-start":
33
  "px-3 pt-3 group-has-[>input]/input-group:pt-3 [.border-b]:pb-3 order-first w-full justify-start",
34
  "block-end":
@@ -38,8 +39,8 @@ const inputGroupAddonVariants = cva(
38
  defaultVariants: {
39
  align: "inline-start",
40
  },
41
- }
42
- )
43
 
44
  function InputGroupAddon({
45
  className,
@@ -54,13 +55,13 @@ function InputGroupAddon({
54
  className={cn(inputGroupAddonVariants({ align }), className)}
55
  onClick={(e) => {
56
  if ((e.target as HTMLElement).closest("button")) {
57
- return
58
  }
59
- e.currentTarget.parentElement?.querySelector("input")?.focus()
60
  }}
61
  {...props}
62
  />
63
- )
64
  }
65
 
66
  const inputGroupButtonVariants = cva(
@@ -77,8 +78,8 @@ const inputGroupButtonVariants = cva(
77
  defaultVariants: {
78
  size: "xs",
79
  },
80
- }
81
- )
82
 
83
  function InputGroupButton({
84
  className,
@@ -88,7 +89,7 @@ function InputGroupButton({
88
  ...props
89
  }: Omit<React.ComponentProps<typeof Button>, "size" | "type"> &
90
  VariantProps<typeof inputGroupButtonVariants> & {
91
- type?: "button" | "submit" | "reset"
92
  }) {
93
  return (
94
  <Button
@@ -98,7 +99,7 @@ function InputGroupButton({
98
  className={cn(inputGroupButtonVariants({ size }), className)}
99
  {...props}
100
  />
101
- )
102
  }
103
 
104
  function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
@@ -106,11 +107,11 @@ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
106
  <span
107
  className={cn(
108
  "text-muted-foreground gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none",
109
- className
110
  )}
111
  {...props}
112
  />
113
- )
114
  }
115
 
116
  function InputGroupInput({
@@ -120,10 +121,13 @@ function InputGroupInput({
120
  return (
121
  <Input
122
  data-slot="input-group-control"
123
- className={cn("rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent flex-1", className)}
 
 
 
124
  {...props}
125
  />
126
- )
127
  }
128
 
129
  function InputGroupTextarea({
@@ -133,10 +137,13 @@ function InputGroupTextarea({
133
  return (
134
  <Textarea
135
  data-slot="input-group-control"
136
- className={cn("rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent flex-1 resize-none", className)}
 
 
 
137
  {...props}
138
  />
139
- )
140
  }
141
 
142
  export {
@@ -146,4 +153,4 @@ export {
146
  InputGroupText,
147
  InputGroupInput,
148
  InputGroupTextarea,
149
- }
 
1
+ "use client";
2
 
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import * as React from "react";
5
+ import { Button } from "@/components/ui/button";
6
+ import { Input } from "@/components/ui/input";
7
+ import { Textarea } from "@/components/ui/textarea";
8
+ import { cn } from "@/lib/utils";
 
9
 
10
  function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
11
  return (
 
14
  role="group"
15
  className={cn(
16
  "border-input bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 h-9 rounded-4xl border transition-colors has-data-[align=block-end]:rounded-2xl has-data-[align=block-start]:rounded-2xl has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot][aria-invalid=true]]:ring-[3px] has-[textarea]:rounded-xl has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 [[data-slot=combobox-content]_&]:focus-within:border-inherit [[data-slot=combobox-content]_&]:focus-within:ring-0 group/input-group relative flex w-full min-w-0 items-center outline-none has-[>textarea]:h-auto",
17
+ className,
18
  )}
19
  {...props}
20
  />
21
+ );
22
  }
23
 
24
  const inputGroupAddonVariants = cva(
 
26
  {
27
  variants: {
28
  align: {
29
+ "inline-start":
30
+ "pl-3 has-[>button]:ml-[-0.25rem] has-[>kbd]:ml-[-0.15rem] order-first",
31
+ "inline-end":
32
+ "pr-3 has-[>button]:mr-[-0.25rem] has-[>kbd]:mr-[-0.15rem] order-last",
33
  "block-start":
34
  "px-3 pt-3 group-has-[>input]/input-group:pt-3 [.border-b]:pb-3 order-first w-full justify-start",
35
  "block-end":
 
39
  defaultVariants: {
40
  align: "inline-start",
41
  },
42
+ },
43
+ );
44
 
45
  function InputGroupAddon({
46
  className,
 
55
  className={cn(inputGroupAddonVariants({ align }), className)}
56
  onClick={(e) => {
57
  if ((e.target as HTMLElement).closest("button")) {
58
+ return;
59
  }
60
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
61
  }}
62
  {...props}
63
  />
64
+ );
65
  }
66
 
67
  const inputGroupButtonVariants = cva(
 
78
  defaultVariants: {
79
  size: "xs",
80
  },
81
+ },
82
+ );
83
 
84
  function InputGroupButton({
85
  className,
 
89
  ...props
90
  }: Omit<React.ComponentProps<typeof Button>, "size" | "type"> &
91
  VariantProps<typeof inputGroupButtonVariants> & {
92
+ type?: "button" | "submit" | "reset";
93
  }) {
94
  return (
95
  <Button
 
99
  className={cn(inputGroupButtonVariants({ size }), className)}
100
  {...props}
101
  />
102
+ );
103
  }
104
 
105
  function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
 
107
  <span
108
  className={cn(
109
  "text-muted-foreground gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none",
110
+ className,
111
  )}
112
  {...props}
113
  />
114
+ );
115
  }
116
 
117
  function InputGroupInput({
 
121
  return (
122
  <Input
123
  data-slot="input-group-control"
124
+ className={cn(
125
+ "rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent flex-1",
126
+ className,
127
+ )}
128
  {...props}
129
  />
130
+ );
131
  }
132
 
133
  function InputGroupTextarea({
 
137
  return (
138
  <Textarea
139
  data-slot="input-group-control"
140
+ className={cn(
141
+ "rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent flex-1 resize-none",
142
+ className,
143
+ )}
144
  {...props}
145
  />
146
+ );
147
  }
148
 
149
  export {
 
153
  InputGroupText,
154
  InputGroupInput,
155
  InputGroupTextarea,
156
+ };
src/components/ui/input.tsx CHANGED
@@ -1,7 +1,7 @@
1
- import * as React from "react"
2
- import { Input as InputPrimitive } from "@base-ui/react/input"
3
 
4
- import { cn } from "@/lib/utils"
5
 
6
  function Input({ className, type, ...props }: React.ComponentProps<"input">) {
7
  return (
@@ -10,11 +10,11 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
10
  data-slot="input"
11
  className={cn(
12
  "bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 rounded-4xl border px-3 py-1 text-base transition-colors file:h-7 file:text-sm file:font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
13
- className
14
  )}
15
  {...props}
16
  />
17
- )
18
  }
19
 
20
- export { Input }
 
1
+ import { Input as InputPrimitive } from "@base-ui/react/input";
2
+ import * as React from "react";
3
 
4
+ import { cn } from "@/lib/utils";
5
 
6
  function Input({ className, type, ...props }: React.ComponentProps<"input">) {
7
  return (
 
10
  data-slot="input"
11
  className={cn(
12
  "bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 rounded-4xl border px-3 py-1 text-base transition-colors file:h-7 file:text-sm file:font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
13
+ className,
14
  )}
15
  {...props}
16
  />
17
+ );
18
  }
19
 
20
+ export { Input };
src/components/ui/label.tsx CHANGED
@@ -1,8 +1,8 @@
1
- "use client"
2
 
3
- import * as React from "react"
4
 
5
- import { cn } from "@/lib/utils"
6
 
7
  function Label({ className, ...props }: React.ComponentProps<"label">) {
8
  return (
@@ -10,11 +10,11 @@ function Label({ className, ...props }: React.ComponentProps<"label">) {
10
  data-slot="label"
11
  className={cn(
12
  "gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed",
13
- className
14
  )}
15
  {...props}
16
  />
17
- )
18
  }
19
 
20
- export { Label }
 
1
+ "use client";
2
 
3
+ import * as React from "react";
4
 
5
+ import { cn } from "@/lib/utils";
6
 
7
  function Label({ className, ...props }: React.ComponentProps<"label">) {
8
  return (
 
10
  data-slot="label"
11
  className={cn(
12
  "gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed",
13
+ className,
14
  )}
15
  {...props}
16
  />
17
+ );
18
  }
19
 
20
+ export { Label };
src/components/ui/select.tsx CHANGED
@@ -1,12 +1,16 @@
1
- "use client"
2
 
3
- import * as React from "react"
4
- import { Select as SelectPrimitive } from "@base-ui/react/select"
 
 
 
 
 
 
 
5
 
6
- import { cn } from "@/lib/utils"
7
- import { IconSelector, IconCheck, IconChevronUp, IconChevronDown } from "@tabler/icons-react"
8
-
9
- const Select = SelectPrimitive.Root
10
 
11
  function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
12
  return (
@@ -15,7 +19,7 @@ function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
15
  className={cn("scroll-my-1 p-1", className)}
16
  {...props}
17
  />
18
- )
19
  }
20
 
21
  function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
@@ -25,7 +29,7 @@ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
25
  className={cn("flex flex-1 text-left", className)}
26
  {...props}
27
  />
28
- )
29
  }
30
 
31
  function SelectTrigger({
@@ -34,7 +38,7 @@ function SelectTrigger({
34
  children,
35
  ...props
36
  }: SelectPrimitive.Trigger.Props & {
37
- size?: "sm" | "default"
38
  }) {
39
  return (
40
  <SelectPrimitive.Trigger
@@ -42,7 +46,7 @@ function SelectTrigger({
42
  data-size={size}
43
  className={cn(
44
  "border-input data-[placeholder]:text-muted-foreground bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-4xl border px-3 py-2 text-sm transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:flex *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
45
- className
46
  )}
47
  {...props}
48
  >
@@ -53,7 +57,7 @@ function SelectTrigger({
53
  }
54
  />
55
  </SelectPrimitive.Trigger>
56
- )
57
  }
58
 
59
  function SelectContent({
@@ -82,7 +86,10 @@ function SelectContent({
82
  >
83
  <SelectPrimitive.Popup
84
  data-slot="select-content"
85
- className={cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 min-w-36 rounded-2xl shadow-2xl ring-1 duration-100 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto", className )}
 
 
 
86
  {...props}
87
  >
88
  <SelectScrollUpButton />
@@ -91,7 +98,7 @@ function SelectContent({
91
  </SelectPrimitive.Popup>
92
  </SelectPrimitive.Positioner>
93
  </SelectPrimitive.Portal>
94
- )
95
  }
96
 
97
  function SelectLabel({
@@ -104,7 +111,7 @@ function SelectLabel({
104
  className={cn("text-muted-foreground px-3 py-2.5 text-xs", className)}
105
  {...props}
106
  />
107
- )
108
  }
109
 
110
  function SelectItem({
@@ -117,7 +124,7 @@ function SelectItem({
117
  data-slot="select-item"
118
  className={cn(
119
  "focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
120
- className
121
  )}
122
  {...props}
123
  >
@@ -125,12 +132,14 @@ function SelectItem({
125
  {children}
126
  </SelectPrimitive.ItemText>
127
  <SelectPrimitive.ItemIndicator
128
- render={<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />}
 
 
129
  >
130
  <IconCheck className="pointer-events-none" />
131
  </SelectPrimitive.ItemIndicator>
132
  </SelectPrimitive.Item>
133
- )
134
  }
135
 
136
  function SelectSeparator({
@@ -140,10 +149,13 @@ function SelectSeparator({
140
  return (
141
  <SelectPrimitive.Separator
142
  data-slot="select-separator"
143
- className={cn("bg-border/50 -mx-1 my-1 h-px pointer-events-none", className)}
 
 
 
144
  {...props}
145
  />
146
- )
147
  }
148
 
149
  function SelectScrollUpButton({
@@ -153,13 +165,15 @@ function SelectScrollUpButton({
153
  return (
154
  <SelectPrimitive.ScrollUpArrow
155
  data-slot="select-scroll-up-button"
156
- className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 top-0 w-full", className)}
 
 
 
157
  {...props}
158
  >
159
- <IconChevronUp
160
- />
161
  </SelectPrimitive.ScrollUpArrow>
162
- )
163
  }
164
 
165
  function SelectScrollDownButton({
@@ -169,13 +183,15 @@ function SelectScrollDownButton({
169
  return (
170
  <SelectPrimitive.ScrollDownArrow
171
  data-slot="select-scroll-down-button"
172
- className={cn("bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 bottom-0 w-full", className)}
 
 
 
173
  {...props}
174
  >
175
- <IconChevronDown
176
- />
177
  </SelectPrimitive.ScrollDownArrow>
178
- )
179
  }
180
 
181
  export {
@@ -189,4 +205,4 @@ export {
189
  SelectSeparator,
190
  SelectTrigger,
191
  SelectValue,
192
- }
 
1
+ "use client";
2
 
3
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
4
+ import {
5
+ IconCheck,
6
+ IconChevronDown,
7
+ IconChevronUp,
8
+ IconSelector,
9
+ } from "@tabler/icons-react";
10
+ import * as React from "react";
11
+ import { cn } from "@/lib/utils";
12
 
13
+ const Select = SelectPrimitive.Root;
 
 
 
14
 
15
  function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
16
  return (
 
19
  className={cn("scroll-my-1 p-1", className)}
20
  {...props}
21
  />
22
+ );
23
  }
24
 
25
  function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
 
29
  className={cn("flex flex-1 text-left", className)}
30
  {...props}
31
  />
32
+ );
33
  }
34
 
35
  function SelectTrigger({
 
38
  children,
39
  ...props
40
  }: SelectPrimitive.Trigger.Props & {
41
+ size?: "sm" | "default";
42
  }) {
43
  return (
44
  <SelectPrimitive.Trigger
 
46
  data-size={size}
47
  className={cn(
48
  "border-input data-[placeholder]:text-muted-foreground bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-4xl border px-3 py-2 text-sm transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:flex *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
49
+ className,
50
  )}
51
  {...props}
52
  >
 
57
  }
58
  />
59
  </SelectPrimitive.Trigger>
60
+ );
61
  }
62
 
63
  function SelectContent({
 
86
  >
87
  <SelectPrimitive.Popup
88
  data-slot="select-content"
89
+ className={cn(
90
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/5 min-w-36 rounded-2xl shadow-2xl ring-1 duration-100 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto",
91
+ className,
92
+ )}
93
  {...props}
94
  >
95
  <SelectScrollUpButton />
 
98
  </SelectPrimitive.Popup>
99
  </SelectPrimitive.Positioner>
100
  </SelectPrimitive.Portal>
101
+ );
102
  }
103
 
104
  function SelectLabel({
 
111
  className={cn("text-muted-foreground px-3 py-2.5 text-xs", className)}
112
  {...props}
113
  />
114
+ );
115
  }
116
 
117
  function SelectItem({
 
124
  data-slot="select-item"
125
  className={cn(
126
  "focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-xl py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
127
+ className,
128
  )}
129
  {...props}
130
  >
 
132
  {children}
133
  </SelectPrimitive.ItemText>
134
  <SelectPrimitive.ItemIndicator
135
+ render={
136
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
137
+ }
138
  >
139
  <IconCheck className="pointer-events-none" />
140
  </SelectPrimitive.ItemIndicator>
141
  </SelectPrimitive.Item>
142
+ );
143
  }
144
 
145
  function SelectSeparator({
 
149
  return (
150
  <SelectPrimitive.Separator
151
  data-slot="select-separator"
152
+ className={cn(
153
+ "bg-border/50 -mx-1 my-1 h-px pointer-events-none",
154
+ className,
155
+ )}
156
  {...props}
157
  />
158
+ );
159
  }
160
 
161
  function SelectScrollUpButton({
 
165
  return (
166
  <SelectPrimitive.ScrollUpArrow
167
  data-slot="select-scroll-up-button"
168
+ className={cn(
169
+ "bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 top-0 w-full",
170
+ className,
171
+ )}
172
  {...props}
173
  >
174
+ <IconChevronUp />
 
175
  </SelectPrimitive.ScrollUpArrow>
176
+ );
177
  }
178
 
179
  function SelectScrollDownButton({
 
183
  return (
184
  <SelectPrimitive.ScrollDownArrow
185
  data-slot="select-scroll-down-button"
186
+ className={cn(
187
+ "bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 bottom-0 w-full",
188
+ className,
189
+ )}
190
  {...props}
191
  >
192
+ <IconChevronDown />
 
193
  </SelectPrimitive.ScrollDownArrow>
194
+ );
195
  }
196
 
197
  export {
 
205
  SelectSeparator,
206
  SelectTrigger,
207
  SelectValue,
208
+ };
src/components/ui/separator.tsx CHANGED
@@ -1,6 +1,6 @@
1
- import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
2
 
3
- import { cn } from "@/lib/utils"
4
 
5
  function Separator({
6
  className,
@@ -13,11 +13,11 @@ function Separator({
13
  orientation={orientation}
14
  className={cn(
15
  "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
16
- className
17
  )}
18
  {...props}
19
  />
20
- )
21
  }
22
 
23
- export { Separator }
 
1
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
2
 
3
+ import { cn } from "@/lib/utils";
4
 
5
  function Separator({
6
  className,
 
13
  orientation={orientation}
14
  className={cn(
15
  "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
16
+ className,
17
  )}
18
  {...props}
19
  />
20
+ );
21
  }
22
 
23
+ export { Separator };
src/components/ui/textarea.tsx CHANGED
@@ -1,6 +1,6 @@
1
- import * as React from "react"
2
 
3
- import { cn } from "@/lib/utils"
4
 
5
  function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6
  return (
@@ -8,11 +8,11 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
8
  data-slot="textarea"
9
  className={cn(
10
  "border-input bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 resize-none rounded-xl border px-3 py-3 text-base transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
11
- className
12
  )}
13
  {...props}
14
  />
15
- )
16
  }
17
 
18
- export { Textarea }
 
1
+ import * as React from "react";
2
 
3
+ import { cn } from "@/lib/utils";
4
 
5
  function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6
  return (
 
8
  data-slot="textarea"
9
  className={cn(
10
  "border-input bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 resize-none rounded-xl border px-3 py-3 text-base transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
11
+ className,
12
  )}
13
  {...props}
14
  />
15
+ );
16
  }
17
 
18
+ export { Textarea };
src/lib/translatorTypes.ts CHANGED
@@ -2,5 +2,5 @@ export type TranslationResult = { translation_text: string };
2
 
3
  export type Translator = (
4
  text: string,
5
- options: { src_lang: string; tgt_lang: string }
6
  ) => Promise<TranslationResult[]>;
 
2
 
3
  export type Translator = (
4
  text: string,
5
+ options: { src_lang: string; tgt_lang: string },
6
  ) => Promise<TranslationResult[]>;
src/lib/utils.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { clsx, type ClassValue } from "clsx"
2
- import { twMerge } from "tailwind-merge"
3
 
4
  export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs))
6
  }
 
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
 
4
  export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
  }
src/main.tsx CHANGED
@@ -1,11 +1,14 @@
1
- import { StrictMode } from "react"
2
- import { createRoot } from "react-dom/client"
3
 
4
- import "./index.css"
5
- import App from "./App.tsx"
6
 
7
- createRoot(document.getElementById("root")!).render(
8
- <StrictMode>
9
- <App />
10
- </StrictMode>
11
- )
 
 
 
 
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
 
4
+ import "./index.css";
5
+ import App from "./App.tsx";
6
 
7
+ const root = document.getElementById("root");
8
+ if (root) {
9
+ createRoot(root).render(
10
+ <StrictMode>
11
+ <App />
12
+ </StrictMode>,
13
+ );
14
+ }
src/store/useColorStore.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { create } from 'zustand'
2
 
3
  interface ColorState {
4
- squareColor: string
5
- setSquareColor: (color: string) => void
6
  }
7
 
8
  export const useColorStore = create<ColorState>((set) => ({
9
- squareColor: 'rebeccapurple', // Default premium color
10
- setSquareColor: (color: string) => set({ squareColor: color }),
11
- }))
 
1
+ import { create } from "zustand";
2
 
3
  interface ColorState {
4
+ squareColor: string;
5
+ setSquareColor: (color: string) => void;
6
  }
7
 
8
  export const useColorStore = create<ColorState>((set) => ({
9
+ squareColor: "rebeccapurple", // Default premium color
10
+ setSquareColor: (color: string) => set({ squareColor: color }),
11
+ }));
src/worker.ts CHANGED
@@ -1,5 +1,5 @@
1
- import * as Comlink from "comlink";
2
  import { pipeline } from "@huggingface/transformers";
 
3
  import type { Translator } from "@/lib/translatorTypes";
4
 
5
  const MODEL_ID = "Xenova/nllb-200-distilled-600M";
@@ -10,7 +10,7 @@ let translatorPromise: Promise<Translator> | null = null;
10
  function getTranslator(): Promise<Translator> {
11
  translatorPromise ??= pipeline(
12
  "translation",
13
- MODEL_ID
14
  ) as unknown as Promise<Translator>;
15
  return translatorPromise;
16
  }
 
 
1
  import { pipeline } from "@huggingface/transformers";
2
+ import * as Comlink from "comlink";
3
  import type { Translator } from "@/lib/translatorTypes";
4
 
5
  const MODEL_ID = "Xenova/nllb-200-distilled-600M";
 
10
  function getTranslator(): Promise<Translator> {
11
  translatorPromise ??= pipeline(
12
  "translation",
13
+ MODEL_ID,
14
  ) as unknown as Promise<Translator>;
15
  return translatorPromise;
16
  }