armand0e commited on
Commit
42116e8
·
1 Parent(s): d00443e
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.png filter=lfs diff=lfs merge=lfs -text
package-lock.json CHANGED
@@ -19,6 +19,7 @@
19
  "react": "18.3.1",
20
  "react-dom": "18.3.1",
21
  "react-markdown": "9.0.3",
 
22
  "tailwind-merge": "2.6.0"
23
  },
24
  "devDependencies": {
@@ -1756,6 +1757,18 @@
1756
  "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
1757
  "license": "MIT"
1758
  },
 
 
 
 
 
 
 
 
 
 
 
 
1759
  "node_modules/estree-util-is-identifier-name": {
1760
  "version": "3.0.0",
1761
  "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
@@ -2460,6 +2473,16 @@
2460
  "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
2461
  }
2462
  },
 
 
 
 
 
 
 
 
 
 
2463
  "node_modules/math-intrinsics": {
2464
  "version": "1.1.0",
2465
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -2469,6 +2492,22 @@
2469
  "node": ">= 0.4"
2470
  }
2471
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2472
  "node_modules/mdast-util-from-markdown": {
2473
  "version": "2.0.2",
2474
  "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
@@ -2493,6 +2532,107 @@
2493
  "url": "https://opencollective.com/unified"
2494
  }
2495
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2496
  "node_modules/mdast-util-mdx-expression": {
2497
  "version": "2.0.1",
2498
  "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
@@ -2722,6 +2862,127 @@
2722
  "micromark-util-types": "^2.0.0"
2723
  }
2724
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2725
  "node_modules/micromark-factory-destination": {
2726
  "version": "2.0.1",
2727
  "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
@@ -3738,6 +3999,24 @@
3738
  "node": ">=8.10.0"
3739
  }
3740
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3741
  "node_modules/remark-parse": {
3742
  "version": "11.0.0",
3743
  "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
@@ -3771,6 +4050,21 @@
3771
  "url": "https://opencollective.com/unified"
3772
  }
3773
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3774
  "node_modules/require-from-string": {
3775
  "version": "2.0.2",
3776
  "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
 
19
  "react": "18.3.1",
20
  "react-dom": "18.3.1",
21
  "react-markdown": "9.0.3",
22
+ "remark-gfm": "^4.0.1",
23
  "tailwind-merge": "2.6.0"
24
  },
25
  "devDependencies": {
 
1757
  "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
1758
  "license": "MIT"
1759
  },
1760
+ "node_modules/escape-string-regexp": {
1761
+ "version": "5.0.0",
1762
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
1763
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
1764
+ "license": "MIT",
1765
+ "engines": {
1766
+ "node": ">=12"
1767
+ },
1768
+ "funding": {
1769
+ "url": "https://github.com/sponsors/sindresorhus"
1770
+ }
1771
+ },
1772
  "node_modules/estree-util-is-identifier-name": {
1773
  "version": "3.0.0",
1774
  "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
 
2473
  "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
2474
  }
2475
  },
2476
+ "node_modules/markdown-table": {
2477
+ "version": "3.0.4",
2478
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
2479
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
2480
+ "license": "MIT",
2481
+ "funding": {
2482
+ "type": "github",
2483
+ "url": "https://github.com/sponsors/wooorm"
2484
+ }
2485
+ },
2486
  "node_modules/math-intrinsics": {
2487
  "version": "1.1.0",
2488
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
 
2492
  "node": ">= 0.4"
2493
  }
2494
  },
2495
+ "node_modules/mdast-util-find-and-replace": {
2496
+ "version": "3.0.2",
2497
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
2498
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
2499
+ "license": "MIT",
2500
+ "dependencies": {
2501
+ "@types/mdast": "^4.0.0",
2502
+ "escape-string-regexp": "^5.0.0",
2503
+ "unist-util-is": "^6.0.0",
2504
+ "unist-util-visit-parents": "^6.0.0"
2505
+ },
2506
+ "funding": {
2507
+ "type": "opencollective",
2508
+ "url": "https://opencollective.com/unified"
2509
+ }
2510
+ },
2511
  "node_modules/mdast-util-from-markdown": {
2512
  "version": "2.0.2",
2513
  "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
 
2532
  "url": "https://opencollective.com/unified"
2533
  }
2534
  },
2535
+ "node_modules/mdast-util-gfm": {
2536
+ "version": "3.1.0",
2537
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
2538
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
2539
+ "license": "MIT",
2540
+ "dependencies": {
2541
+ "mdast-util-from-markdown": "^2.0.0",
2542
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
2543
+ "mdast-util-gfm-footnote": "^2.0.0",
2544
+ "mdast-util-gfm-strikethrough": "^2.0.0",
2545
+ "mdast-util-gfm-table": "^2.0.0",
2546
+ "mdast-util-gfm-task-list-item": "^2.0.0",
2547
+ "mdast-util-to-markdown": "^2.0.0"
2548
+ },
2549
+ "funding": {
2550
+ "type": "opencollective",
2551
+ "url": "https://opencollective.com/unified"
2552
+ }
2553
+ },
2554
+ "node_modules/mdast-util-gfm-autolink-literal": {
2555
+ "version": "2.0.1",
2556
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
2557
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
2558
+ "license": "MIT",
2559
+ "dependencies": {
2560
+ "@types/mdast": "^4.0.0",
2561
+ "ccount": "^2.0.0",
2562
+ "devlop": "^1.0.0",
2563
+ "mdast-util-find-and-replace": "^3.0.0",
2564
+ "micromark-util-character": "^2.0.0"
2565
+ },
2566
+ "funding": {
2567
+ "type": "opencollective",
2568
+ "url": "https://opencollective.com/unified"
2569
+ }
2570
+ },
2571
+ "node_modules/mdast-util-gfm-footnote": {
2572
+ "version": "2.1.0",
2573
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
2574
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
2575
+ "license": "MIT",
2576
+ "dependencies": {
2577
+ "@types/mdast": "^4.0.0",
2578
+ "devlop": "^1.1.0",
2579
+ "mdast-util-from-markdown": "^2.0.0",
2580
+ "mdast-util-to-markdown": "^2.0.0",
2581
+ "micromark-util-normalize-identifier": "^2.0.0"
2582
+ },
2583
+ "funding": {
2584
+ "type": "opencollective",
2585
+ "url": "https://opencollective.com/unified"
2586
+ }
2587
+ },
2588
+ "node_modules/mdast-util-gfm-strikethrough": {
2589
+ "version": "2.0.0",
2590
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
2591
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
2592
+ "license": "MIT",
2593
+ "dependencies": {
2594
+ "@types/mdast": "^4.0.0",
2595
+ "mdast-util-from-markdown": "^2.0.0",
2596
+ "mdast-util-to-markdown": "^2.0.0"
2597
+ },
2598
+ "funding": {
2599
+ "type": "opencollective",
2600
+ "url": "https://opencollective.com/unified"
2601
+ }
2602
+ },
2603
+ "node_modules/mdast-util-gfm-table": {
2604
+ "version": "2.0.0",
2605
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
2606
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
2607
+ "license": "MIT",
2608
+ "dependencies": {
2609
+ "@types/mdast": "^4.0.0",
2610
+ "devlop": "^1.0.0",
2611
+ "markdown-table": "^3.0.0",
2612
+ "mdast-util-from-markdown": "^2.0.0",
2613
+ "mdast-util-to-markdown": "^2.0.0"
2614
+ },
2615
+ "funding": {
2616
+ "type": "opencollective",
2617
+ "url": "https://opencollective.com/unified"
2618
+ }
2619
+ },
2620
+ "node_modules/mdast-util-gfm-task-list-item": {
2621
+ "version": "2.0.0",
2622
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
2623
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
2624
+ "license": "MIT",
2625
+ "dependencies": {
2626
+ "@types/mdast": "^4.0.0",
2627
+ "devlop": "^1.0.0",
2628
+ "mdast-util-from-markdown": "^2.0.0",
2629
+ "mdast-util-to-markdown": "^2.0.0"
2630
+ },
2631
+ "funding": {
2632
+ "type": "opencollective",
2633
+ "url": "https://opencollective.com/unified"
2634
+ }
2635
+ },
2636
  "node_modules/mdast-util-mdx-expression": {
2637
  "version": "2.0.1",
2638
  "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
 
2862
  "micromark-util-types": "^2.0.0"
2863
  }
2864
  },
2865
+ "node_modules/micromark-extension-gfm": {
2866
+ "version": "3.0.0",
2867
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
2868
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
2869
+ "license": "MIT",
2870
+ "dependencies": {
2871
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
2872
+ "micromark-extension-gfm-footnote": "^2.0.0",
2873
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
2874
+ "micromark-extension-gfm-table": "^2.0.0",
2875
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
2876
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
2877
+ "micromark-util-combine-extensions": "^2.0.0",
2878
+ "micromark-util-types": "^2.0.0"
2879
+ },
2880
+ "funding": {
2881
+ "type": "opencollective",
2882
+ "url": "https://opencollective.com/unified"
2883
+ }
2884
+ },
2885
+ "node_modules/micromark-extension-gfm-autolink-literal": {
2886
+ "version": "2.1.0",
2887
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
2888
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
2889
+ "license": "MIT",
2890
+ "dependencies": {
2891
+ "micromark-util-character": "^2.0.0",
2892
+ "micromark-util-sanitize-uri": "^2.0.0",
2893
+ "micromark-util-symbol": "^2.0.0",
2894
+ "micromark-util-types": "^2.0.0"
2895
+ },
2896
+ "funding": {
2897
+ "type": "opencollective",
2898
+ "url": "https://opencollective.com/unified"
2899
+ }
2900
+ },
2901
+ "node_modules/micromark-extension-gfm-footnote": {
2902
+ "version": "2.1.0",
2903
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
2904
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
2905
+ "license": "MIT",
2906
+ "dependencies": {
2907
+ "devlop": "^1.0.0",
2908
+ "micromark-core-commonmark": "^2.0.0",
2909
+ "micromark-factory-space": "^2.0.0",
2910
+ "micromark-util-character": "^2.0.0",
2911
+ "micromark-util-normalize-identifier": "^2.0.0",
2912
+ "micromark-util-sanitize-uri": "^2.0.0",
2913
+ "micromark-util-symbol": "^2.0.0",
2914
+ "micromark-util-types": "^2.0.0"
2915
+ },
2916
+ "funding": {
2917
+ "type": "opencollective",
2918
+ "url": "https://opencollective.com/unified"
2919
+ }
2920
+ },
2921
+ "node_modules/micromark-extension-gfm-strikethrough": {
2922
+ "version": "2.1.0",
2923
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
2924
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
2925
+ "license": "MIT",
2926
+ "dependencies": {
2927
+ "devlop": "^1.0.0",
2928
+ "micromark-util-chunked": "^2.0.0",
2929
+ "micromark-util-classify-character": "^2.0.0",
2930
+ "micromark-util-resolve-all": "^2.0.0",
2931
+ "micromark-util-symbol": "^2.0.0",
2932
+ "micromark-util-types": "^2.0.0"
2933
+ },
2934
+ "funding": {
2935
+ "type": "opencollective",
2936
+ "url": "https://opencollective.com/unified"
2937
+ }
2938
+ },
2939
+ "node_modules/micromark-extension-gfm-table": {
2940
+ "version": "2.1.1",
2941
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
2942
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
2943
+ "license": "MIT",
2944
+ "dependencies": {
2945
+ "devlop": "^1.0.0",
2946
+ "micromark-factory-space": "^2.0.0",
2947
+ "micromark-util-character": "^2.0.0",
2948
+ "micromark-util-symbol": "^2.0.0",
2949
+ "micromark-util-types": "^2.0.0"
2950
+ },
2951
+ "funding": {
2952
+ "type": "opencollective",
2953
+ "url": "https://opencollective.com/unified"
2954
+ }
2955
+ },
2956
+ "node_modules/micromark-extension-gfm-tagfilter": {
2957
+ "version": "2.0.0",
2958
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
2959
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
2960
+ "license": "MIT",
2961
+ "dependencies": {
2962
+ "micromark-util-types": "^2.0.0"
2963
+ },
2964
+ "funding": {
2965
+ "type": "opencollective",
2966
+ "url": "https://opencollective.com/unified"
2967
+ }
2968
+ },
2969
+ "node_modules/micromark-extension-gfm-task-list-item": {
2970
+ "version": "2.1.0",
2971
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
2972
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
2973
+ "license": "MIT",
2974
+ "dependencies": {
2975
+ "devlop": "^1.0.0",
2976
+ "micromark-factory-space": "^2.0.0",
2977
+ "micromark-util-character": "^2.0.0",
2978
+ "micromark-util-symbol": "^2.0.0",
2979
+ "micromark-util-types": "^2.0.0"
2980
+ },
2981
+ "funding": {
2982
+ "type": "opencollective",
2983
+ "url": "https://opencollective.com/unified"
2984
+ }
2985
+ },
2986
  "node_modules/micromark-factory-destination": {
2987
  "version": "2.0.1",
2988
  "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
 
3999
  "node": ">=8.10.0"
4000
  }
4001
  },
4002
+ "node_modules/remark-gfm": {
4003
+ "version": "4.0.1",
4004
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
4005
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
4006
+ "license": "MIT",
4007
+ "dependencies": {
4008
+ "@types/mdast": "^4.0.0",
4009
+ "mdast-util-gfm": "^3.0.0",
4010
+ "micromark-extension-gfm": "^3.0.0",
4011
+ "remark-parse": "^11.0.0",
4012
+ "remark-stringify": "^11.0.0",
4013
+ "unified": "^11.0.0"
4014
+ },
4015
+ "funding": {
4016
+ "type": "opencollective",
4017
+ "url": "https://opencollective.com/unified"
4018
+ }
4019
+ },
4020
  "node_modules/remark-parse": {
4021
  "version": "11.0.0",
4022
  "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
 
4050
  "url": "https://opencollective.com/unified"
4051
  }
4052
  },
4053
+ "node_modules/remark-stringify": {
4054
+ "version": "11.0.0",
4055
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
4056
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
4057
+ "license": "MIT",
4058
+ "dependencies": {
4059
+ "@types/mdast": "^4.0.0",
4060
+ "mdast-util-to-markdown": "^2.0.0",
4061
+ "unified": "^11.0.0"
4062
+ },
4063
+ "funding": {
4064
+ "type": "opencollective",
4065
+ "url": "https://opencollective.com/unified"
4066
+ }
4067
+ },
4068
  "node_modules/require-from-string": {
4069
  "version": "2.0.2",
4070
  "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
package.json CHANGED
@@ -19,6 +19,7 @@
19
  "react": "18.3.1",
20
  "react-dom": "18.3.1",
21
  "react-markdown": "9.0.3",
 
22
  "tailwind-merge": "2.6.0"
23
  },
24
  "devDependencies": {
 
19
  "react": "18.3.1",
20
  "react-dom": "18.3.1",
21
  "react-markdown": "9.0.3",
22
+ "remark-gfm": "^4.0.1",
23
  "tailwind-merge": "2.6.0"
24
  },
25
  "devDependencies": {
public/teich.png ADDED

Git LFS Details

  • SHA256: 4716ceed2a96780e5458604fdf33bb26d34264915c25634d903de42562924318
  • Pointer size: 131 Bytes
  • Size of remote file: 598 kB
src/app/globals.css CHANGED
@@ -3,54 +3,33 @@
3
  @tailwind utilities;
4
 
5
  :root {
6
- --background: 0 0% 100%;
7
- --foreground: 220 13% 18%;
8
- --card: 0 0% 100%;
9
- --card-foreground: 220 13% 18%;
10
- --popover: 0 0% 100%;
11
- --popover-foreground: 220 13% 18%;
12
- --primary: 221 83% 53%;
13
- --primary-foreground: 210 40% 98%;
14
- --secondary: 220 14% 96%;
15
- --secondary-foreground: 220 13% 18%;
16
- --muted: 220 14% 96%;
17
- --muted-foreground: 220 9% 46%;
18
- --accent: 220 14% 96%;
19
- --accent-foreground: 220 13% 18%;
20
- --destructive: 0 84% 60%;
21
- --destructive-foreground: 210 40% 98%;
22
- --border: 220 13% 91%;
23
- --input: 220 13% 91%;
24
- --ring: 221 83% 53%;
25
  --radius: 0.75rem;
26
  }
27
 
28
- .dark {
29
- --background: 224 71% 4%;
30
- --foreground: 213 31% 91%;
31
- --card: 224 71% 4%;
32
- --card-foreground: 213 31% 91%;
33
- --popover: 224 71% 4%;
34
- --popover-foreground: 213 31% 91%;
35
- --primary: 217 91% 60%;
36
- --primary-foreground: 222 47% 11%;
37
- --secondary: 222 47% 11%;
38
- --secondary-foreground: 213 31% 91%;
39
- --muted: 223 47% 11%;
40
- --muted-foreground: 215 20% 65%;
41
- --accent: 216 34% 17%;
42
- --accent-foreground: 213 31% 91%;
43
- --destructive: 0 63% 31%;
44
- --destructive-foreground: 210 40% 98%;
45
- --border: 216 34% 17%;
46
- --input: 216 34% 17%;
47
- --ring: 224 64% 33%;
48
- }
49
-
50
- * {
51
  box-sizing: border-box;
52
- padding: 0;
53
  margin: 0;
 
54
  }
55
 
56
  html {
@@ -59,24 +38,23 @@ html {
59
 
60
  html,
61
  body {
 
62
  max-width: 100vw;
63
- min-height: 100vh;
64
  overflow-x: hidden;
65
- font-feature-settings: "rlig" 1, "calt" 1;
66
  }
67
 
68
  body {
69
- color: hsl(var(--foreground));
70
- background: hsl(var(--background));
71
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
 
 
72
  -webkit-font-smoothing: antialiased;
73
  -moz-osx-font-smoothing: grayscale;
74
  }
75
 
76
- /* Scrollbar styling */
77
  ::-webkit-scrollbar {
78
  width: 6px;
79
- height: 6px;
80
  }
81
 
82
  ::-webkit-scrollbar-track {
@@ -84,27 +62,24 @@ body {
84
  }
85
 
86
  ::-webkit-scrollbar-thumb {
87
- background: hsl(var(--muted-foreground) / 0.2);
88
  border-radius: 3px;
89
  }
90
 
91
  ::-webkit-scrollbar-thumb:hover {
92
- background: hsl(var(--muted-foreground) / 0.4);
93
  }
94
 
95
- /* Selection */
96
  ::selection {
97
- background: hsl(var(--primary) / 0.2);
98
- color: hsl(var(--foreground));
99
  }
100
 
101
- /* Focus styles */
102
  :focus-visible {
103
- outline: 2px solid hsl(var(--ring));
104
  outline-offset: 2px;
105
  }
106
 
107
- /* Animations */
108
  @keyframes fadeIn {
109
  from {
110
  opacity: 0;
@@ -115,10 +90,10 @@ body {
115
  }
116
  }
117
 
118
- @keyframes slideUp {
119
  from {
120
  opacity: 0;
121
- transform: translateY(10px);
122
  }
123
 
124
  to {
@@ -127,157 +102,284 @@ body {
127
  }
128
  }
129
 
130
- @keyframes pulse-subtle {
131
 
132
  0%,
 
133
  100% {
134
- opacity: 1;
135
  }
136
 
137
- 50% {
138
- opacity: 0.7;
139
  }
140
  }
141
 
142
  .animate-fade-in {
143
- animation: fadeIn 0.3s ease-out;
144
  }
145
 
146
- .animate-slide-up {
147
- animation: slideUp 0.4s ease-out;
148
  }
149
 
150
- .animate-pulse-subtle {
151
- animation: pulse-subtle 2s ease-in-out infinite;
 
152
  }
153
 
154
- /* Typography */
155
- .text-balance {
156
- text-wrap: balance;
157
  }
158
 
159
- /* Glass effect */
160
- .glass {
161
- background: hsl(var(--background) / 0.8);
162
- backdrop-filter: blur(12px);
163
- -webkit-backdrop-filter: blur(12px);
164
  }
165
 
166
- /* Gradient text */
167
- .gradient-text {
168
- background: linear-gradient(135deg, hsl(var(--primary)) 0%, hsl(280 80% 60%) 100%);
169
- -webkit-background-clip: text;
170
- -webkit-text-fill-color: transparent;
171
- background-clip: text;
172
  }
173
 
174
- /* Message bubble styles */
175
- .message-user {
176
- background: linear-gradient(135deg, hsl(var(--primary)) 0%, hsl(var(--primary) / 0.8) 100%);
177
  }
178
 
179
- .message-assistant {
180
- background: hsl(var(--secondary));
 
181
  }
182
 
183
- /* Input glow effect */
184
- .input-glow:focus-within {
185
- box-shadow: 0 0 0 3px hsl(var(--primary) / 0.1), 0 0 20px hsl(var(--primary) / 0.1);
 
186
  }
187
 
188
- /* Markdown styling */
189
- .prose {
190
- max-width: none;
191
- line-height: 1.7;
 
 
192
  }
193
 
194
- .prose p {
195
- margin-bottom: 1em;
196
  }
197
 
198
- .prose p:last-child {
199
  margin-bottom: 0;
200
  }
201
 
202
- .prose pre {
203
- background: hsl(var(--muted));
204
- border: 1px solid hsl(var(--border));
205
- border-radius: var(--radius);
206
- padding: 1rem;
207
- overflow-x: auto;
208
- font-size: 0.875rem;
209
- margin: 1rem 0;
210
  }
211
 
212
- .prose code {
213
- background: hsl(var(--muted));
214
- padding: 0.2rem 0.4rem;
215
- border-radius: 0.375rem;
216
- font-size: 0.875em;
217
- font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
218
  }
219
 
220
- .prose pre code {
221
- background: transparent;
222
- padding: 0;
223
- border-radius: 0;
224
  }
225
 
226
- .prose ul,
227
- .prose ol {
228
- padding-left: 1.5rem;
229
- margin: 0.75rem 0;
230
  }
231
 
232
- .prose li {
233
- margin: 0.25rem 0;
234
  }
235
 
236
- .prose blockquote {
237
- border-left: 3px solid hsl(var(--primary));
238
- padding-left: 1rem;
239
- margin: 1rem 0;
240
- color: hsl(var(--muted-foreground));
241
- font-style: italic;
242
  }
243
 
244
- .prose h1,
245
- .prose h2,
246
- .prose h3,
247
- .prose h4 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  font-weight: 600;
249
- margin-top: 1.5em;
250
- margin-bottom: 0.5em;
251
  }
252
 
253
- .prose h1 {
254
- font-size: 1.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  }
256
 
257
- .prose h2 {
258
- font-size: 1.25rem;
 
 
 
 
259
  }
260
 
261
- .prose h3 {
262
- font-size: 1.125rem;
 
 
 
263
  }
264
 
265
- .prose a {
266
- color: hsl(var(--primary));
267
- text-decoration: underline;
268
- text-underline-offset: 2px;
269
  }
270
 
271
- .prose a:hover {
272
- text-decoration-thickness: 2px;
273
  }
274
 
275
- .prose strong {
 
 
276
  font-weight: 600;
 
277
  }
278
 
279
- .prose hr {
280
- border: none;
281
- border-top: 1px solid hsl(var(--border));
282
- margin: 1.5rem 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
 
3
  @tailwind utilities;
4
 
5
  :root {
6
+ --background: #09090b;
7
+ --foreground: #fafafa;
8
+ --muted: #18181b;
9
+ --muted-foreground: #a1a1aa;
10
+ --border: #27272a;
11
+ --card: #18181b;
12
+ --card-foreground: #fafafa;
13
+ --accent: #ff4c00;
14
+ --accent-hover: #ff6a2a;
15
+ --accent-light: rgba(255, 76, 0, 0.15);
16
+ --primary: #ff4c00;
17
+ --primary-foreground: #ffffff;
18
+ --secondary: #18181b;
19
+ --secondary-foreground: #fafafa;
20
+ --destructive: #ef4444;
21
+ --destructive-foreground: #ffffff;
22
+ --input: #27272a;
23
+ --ring: #ff4c00;
 
24
  --radius: 0.75rem;
25
  }
26
 
27
+ *,
28
+ *::before,
29
+ *::after {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  box-sizing: border-box;
 
31
  margin: 0;
32
+ padding: 0;
33
  }
34
 
35
  html {
 
38
 
39
  html,
40
  body {
41
+ height: 100%;
42
  max-width: 100vw;
 
43
  overflow-x: hidden;
 
44
  }
45
 
46
  body {
47
+ color: var(--foreground);
48
+ background: var(--background);
49
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
50
+ font-size: 15px;
51
+ line-height: 1.6;
52
  -webkit-font-smoothing: antialiased;
53
  -moz-osx-font-smoothing: grayscale;
54
  }
55
 
 
56
  ::-webkit-scrollbar {
57
  width: 6px;
 
58
  }
59
 
60
  ::-webkit-scrollbar-track {
 
62
  }
63
 
64
  ::-webkit-scrollbar-thumb {
65
+ background: #3f3f46;
66
  border-radius: 3px;
67
  }
68
 
69
  ::-webkit-scrollbar-thumb:hover {
70
+ background: #52525b;
71
  }
72
 
 
73
  ::selection {
74
+ background: var(--accent-light);
75
+ color: var(--foreground);
76
  }
77
 
 
78
  :focus-visible {
79
+ outline: 2px solid var(--ring);
80
  outline-offset: 2px;
81
  }
82
 
 
83
  @keyframes fadeIn {
84
  from {
85
  opacity: 0;
 
90
  }
91
  }
92
 
93
+ @keyframes slideIn {
94
  from {
95
  opacity: 0;
96
+ transform: translateY(8px);
97
  }
98
 
99
  to {
 
102
  }
103
  }
104
 
105
+ @keyframes bounce-dot {
106
 
107
  0%,
108
+ 80%,
109
  100% {
110
+ transform: scale(0);
111
  }
112
 
113
+ 40% {
114
+ transform: scale(1);
115
  }
116
  }
117
 
118
  .animate-fade-in {
119
+ animation: fadeIn 0.2s ease-out;
120
  }
121
 
122
+ .animate-slide-in {
123
+ animation: slideIn 0.3s ease-out;
124
  }
125
 
126
+ .dot-1 {
127
+ animation: bounce-dot 1.4s infinite ease-in-out both;
128
+ animation-delay: -0.32s;
129
  }
130
 
131
+ .dot-2 {
132
+ animation: bounce-dot 1.4s infinite ease-in-out both;
133
+ animation-delay: -0.16s;
134
  }
135
 
136
+ .dot-3 {
137
+ animation: bounce-dot 1.4s infinite ease-in-out both;
 
 
 
138
  }
139
 
140
+ .input-container {
141
+ background: var(--card);
142
+ border: 1px solid var(--border);
143
+ border-radius: 12px;
144
+ transition: border-color 0.2s, box-shadow 0.2s;
 
145
  }
146
 
147
+ .input-container:focus-within {
148
+ border-color: var(--accent);
149
+ box-shadow: 0 0 0 3px var(--accent-light);
150
  }
151
 
152
+ .user-message {
153
+ background: var(--accent);
154
+ color: var(--primary-foreground);
155
  }
156
 
157
+ .markdown-content {
158
+ font-size: 15px;
159
+ line-height: 1.7;
160
+ color: var(--foreground);
161
  }
162
 
163
+ .markdown-content>*:first-child {
164
+ margin-top: 0;
165
+ }
166
+
167
+ .markdown-content>*:last-child {
168
+ margin-bottom: 0;
169
  }
170
 
171
+ .markdown-content p {
172
+ margin: 0 0 1em 0;
173
  }
174
 
175
+ .markdown-content p:last-child {
176
  margin-bottom: 0;
177
  }
178
 
179
+ .markdown-content h1,
180
+ .markdown-content h2,
181
+ .markdown-content h3,
182
+ .markdown-content h4 {
183
+ font-weight: 600;
184
+ line-height: 1.3;
185
+ margin: 1.5em 0 0.5em 0;
186
+ color: var(--foreground);
187
  }
188
 
189
+ .markdown-content h1:first-child,
190
+ .markdown-content h2:first-child,
191
+ .markdown-content h3:first-child,
192
+ .markdown-content h4:first-child {
193
+ margin-top: 0;
 
194
  }
195
 
196
+ .markdown-content h1 {
197
+ font-size: 1.5em;
 
 
198
  }
199
 
200
+ .markdown-content h2 {
201
+ font-size: 1.25em;
 
 
202
  }
203
 
204
+ .markdown-content h3 {
205
+ font-size: 1.1em;
206
  }
207
 
208
+ .markdown-content h4 {
209
+ font-size: 1em;
 
 
 
 
210
  }
211
 
212
+ .markdown-content ul,
213
+ .markdown-content ol {
214
+ margin: 0.75em 0;
215
+ padding-left: 1.5em;
216
+ }
217
+
218
+ .markdown-content li {
219
+ margin: 0.35em 0;
220
+ }
221
+
222
+ .markdown-content li>ul,
223
+ .markdown-content li>ol {
224
+ margin: 0.25em 0;
225
+ }
226
+
227
+ .markdown-content a {
228
+ color: var(--accent);
229
+ text-decoration: none;
230
+ border-bottom: 1px solid transparent;
231
+ transition: border-color 0.15s;
232
+ }
233
+
234
+ .markdown-content a:hover {
235
+ border-bottom-color: var(--accent);
236
+ }
237
+
238
+ .markdown-content strong {
239
  font-weight: 600;
 
 
240
  }
241
 
242
+ .markdown-content em {
243
+ font-style: italic;
244
+ }
245
+
246
+ .markdown-content blockquote {
247
+ margin: 1em 0;
248
+ padding: 0.5em 0 0.5em 1em;
249
+ border-left: 3px solid var(--accent);
250
+ color: var(--muted-foreground);
251
+ font-style: italic;
252
+ }
253
+
254
+ .markdown-content hr {
255
+ margin: 1.5em 0;
256
+ border: none;
257
+ border-top: 1px solid var(--border);
258
+ }
259
+
260
+ .markdown-content .code-block {
261
+ margin: 1em 0;
262
+ padding: 1em;
263
+ background: var(--muted);
264
+ border: 1px solid var(--border);
265
+ border-radius: 8px;
266
+ overflow-x: auto;
267
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace;
268
+ font-size: 0.875em;
269
+ line-height: 1.5;
270
  }
271
 
272
+ .markdown-content .inline-code {
273
+ padding: 0.15em 0.4em;
274
+ background: var(--muted);
275
+ border-radius: 4px;
276
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace;
277
+ font-size: 0.9em;
278
  }
279
 
280
+ .markdown-content .table-wrapper {
281
+ margin: 1em 0;
282
+ overflow-x: auto;
283
+ border: 1px solid var(--border);
284
+ border-radius: 8px;
285
  }
286
 
287
+ .markdown-content table {
288
+ width: 100%;
289
+ border-collapse: collapse;
290
+ font-size: 0.9em;
291
  }
292
 
293
+ .markdown-content thead {
294
+ background: var(--muted);
295
  }
296
 
297
+ .markdown-content th {
298
+ padding: 0.75em 1em;
299
+ text-align: left;
300
  font-weight: 600;
301
+ border-bottom: 1px solid var(--border);
302
  }
303
 
304
+ .markdown-content td {
305
+ padding: 0.75em 1em;
306
+ border-bottom: 1px solid var(--border);
307
+ }
308
+
309
+ .markdown-content tbody tr:last-child td {
310
+ border-bottom: none;
311
+ }
312
+
313
+ .markdown-content tbody tr:hover {
314
+ background: rgba(255, 255, 255, 0.02);
315
+ }
316
+
317
+ .thinking-block {
318
+ margin: 0.5em 0;
319
+ border: 1px solid var(--border);
320
+ border-radius: 8px;
321
+ overflow: hidden;
322
+ }
323
+
324
+ .thinking-header {
325
+ display: flex;
326
+ align-items: center;
327
+ gap: 0.5em;
328
+ padding: 0.625em 0.875em;
329
+ background: var(--muted);
330
+ cursor: pointer;
331
+ -webkit-user-select: none;
332
+ user-select: none;
333
+ font-size: 0.875em;
334
+ color: var(--muted-foreground);
335
+ transition: background 0.15s;
336
+ }
337
+
338
+ .thinking-header:hover {
339
+ background: #27272a;
340
+ }
341
+
342
+ .thinking-content {
343
+ padding: 0.875em;
344
+ font-size: 0.875em;
345
+ color: var(--muted-foreground);
346
+ line-height: 1.6;
347
+ white-space: pre-wrap;
348
+ border-top: 1px solid var(--border);
349
+ background: var(--background);
350
+ }
351
+
352
+ .tool-block {
353
+ margin: 0.5em 0;
354
+ border: 1px solid var(--border);
355
+ border-radius: 8px;
356
+ overflow: hidden;
357
+ }
358
+
359
+ .tool-header {
360
+ display: flex;
361
+ align-items: center;
362
+ gap: 0.625em;
363
+ padding: 0.625em 0.875em;
364
+ background: var(--muted);
365
+ cursor: pointer;
366
+ -webkit-user-select: none;
367
+ user-select: none;
368
+ font-size: 0.875em;
369
+ }
370
+
371
+ .tool-header:hover {
372
+ background: #27272a;
373
+ }
374
+
375
+ .tool-content {
376
+ padding: 0.875em;
377
+ font-size: 0.8125em;
378
+ color: var(--muted-foreground);
379
+ line-height: 1.5;
380
+ white-space: pre-wrap;
381
+ border-top: 1px solid var(--border);
382
+ max-height: 200px;
383
+ overflow-y: auto;
384
+ background: var(--background);
385
  }
src/components/chat.tsx CHANGED
@@ -1,10 +1,7 @@
1
  "use client";
2
 
3
  import { useState, useRef, useEffect } from "react";
4
- import { Globe, Send, Loader2, RotateCcw, Sparkles, MessageSquare } from "lucide-react";
5
- import { Button } from "@/components/ui/button";
6
- import { ReasoningBlock } from "@/components/reasoning-block";
7
- import { ToolInvocation } from "@/components/tool-invocation";
8
  import { Markdown } from "@/components/markdown";
9
  import { cn } from "@/lib/utils";
10
 
@@ -22,6 +19,93 @@ interface Message {
22
  }>;
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  export function Chat() {
26
  const [messages, setMessages] = useState<Message[]>([]);
27
  const [input, setInput] = useState("");
@@ -39,12 +123,11 @@ export function Chat() {
39
  scrollToBottom();
40
  }, [messages, streamingMessage]);
41
 
42
- // Auto-resize textarea
43
  useEffect(() => {
44
  const textarea = textareaRef.current;
45
  if (textarea) {
46
  textarea.style.height = "auto";
47
- textarea.style.height = Math.min(textarea.scrollHeight, 200) + "px";
48
  }
49
  }, [input]);
50
 
@@ -169,7 +252,6 @@ export function Chat() {
169
  }
170
  }
171
 
172
- // Finalize message
173
  setStreamingMessage((prev) => {
174
  if (prev) {
175
  setMessages((msgs) => [...msgs, prev]);
@@ -198,62 +280,75 @@ export function Chat() {
198
 
199
  const hasMessages = messages.length > 0 || streamingMessage;
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  return (
202
  <div className="flex flex-col h-screen bg-background">
203
  {/* Header */}
204
- <header className="sticky top-0 z-50 glass border-b border-border/50">
205
- <div className="max-w-3xl mx-auto px-4 h-14 flex items-center justify-between">
206
- <div className="flex items-center gap-3">
207
- <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
208
- <Sparkles className="w-4 h-4 text-white" />
209
- </div>
210
- <div>
211
- <h1 className="text-sm font-semibold">Qwen3 Claude Opus</h1>
212
- <p className="text-xs text-muted-foreground">Reasoning Model</p>
213
- </div>
214
  </div>
215
  {hasMessages && (
216
- <Button
217
- variant="ghost"
218
- size="sm"
219
  onClick={clearChat}
220
- className="text-muted-foreground hover:text-foreground gap-2"
221
  >
222
- <RotateCcw className="h-4 w-4" />
223
- <span className="hidden sm:inline">New Chat</span>
224
- </Button>
225
  )}
226
  </div>
227
  </header>
228
 
229
- {/* Messages Area */}
230
- <div className="flex-1 overflow-y-auto">
231
- <div className="max-w-3xl mx-auto px-4 py-6">
232
- {/* Empty State */}
233
  {!hasMessages && (
234
- <div className="flex flex-col items-center justify-center min-h-[60vh] text-center animate-fade-in">
235
- <div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-blue-500/20 to-purple-600/20 flex items-center justify-center mb-6">
236
- <MessageSquare className="w-8 h-8 text-blue-500" />
237
- </div>
238
- <h2 className="text-2xl font-semibold mb-2 text-balance">
239
- How can I help you today?
240
- </h2>
241
- <p className="text-muted-foreground max-w-md text-balance">
242
- I'm a reasoning model that thinks through problems step by step.
243
- Toggle web search for real-time information.
244
  </p>
245
-
246
- {/* Quick Actions */}
247
- <div className="flex flex-wrap gap-2 mt-8 justify-center">
248
- {[
249
- "Explain quantum computing",
250
- "Write a Python function",
251
- "What's the latest news?",
252
- ].map((prompt) => (
253
  <button
254
  key={prompt}
255
  onClick={() => setInput(prompt)}
256
- className="px-4 py-2 text-sm rounded-full border border-border hover:bg-accent hover:border-accent transition-colors"
257
  >
258
  {prompt}
259
  </button>
@@ -262,160 +357,92 @@ export function Chat() {
262
  </div>
263
  )}
264
 
265
- {/* Messages */}
266
  <div className="space-y-6">
267
  {messages.map((message) => (
268
- <div
269
- key={message.id}
270
- className={cn(
271
- "animate-slide-up",
272
- message.role === "user" ? "flex justify-end" : ""
273
- )}
274
- >
275
  {message.role === "user" ? (
276
- <div className="message-user text-white px-4 py-3 rounded-2xl rounded-br-md max-w-[85%] shadow-lg">
277
- <p className="text-sm leading-relaxed whitespace-pre-wrap">{message.content}</p>
 
 
278
  </div>
279
  ) : (
280
- <div className="space-y-3 max-w-full">
281
- {message.reasoning && (
282
- <ReasoningBlock
283
- content={message.reasoning}
284
- duration={message.reasoningDuration}
285
- />
286
- )}
287
- {message.toolInvocations?.map((tool, idx) => (
288
- <ToolInvocation
289
- key={idx}
290
- toolName={tool.toolName}
291
- args={tool.args}
292
- result={tool.result}
293
- status={tool.status}
294
- />
295
- ))}
296
- {message.content && (
297
- <div className="prose text-foreground">
298
- <Markdown>{message.content}</Markdown>
299
- </div>
300
- )}
301
- </div>
302
  )}
303
  </div>
304
  ))}
305
 
306
- {/* Streaming message */}
307
  {streamingMessage && (
308
- <div className="space-y-3 animate-fade-in">
309
- {streamingMessage.reasoning && (
310
- <ReasoningBlock
311
- content={streamingMessage.reasoning}
312
- isStreaming={!streamingMessage.reasoningDuration}
313
- duration={streamingMessage.reasoningDuration}
314
- />
315
- )}
316
- {streamingMessage.toolInvocations?.map((tool, idx) => (
317
- <ToolInvocation
318
- key={idx}
319
- toolName={tool.toolName}
320
- args={tool.args}
321
- result={tool.result}
322
- status={tool.status}
323
- />
324
- ))}
325
- {streamingMessage.content && (
326
- <div className="prose text-foreground">
327
- <Markdown>{streamingMessage.content}</Markdown>
328
- </div>
329
- )}
330
- {!streamingMessage.content && !streamingMessage.reasoning && (
331
- <div className="flex items-center gap-3 text-muted-foreground py-2">
332
- <div className="flex gap-1">
333
- <span className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" style={{ animationDelay: "0ms" }} />
334
- <span className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" style={{ animationDelay: "150ms" }} />
335
- <span className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" style={{ animationDelay: "300ms" }} />
336
- </div>
337
- <span className="text-sm">Thinking...</span>
338
- </div>
339
- )}
340
  </div>
341
  )}
342
  </div>
343
 
344
  <div ref={messagesEndRef} className="h-4" />
345
  </div>
346
- </div>
347
-
348
- {/* Input Area */}
349
- <div className="sticky bottom-0 glass border-t border-border/50">
350
- <div className="max-w-3xl mx-auto px-4 py-4">
351
- <form onSubmit={handleSubmit} className="relative">
352
- <div className={cn(
353
- "flex items-end gap-2 p-2 rounded-2xl border border-border bg-background/50 transition-all duration-200",
354
- "input-glow focus-within:border-primary/50"
355
- )}>
356
- {/* Web Search Toggle */}
357
  <button
358
  type="button"
359
  onClick={() => setSearchEnabled(!searchEnabled)}
360
  className={cn(
361
- "shrink-0 w-10 h-10 rounded-xl flex items-center justify-center transition-all duration-200",
362
  searchEnabled
363
- ? "bg-blue-500 text-white shadow-lg shadow-blue-500/25"
364
- : "bg-muted text-muted-foreground hover:bg-accent hover:text-foreground"
365
  )}
366
- title={searchEnabled ? "Web search enabled" : "Enable web search"}
367
  >
368
- <Globe className="w-5 h-5" />
369
  </button>
370
 
371
- {/* Textarea */}
372
  <textarea
373
  ref={textareaRef}
374
  value={input}
375
  onChange={(e) => setInput(e.target.value)}
376
  onKeyDown={handleKeyDown}
377
- placeholder="Message Qwen3 Claude Opus..."
378
  rows={1}
379
  disabled={isLoading}
380
- className={cn(
381
- "flex-1 bg-transparent border-0 resize-none text-sm leading-6",
382
- "placeholder:text-muted-foreground focus:outline-none focus:ring-0",
383
- "min-h-[40px] max-h-[200px] py-2 px-1"
384
- )}
385
  />
386
 
387
- {/* Send Button */}
388
  <button
389
  type="submit"
390
  disabled={isLoading || !input.trim()}
391
  className={cn(
392
- "shrink-0 w-10 h-10 rounded-xl flex items-center justify-center transition-all duration-200",
393
  input.trim() && !isLoading
394
- ? "bg-primary text-primary-foreground shadow-lg shadow-primary/25 hover:opacity-90"
395
- : "bg-muted text-muted-foreground cursor-not-allowed"
396
  )}
397
  >
398
  {isLoading ? (
399
- <Loader2 className="w-5 h-5 animate-spin" />
400
  ) : (
401
- <Send className="w-5 h-5" />
402
  )}
403
  </button>
404
  </div>
405
 
406
- {/* Status Bar */}
407
- <div className="flex items-center justify-center gap-4 mt-3 text-xs text-muted-foreground">
408
  {searchEnabled && (
409
  <span className="flex items-center gap-1.5">
410
- <span className="w-1.5 h-1.5 bg-green-500 rounded-full animate-pulse" />
411
- Web search enabled
412
  </span>
413
  )}
414
- <span>Press Enter to send, Shift+Enter for new line</span>
415
  </div>
416
  </form>
417
  </div>
418
- </div>
419
  </div>
420
  );
421
  }
 
1
  "use client";
2
 
3
  import { useState, useRef, useEffect } from "react";
4
+ import { Globe, Send, Loader2, Plus, ChevronDown, ChevronRight } from "lucide-react";
 
 
 
5
  import { Markdown } from "@/components/markdown";
6
  import { cn } from "@/lib/utils";
7
 
 
19
  }>;
20
  }
21
 
22
+ function ThinkingBlock({
23
+ content,
24
+ duration,
25
+ isStreaming
26
+ }: {
27
+ content: string;
28
+ duration?: number;
29
+ isStreaming?: boolean;
30
+ }) {
31
+ const [isOpen, setIsOpen] = useState(isStreaming);
32
+
33
+ useEffect(() => {
34
+ if (isStreaming) setIsOpen(true);
35
+ }, [isStreaming]);
36
+
37
+ const formatDuration = (s?: number) => {
38
+ if (!s) return "";
39
+ if (s < 1) return " · <1s";
40
+ return ` · ${Math.round(s)}s`;
41
+ };
42
+
43
+ return (
44
+ <div className="thinking-block">
45
+ <button
46
+ className="thinking-header w-full"
47
+ onClick={() => setIsOpen(!isOpen)}
48
+ >
49
+ {isOpen ? <ChevronDown className="w-4 h-4" /> : <ChevronRight className="w-4 h-4" />}
50
+ <span>
51
+ {isStreaming ? "Thinking..." : `Thought process${formatDuration(duration)}`}
52
+ </span>
53
+ {isStreaming && (
54
+ <span className="ml-auto flex gap-1">
55
+ <span className="w-1.5 h-1.5 rounded-full bg-current dot-1" />
56
+ <span className="w-1.5 h-1.5 rounded-full bg-current dot-2" />
57
+ <span className="w-1.5 h-1.5 rounded-full bg-current dot-3" />
58
+ </span>
59
+ )}
60
+ </button>
61
+ {isOpen && (
62
+ <div className="thinking-content">{content}</div>
63
+ )}
64
+ </div>
65
+ );
66
+ }
67
+
68
+ function ToolBlock({
69
+ toolName,
70
+ args,
71
+ result,
72
+ status
73
+ }: {
74
+ toolName: string;
75
+ args: Record<string, unknown>;
76
+ result?: string;
77
+ status: string;
78
+ }) {
79
+ const [isOpen, setIsOpen] = useState(false);
80
+ const isRunning = status === "running" || status === "pending";
81
+ const query = (args.query as string) || JSON.stringify(args);
82
+
83
+ return (
84
+ <div className="tool-block">
85
+ <button
86
+ className="tool-header w-full"
87
+ onClick={() => setIsOpen(!isOpen)}
88
+ >
89
+ {isRunning ? (
90
+ <Loader2 className="w-4 h-4 animate-spin text-blue-500" />
91
+ ) : (
92
+ <Globe className="w-4 h-4 text-green-500" />
93
+ )}
94
+ <span className="font-medium">
95
+ {toolName === "web_search" ? "Web Search" : toolName}
96
+ </span>
97
+ <span className="text-muted-foreground truncate flex-1 text-left ml-1">
98
+ {query}
99
+ </span>
100
+ {isOpen ? <ChevronDown className="w-4 h-4 shrink-0" /> : <ChevronRight className="w-4 h-4 shrink-0" />}
101
+ </button>
102
+ {isOpen && result && (
103
+ <div className="tool-content">{result}</div>
104
+ )}
105
+ </div>
106
+ );
107
+ }
108
+
109
  export function Chat() {
110
  const [messages, setMessages] = useState<Message[]>([]);
111
  const [input, setInput] = useState("");
 
123
  scrollToBottom();
124
  }, [messages, streamingMessage]);
125
 
 
126
  useEffect(() => {
127
  const textarea = textareaRef.current;
128
  if (textarea) {
129
  textarea.style.height = "auto";
130
+ textarea.style.height = Math.min(textarea.scrollHeight, 160) + "px";
131
  }
132
  }, [input]);
133
 
 
252
  }
253
  }
254
 
 
255
  setStreamingMessage((prev) => {
256
  if (prev) {
257
  setMessages((msgs) => [...msgs, prev]);
 
280
 
281
  const hasMessages = messages.length > 0 || streamingMessage;
282
 
283
+ const renderMessage = (message: Message, isStreaming = false) => (
284
+ <div className="space-y-3">
285
+ {message.reasoning && (
286
+ <ThinkingBlock
287
+ content={message.reasoning}
288
+ duration={message.reasoningDuration}
289
+ isStreaming={isStreaming && !message.reasoningDuration}
290
+ />
291
+ )}
292
+ {message.toolInvocations?.map((tool, idx) => (
293
+ <ToolBlock
294
+ key={idx}
295
+ toolName={tool.toolName}
296
+ args={tool.args}
297
+ result={tool.result}
298
+ status={tool.status}
299
+ />
300
+ ))}
301
+ {message.content && (
302
+ <Markdown>{message.content}</Markdown>
303
+ )}
304
+ {isStreaming && !message.content && !message.reasoning && (
305
+ <div className="flex items-center gap-2 py-2 text-muted-foreground">
306
+ <span className="flex gap-1">
307
+ <span className="w-2 h-2 rounded-full bg-current dot-1" />
308
+ <span className="w-2 h-2 rounded-full bg-current dot-2" />
309
+ <span className="w-2 h-2 rounded-full bg-current dot-3" />
310
+ </span>
311
+ </div>
312
+ )}
313
+ </div>
314
+ );
315
+
316
  return (
317
  <div className="flex flex-col h-screen bg-background">
318
  {/* Header */}
319
+ <header className="shrink-0 border-b border-[#27272a] bg-[#09090b]">
320
+ <div className="max-w-3xl mx-auto px-4 sm:px-6 h-14 flex items-center justify-between">
321
+ <div className="flex items-center gap-2.5">
322
+ <img src="/teich.png" alt="Teich AI" className="w-8 h-8" />
323
+ <span className="font-semibold text-[#fafafa]">Qwen3-4B-Thinking-2507-Claude-4.5-Opus</span>
 
 
 
 
 
324
  </div>
325
  {hasMessages && (
326
+ <button
 
 
327
  onClick={clearChat}
328
+ className="flex items-center gap-1.5 px-3 py-1.5 text-sm text-[#a1a1aa] hover:text-[#fafafa] hover:bg-[#27272a] rounded-md transition-colors"
329
  >
330
+ <Plus className="w-4 h-4" />
331
+ New Chat
332
+ </button>
333
  )}
334
  </div>
335
  </header>
336
 
337
+ {/* Messages */}
338
+ <main className="flex-1 overflow-y-auto">
339
+ <div className="max-w-3xl mx-auto px-4 sm:px-6 py-6">
 
340
  {!hasMessages && (
341
+ <div className="flex flex-col items-center justify-center min-h-[50vh] text-center animate-fade-in">
342
+ <h2 className="text-xl font-semibold mb-2 text-[#fafafa]">How can I help you?</h2>
343
+ <p className="text-[#a1a1aa] text-sm max-w-sm mb-8">
344
+ A reasoning model that thinks step by step. Enable web search for real-time information.
 
 
 
 
 
 
345
  </p>
346
+ <div className="flex flex-wrap gap-2 justify-center">
347
+ {["Explain quantum computing", "Write a Python function", "What's in the news?"].map((prompt) => (
 
 
 
 
 
 
348
  <button
349
  key={prompt}
350
  onClick={() => setInput(prompt)}
351
+ className="px-3 py-1.5 text-sm border border-[#27272a] text-[#a1a1aa] rounded-md hover:bg-[#18181b] hover:text-[#fafafa] hover:border-[#3f3f46] transition-colors"
352
  >
353
  {prompt}
354
  </button>
 
357
  </div>
358
  )}
359
 
 
360
  <div className="space-y-6">
361
  {messages.map((message) => (
362
+ <div key={message.id} className="animate-slide-in">
 
 
 
 
 
 
363
  {message.role === "user" ? (
364
+ <div className="flex justify-end">
365
+ <div className="user-message px-4 py-2.5 rounded-2xl rounded-br-sm max-w-[85%]">
366
+ <p className="text-sm whitespace-pre-wrap">{message.content}</p>
367
+ </div>
368
  </div>
369
  ) : (
370
+ renderMessage(message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  )}
372
  </div>
373
  ))}
374
 
 
375
  {streamingMessage && (
376
+ <div className="animate-fade-in">
377
+ {renderMessage(streamingMessage, true)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  </div>
379
  )}
380
  </div>
381
 
382
  <div ref={messagesEndRef} className="h-4" />
383
  </div>
384
+ </main>
385
+
386
+ {/* Input */}
387
+ <footer className="shrink-0 border-t border-[#27272a] bg-[#09090b]">
388
+ <div className="max-w-3xl mx-auto px-4 sm:px-6 py-4">
389
+ <form onSubmit={handleSubmit}>
390
+ <div className="input-container flex items-end gap-2 p-2">
 
 
 
 
391
  <button
392
  type="button"
393
  onClick={() => setSearchEnabled(!searchEnabled)}
394
  className={cn(
395
+ "shrink-0 w-9 h-9 rounded-lg flex items-center justify-center transition-colors",
396
  searchEnabled
397
+ ? "bg-[#ff4c00] text-white"
398
+ : "text-[#a1a1aa] hover:text-[#fafafa] hover:bg-[#27272a]"
399
  )}
400
+ title={searchEnabled ? "Disable web search" : "Enable web search"}
401
  >
402
+ <Globe className="w-4 h-4" />
403
  </button>
404
 
 
405
  <textarea
406
  ref={textareaRef}
407
  value={input}
408
  onChange={(e) => setInput(e.target.value)}
409
  onKeyDown={handleKeyDown}
410
+ placeholder="Send a message..."
411
  rows={1}
412
  disabled={isLoading}
413
+ className="flex-1 bg-transparent border-0 resize-none text-sm leading-6 text-[#fafafa] placeholder:text-[#71717a] focus:outline-none min-h-[36px] max-h-[160px] py-1.5"
 
 
 
 
414
  />
415
 
 
416
  <button
417
  type="submit"
418
  disabled={isLoading || !input.trim()}
419
  className={cn(
420
+ "shrink-0 w-9 h-9 rounded-lg flex items-center justify-center transition-colors",
421
  input.trim() && !isLoading
422
+ ? "bg-[#ff4c00] text-white hover:bg-[#ff6a2a]"
423
+ : "text-[#a1a1aa] cursor-not-allowed"
424
  )}
425
  >
426
  {isLoading ? (
427
+ <Loader2 className="w-4 h-4 animate-spin" />
428
  ) : (
429
+ <Send className="w-4 h-4" />
430
  )}
431
  </button>
432
  </div>
433
 
434
+ <div className="flex items-center justify-center gap-3 mt-2 text-xs text-[#a1a1aa]">
 
435
  {searchEnabled && (
436
  <span className="flex items-center gap-1.5">
437
+ <span className="w-1.5 h-1.5 bg-[#ff4c00] rounded-full" />
438
+ Web search on
439
  </span>
440
  )}
441
+ <span>Enter to send · Shift+Enter for newline</span>
442
  </div>
443
  </form>
444
  </div>
445
+ </footer>
446
  </div>
447
  );
448
  }
src/components/markdown.tsx CHANGED
@@ -1,6 +1,7 @@
1
  "use client";
2
 
3
  import ReactMarkdown from "react-markdown";
 
4
  import { cn } from "@/lib/utils";
5
 
6
  interface MarkdownProps {
@@ -11,48 +12,46 @@ interface MarkdownProps {
11
  export function Markdown({ children, className }: MarkdownProps) {
12
  return (
13
  <ReactMarkdown
14
- className={cn("prose max-w-none", className)}
 
15
  components={{
16
  pre: ({ children }) => (
17
- <pre className="bg-muted/50 border border-border rounded-xl p-4 overflow-x-auto my-4 text-sm">
18
- {children}
19
- </pre>
20
  ),
21
  code: ({ children, className: codeClassName }) => {
22
  const isInline = !codeClassName;
23
  if (isInline) {
24
- return (
25
- <code className="bg-muted/70 px-1.5 py-0.5 rounded-md text-sm font-mono">
26
- {children}
27
- </code>
28
- );
29
  }
30
  return <code className={codeClassName}>{children}</code>;
31
  },
32
- p: ({ children }) => <p className="mb-4 last:mb-0 leading-7">{children}</p>,
33
- ul: ({ children }) => <ul className="list-disc pl-6 mb-4 space-y-1">{children}</ul>,
34
- ol: ({ children }) => <ol className="list-decimal pl-6 mb-4 space-y-1">{children}</ol>,
35
- li: ({ children }) => <li className="leading-7">{children}</li>,
36
- h1: ({ children }) => <h1 className="text-xl font-semibold mb-4 mt-6 first:mt-0">{children}</h1>,
37
- h2: ({ children }) => <h2 className="text-lg font-semibold mb-3 mt-5 first:mt-0">{children}</h2>,
38
- h3: ({ children }) => <h3 className="text-base font-semibold mb-2 mt-4 first:mt-0">{children}</h3>,
39
- blockquote: ({ children }) => (
40
- <blockquote className="border-l-3 border-primary/50 pl-4 italic my-4 text-muted-foreground">
41
- {children}
42
- </blockquote>
43
- ),
44
  a: ({ children, href }) => (
45
- <a
46
- href={href}
47
- target="_blank"
48
- rel="noopener noreferrer"
49
- className="text-primary hover:underline underline-offset-2"
50
- >
51
  {children}
52
  </a>
53
  ),
54
- strong: ({ children }) => <strong className="font-semibold">{children}</strong>,
55
- hr: () => <hr className="my-6 border-border" />,
 
 
 
 
 
 
 
 
 
 
 
56
  }}
57
  >
58
  {children}
 
1
  "use client";
2
 
3
  import ReactMarkdown from "react-markdown";
4
+ import remarkGfm from "remark-gfm";
5
  import { cn } from "@/lib/utils";
6
 
7
  interface MarkdownProps {
 
12
  export function Markdown({ children, className }: MarkdownProps) {
13
  return (
14
  <ReactMarkdown
15
+ className={cn("markdown-content", className)}
16
+ remarkPlugins={[remarkGfm]}
17
  components={{
18
  pre: ({ children }) => (
19
+ <pre className="code-block">{children}</pre>
 
 
20
  ),
21
  code: ({ children, className: codeClassName }) => {
22
  const isInline = !codeClassName;
23
  if (isInline) {
24
+ return <code className="inline-code">{children}</code>;
 
 
 
 
25
  }
26
  return <code className={codeClassName}>{children}</code>;
27
  },
28
+ p: ({ children }) => <p>{children}</p>,
29
+ ul: ({ children }) => <ul>{children}</ul>,
30
+ ol: ({ children }) => <ol>{children}</ol>,
31
+ li: ({ children }) => <li>{children}</li>,
32
+ h1: ({ children }) => <h1>{children}</h1>,
33
+ h2: ({ children }) => <h2>{children}</h2>,
34
+ h3: ({ children }) => <h3>{children}</h3>,
35
+ h4: ({ children }) => <h4>{children}</h4>,
36
+ blockquote: ({ children }) => <blockquote>{children}</blockquote>,
 
 
 
37
  a: ({ children, href }) => (
38
+ <a href={href} target="_blank" rel="noopener noreferrer">
 
 
 
 
 
39
  {children}
40
  </a>
41
  ),
42
+ strong: ({ children }) => <strong>{children}</strong>,
43
+ em: ({ children }) => <em>{children}</em>,
44
+ hr: () => <hr />,
45
+ table: ({ children }) => (
46
+ <div className="table-wrapper">
47
+ <table>{children}</table>
48
+ </div>
49
+ ),
50
+ thead: ({ children }) => <thead>{children}</thead>,
51
+ tbody: ({ children }) => <tbody>{children}</tbody>,
52
+ tr: ({ children }) => <tr>{children}</tr>,
53
+ th: ({ children }) => <th>{children}</th>,
54
+ td: ({ children }) => <td>{children}</td>,
55
  }}
56
  >
57
  {children}
teich.png ADDED

Git LFS Details

  • SHA256: 4716ceed2a96780e5458604fdf33bb26d34264915c25634d903de42562924318
  • Pointer size: 131 Bytes
  • Size of remote file: 598 kB