monorepotime 1.1.11 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -24075,11 +24075,11 @@ var require_lib3 = __commonJS({
24075
24075
  var headers = [], method = req.method && req.method.toUpperCase && req.method.toUpperCase();
24076
24076
  if (method === "OPTIONS") {
24077
24077
  headers.push(configureOrigin(options, req));
24078
- headers.push(configureCredentials(options, req));
24079
- headers.push(configureMethods(options, req));
24078
+ headers.push(configureCredentials(options));
24079
+ headers.push(configureMethods(options));
24080
24080
  headers.push(configureAllowedHeaders(options, req));
24081
- headers.push(configureMaxAge(options, req));
24082
- headers.push(configureExposedHeaders(options, req));
24081
+ headers.push(configureMaxAge(options));
24082
+ headers.push(configureExposedHeaders(options));
24083
24083
  applyHeaders(headers, res);
24084
24084
  if (options.preflightContinue) {
24085
24085
  next();
@@ -24090,8 +24090,8 @@ var require_lib3 = __commonJS({
24090
24090
  }
24091
24091
  } else {
24092
24092
  headers.push(configureOrigin(options, req));
24093
- headers.push(configureCredentials(options, req));
24094
- headers.push(configureExposedHeaders(options, req));
24093
+ headers.push(configureCredentials(options));
24094
+ headers.push(configureExposedHeaders(options));
24095
24095
  applyHeaders(headers, res);
24096
24096
  next();
24097
24097
  }
@@ -55247,7 +55247,7 @@ __export(index_exports, {
55247
55247
  io: () => io2
55248
55248
  });
55249
55249
  module.exports = __toCommonJS(index_exports);
55250
- var import_express24 = __toESM(require_express2());
55250
+ var import_express25 = __toESM(require_express2());
55251
55251
  var import_cors = __toESM(require_lib3());
55252
55252
  var import_path19 = __toESM(require("path"));
55253
55253
 
@@ -56729,12 +56729,33 @@ var EXCLUDE_PATTERNS = {
56729
56729
  "**/turbo.json": true,
56730
56730
  "**/nodemon.json": true,
56731
56731
  "**/temp.md": true,
56732
- "**/*postcss*": true,
56733
- "**/*tailwind*": true,
56734
- "**/*tsconfig*": true,
56735
- "**/*eslint*": true,
56736
- "**/*prettier*": true,
56737
- "**/*vite*": true,
56732
+ "**/postcss.config.js": true,
56733
+ "**/configs": true,
56734
+ "**/tailwind.config.js": true,
56735
+ "**/tsconfig.app.json": true,
56736
+ "**/tsconfig.json": true,
56737
+ "**/tsconfig.node.json": true,
56738
+ "**/tsconfig.spec.json": true,
56739
+ "**/eslint.json": true,
56740
+ "**/eslint.config.js": true,
56741
+ "**/eslint.config.mjs": true,
56742
+ "**/prettier.config.js": true,
56743
+ "**/prettier.config.mjs": true,
56744
+ "**/prettier.config.cjs": true,
56745
+ "**/vite.config.ts": true,
56746
+ "**/vite.config.js": true,
56747
+ "**/tsup.config.ts": true,
56748
+ "**/rollup.config.ts": true,
56749
+ "**/rollup.config.js": true,
56750
+ "**/webpack.config.js": true,
56751
+ "**/babel.config.js": true,
56752
+ "**/jest.config.js": true,
56753
+ "**/jest.config.ts": true,
56754
+ "**/next.config.js": true,
56755
+ "**/next.config.mjs": true,
56756
+ "**/postcss.config.cjs": true,
56757
+ "**/postcss.config.mjs": true,
56758
+ "**/tailwind.config.ts": true,
56738
56759
  "_temp": true,
56739
56760
  ".gitignore": true,
56740
56761
  ".vscode": true,
@@ -57624,7 +57645,7 @@ router17.post("/stop-all", async (req, res) => {
57624
57645
  var apidocker_default = router17;
57625
57646
 
57626
57647
  // src/routes/availabletemplates.ts
57627
- var import_express21 = __toESM(require_express2());
57648
+ var import_express22 = __toESM(require_express2());
57628
57649
 
57629
57650
  // ../../packages/template/databases/mysql.ts
57630
57651
  var MySQL = {
@@ -57666,6 +57687,11 @@ const EDITOR_URL = 'http://localhost/phpmyadmin'; // Change this to your preferr
57666
57687
  cmd: "npm",
57667
57688
  args: ["pkg", "set", "scripts.stop=echo 'Note: MySQL is running as a system service. Please stop it manually.'"]
57668
57689
  },
57690
+ {
57691
+ action: "command",
57692
+ cmd: "npm",
57693
+ args: ["pkg", "set", "description=MySQL (Local)"]
57694
+ },
57669
57695
  {
57670
57696
  action: "command",
57671
57697
  cmd: "npm",
@@ -57861,7 +57887,12 @@ process.on('SIGTERM', cleanup);`
57861
57887
  {
57862
57888
  action: "command",
57863
57889
  cmd: "npm",
57864
- args: ["pkg", "set", "fontawesomeIcon=fas fa-database text-blue-300"]
57890
+ args: ["pkg", "set", "description=PostgreSQL (Docker)"]
57891
+ },
57892
+ {
57893
+ action: "command",
57894
+ cmd: "npm",
57895
+ args: ["pkg", "set", "fontawesomeIcon=fas fa-database text-blue-500"]
57865
57896
  }
57866
57897
  ]
57867
57898
  };
@@ -57895,7 +57926,12 @@ var Supabase = {
57895
57926
  {
57896
57927
  action: "command",
57897
57928
  cmd: "npm",
57898
- args: ["pkg", "set", "fontawesomeIcon=fas fa-bolt text-green-400"]
57929
+ args: ["pkg", "set", "description=Supabase (Docker)"]
57930
+ },
57931
+ {
57932
+ action: "command",
57933
+ cmd: "npm",
57934
+ args: ["pkg", "set", "fontawesomeIcon=fas fa-bolt text-green-500"]
57899
57935
  }
57900
57936
  ]
57901
57937
  };
@@ -58050,7 +58086,12 @@ process.on('SIGTERM', cleanup);`
58050
58086
  {
58051
58087
  action: "command",
58052
58088
  cmd: "npm",
58053
- args: ["pkg", "set", "fontawesomeIcon=fas fa-server text-red-400"]
58089
+ args: ["pkg", "set", "description=Redis (Docker)"]
58090
+ },
58091
+ {
58092
+ action: "command",
58093
+ cmd: "npm",
58094
+ args: ["pkg", "set", "fontawesomeIcon=fas fa-server text-red-500"]
58054
58095
  }
58055
58096
  ]
58056
58097
  };
@@ -58205,6 +58246,11 @@ process.on('SIGTERM', cleanup);`
58205
58246
  cmd: "npm",
58206
58247
  args: ["pkg", "set", `scripts.stop=node -e 'const fs=require("fs"); try{const p=JSON.parse(fs.readFileSync(".runtime.json")).port; fetch("http://localhost:"+p+"/stop").catch(e=>{})}catch(e){}'`]
58207
58248
  },
58249
+ {
58250
+ action: "command",
58251
+ cmd: "npm",
58252
+ args: ["pkg", "set", "description=MongoDB (Docker)"]
58253
+ },
58208
58254
  {
58209
58255
  action: "command",
58210
58256
  cmd: "npm",
@@ -58362,6 +58408,11 @@ process.on('SIGTERM', cleanup);`
58362
58408
  cmd: "npm",
58363
58409
  args: ["pkg", "set", "fontawesomeIcon=fab fa-meetup text-green-500"]
58364
58410
  },
58411
+ {
58412
+ action: "command",
58413
+ cmd: "npm",
58414
+ args: ["pkg", "set", "description=Meilisearch (Docker)"]
58415
+ },
58365
58416
  {
58366
58417
  action: "command",
58367
58418
  cmd: "npm",
@@ -58537,6 +58588,11 @@ process.on('SIGTERM', cleanup);`
58537
58588
  cmd: "npm",
58538
58589
  args: ["pkg", "set", "fontawesomeIcon=fab fa-amazon text-blue-500"]
58539
58590
  },
58591
+ {
58592
+ action: "command",
58593
+ cmd: "npm",
58594
+ args: ["pkg", "set", "description=MinIO (Docker)"]
58595
+ },
58540
58596
  {
58541
58597
  action: "command",
58542
58598
  cmd: "npm",
@@ -58557,1534 +58613,297 @@ var templates = [
58557
58613
  ];
58558
58614
  var database_default = templates;
58559
58615
 
58560
- // ../../packages/template/demo/aichat/files/indexHtml.ts
58561
- var indexHTML = `<!DOCTYPE html>
58562
- <html lang="en">
58563
- <head>
58564
- <meta charset="UTF-8">
58565
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
58566
- <title>FreshFruit - Premium Organic Fruits</title>
58567
- <meta name="description" content="Fresh organic fruits delivered to your doorstep. Premium quality, farm-to-table freshness.">
58568
- <link rel="preconnect" href="https://fonts.googleapis.com">
58569
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
58570
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
58571
- <link rel="stylesheet" href="/styles.css">
58572
- </head>
58573
- <body>
58574
- <!-- Header -->
58575
- <header class="header">
58576
- <div class="header-content">
58577
- <div class="logo">\u{1F34A} FreshFruit</div>
58578
- <nav>
58579
- <ul class="nav-links">
58580
- <li><a href="#products">Products</a></li>
58581
- <li><a href="#about">About</a></li>
58582
- <li><a href="#contact">Contact</a></li>
58583
- </ul>
58584
- </nav>
58585
- </div>
58586
- </header>
58587
-
58588
- <!-- Hero Section -->
58589
- <section class="hero">
58590
- <h1>Fresh Organic Fruits Delivered Daily</h1>
58591
- <p>Experience the finest selection of farm-fresh fruits, handpicked and delivered straight to your doorstep. Taste the difference of truly organic produce.</p>
58592
- <button class="cta-button">Shop Now</button>
58593
- </section>
58594
-
58595
- <!-- Products Section -->
58596
- <section class="products" id="products">
58597
- <h2>Our Fresh Selection</h2>
58598
- <div class="products-grid">
58599
- <div class="product-card">
58600
- <div class="product-image">\u{1F34E}</div>
58601
- <div class="product-info">
58602
- <h3>Organic Apples</h3>
58603
- <p>Crisp and sweet, straight from our partner orchards</p>
58604
- <span class="product-price">$4.99/lb</span>
58605
- </div>
58606
- </div>
58607
- <div class="product-card">
58608
- <div class="product-image">\u{1F34A}</div>
58609
- <div class="product-info">
58610
- <h3>Valencia Oranges</h3>
58611
- <p>Juicy and vitamin-packed, perfect for fresh juice</p>
58612
- <span class="product-price">$5.49/lb</span>
58613
- </div>
58614
- </div>
58615
- <div class="product-card">
58616
- <div class="product-image">\u{1F347}</div>
58617
- <div class="product-info">
58618
- <h3>Premium Grapes</h3>
58619
- <p>Seedless and bursting with natural sweetness</p>
58620
- <span class="product-price">$6.99/lb</span>
58621
- </div>
58622
- </div>
58623
- <div class="product-card">
58624
- <div class="product-image">\u{1F96D}</div>
58625
- <div class="product-info">
58626
- <h3>Alphonso Mangoes</h3>
58627
- <p>The king of fruits, rich and aromatic</p>
58628
- <span class="product-price">$8.99/lb</span>
58629
- </div>
58630
- </div>
58631
- <div class="product-card">
58632
- <div class="product-image">\u{1F353}</div>
58633
- <div class="product-info">
58634
- <h3>Fresh Strawberries</h3>
58635
- <p>Hand-picked at peak ripeness for maximum flavor</p>
58636
- <span class="product-price">$7.49/lb</span>
58637
- </div>
58638
- </div>
58639
- <div class="product-card">
58640
- <div class="product-image">\u{1F34C}</div>
58641
- <div class="product-info">
58642
- <h3>Organic Bananas</h3>
58643
- <p>Naturally ripened, perfect for smoothies</p>
58644
- <span class="product-price">$2.99/lb</span>
58645
- </div>
58646
- </div>
58647
- </div>
58648
- </section>
58649
-
58650
- <!-- Chat Widget -->
58651
- <div class="chat-widget">
58652
- <div class="chat-window" id="chatWindow">
58653
- <div class="chat-header">
58654
- <div class="chat-header-avatar">\u{1F34A}</div>
58655
- <div class="chat-header-info">
58656
- <h4>FreshFruit Support</h4>
58657
- <span>We typically reply instantly</span>
58658
- </div>
58659
- </div>
58660
- <div class="chat-messages" id="chatMessages">
58661
- <div class="message bot">
58662
- Hi! \u{1F44B} Welcome to FreshFruit! How can I help you today? I can answer questions about our products, delivery, or anything else!
58663
- </div>
58664
- </div>
58665
- <div class="chat-input-container">
58666
- <input type="text" class="chat-input" id="chatInput" placeholder="Type your message..." autocomplete="off">
58667
- <button class="chat-send" id="chatSend">
58668
- <svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
58669
- </button>
58670
- </div>
58671
- </div>
58672
- <button class="chat-toggle" id="chatToggle">
58673
- <svg viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
58674
- </button>
58675
- </div>
58676
-
58677
- <script>
58678
- // Chat Widget Functionality
58679
- const chatToggle = document.getElementById('chatToggle');
58680
- const chatWindow = document.getElementById('chatWindow');
58681
- const chatInput = document.getElementById('chatInput');
58682
- const chatSend = document.getElementById('chatSend');
58683
- const chatMessages = document.getElementById('chatMessages');
58684
-
58685
- // Toggle chat window
58686
- chatToggle.addEventListener('click', () => {
58687
- chatWindow.classList.toggle('open');
58688
- if (chatWindow.classList.contains('open')) {
58689
- chatInput.focus();
58690
- }
58691
- });
58692
-
58693
- // Send message function
58694
- async function sendMessage() {
58695
- const message = chatInput.value.trim();
58696
- if (!message) return;
58697
-
58698
- // Add user message
58699
- addMessage(message, 'user');
58700
- chatInput.value = '';
58701
-
58702
- // Show typing indicator
58703
- const typingEl = showTyping();
58704
-
58705
- try {
58706
- const response = await fetch('/api/chat', {
58707
- method: 'POST',
58708
- headers: { 'Content-Type': 'application/json' },
58709
- body: JSON.stringify({ message })
58710
- });
58711
-
58712
- const data = await response.json();
58713
- typingEl.remove();
58714
-
58715
- if (data.reply) {
58716
- addMessage(data.reply, 'bot');
58717
- } else {
58718
- addMessage('Sorry, I encountered an error. Please try again.', 'bot');
58719
- }
58720
- } catch (error) {
58721
- typingEl.remove();
58722
- addMessage('Sorry, I\\'m having trouble connecting. Please try again later.', 'bot');
58723
- }
58724
- }
58725
-
58726
- // Add message to chat
58727
- function addMessage(text, type) {
58728
- const messageEl = document.createElement('div');
58729
- messageEl.className = 'message ' + type;
58730
- messageEl.textContent = text;
58731
- chatMessages.appendChild(messageEl);
58732
- chatMessages.scrollTop = chatMessages.scrollHeight;
58733
- }
58734
-
58735
- // Show typing indicator
58736
- function showTyping() {
58737
- const typingEl = document.createElement('div');
58738
- typingEl.className = 'typing-indicator';
58739
- typingEl.innerHTML = '<span></span><span></span><span></span>';
58740
- chatMessages.appendChild(typingEl);
58741
- chatMessages.scrollTop = chatMessages.scrollHeight;
58742
- return typingEl;
58743
- }
58744
-
58745
- // Event listeners
58746
- chatSend.addEventListener('click', sendMessage);
58747
- chatInput.addEventListener('keypress', (e) => {
58748
- if (e.key === 'Enter') sendMessage();
58749
- });
58750
- </script>
58751
- </body>
58752
- </html>
58753
- `;
58754
-
58755
- // ../../packages/template/demo/aichat/files/adminHtml.ts
58756
- var adminHTML = `<!DOCTYPE html>
58757
- <html lang="en">
58758
- <head>
58759
- <meta charset="UTF-8">
58760
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
58761
- <title>Admin Panel - AI Chat Configuration</title>
58762
- <link rel="preconnect" href="https://fonts.googleapis.com">
58763
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
58764
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
58765
- <link rel="stylesheet" href="/styles.css">
58766
- </head>
58767
- <body>
58768
- <header class="header">
58769
- <div class="header-content">
58770
- <div class="logo">\u{1F34A} FreshFruit Admin</div>
58771
- <nav>
58772
- <ul class="nav-links">
58773
- <li><a href="/">View Store</a></li>
58774
- </ul>
58775
- </nav>
58776
- </div>
58777
- </header>
58778
-
58779
- <div class="admin-container">
58780
- <div class="admin-header">
58781
- <h1>AI Chat Configuration</h1>
58782
- <p>Configure your AI provider and embed FAQ knowledge for customer support</p>
58783
- </div>
58784
-
58785
- <!-- AI Provider Configuration -->
58786
- <div class="admin-card">
58787
- <h2>\u{1F511} AI Provider Settings</h2>
58788
- <form id="configForm">
58789
- <div class="form-group">
58790
- <label for="apiKey">API Key</label>
58791
- <input type="password" class="form-input" id="apiKey" placeholder="sk-..." autocomplete="off">
58792
- </div>
58793
- <div class="form-group">
58794
- <label for="providerUrl">Chat Completions URL</label>
58795
- <input type="text" class="form-input" id="providerUrl" placeholder="https://api.openai.com/v1/chat/completions">
58796
- </div>
58797
- <div class="form-group">
58798
- <label for="embeddingsUrl">Embeddings URL</label>
58799
- <input type="text" class="form-input" id="embeddingsUrl" placeholder="https://api.openai.com/v1/embeddings">
58800
- </div>
58801
- <div class="form-group">
58802
- <label for="model">Chat Model</label>
58803
- <input type="text" class="form-input" id="model" placeholder="gpt-3.5-turbo">
58804
- </div>
58805
- <div class="form-group">
58806
- <label for="embeddingsModel">Embeddings Model</label>
58807
- <input type="text" class="form-input" id="embeddingsModel" placeholder="text-embedding-3-small">
58808
- </div>
58809
- <button type="submit" class="btn">\u{1F4BE} Save Configuration</button>
58810
- </form>
58811
- </div>
58812
-
58813
- <!-- Knowledge Base Embedding -->
58814
- <div class="admin-card">
58815
- <h2>\u{1F4DA} Knowledge Base</h2>
58816
- <p style="color: var(--text-muted); margin-bottom: 1.5rem;">
58817
- Enter your FAQ content below. Each paragraph will be embedded and used to answer customer questions.
58818
- </p>
58819
- <form id="embedForm">
58820
- <div class="form-group">
58821
- <label for="faqContent">FAQ Content</label>
58822
- <textarea class="form-input" id="faqContent" placeholder="Enter your FAQ content here...
58823
-
58824
- Example:
58825
- Q: What are your delivery hours?
58826
- A: We deliver from 8 AM to 8 PM, Monday through Saturday.
58827
-
58828
- Q: How do I return a product?
58829
- A: You can return any product within 24 hours of delivery if you're not satisfied. Contact our support team.
58830
-
58831
- Q: Do you offer organic certification?
58832
- A: Yes, all our products are certified organic by USDA."></textarea>
58833
- </div>
58834
- <button type="submit" class="btn" id="embedBtn">\u{1F52E} Embed Knowledge</button>
58835
- </form>
58836
- <div class="status-bar">
58837
- <div class="status-item">
58838
- <div class="value" id="embeddingCount">0</div>
58839
- <div class="label">Embeddings</div>
58840
- </div>
58841
- <div class="status-item">
58842
- <div class="value" id="lastUpdated">Never</div>
58843
- <div class="label">Last Updated</div>
58844
- </div>
58845
- <div class="status-item">
58846
- <div class="value" id="configStatus">\u274C</div>
58847
- <div class="label">API Configured</div>
58848
- </div>
58849
- </div>
58850
- </div>
58851
-
58852
- <!-- Clear Data -->
58853
- <div class="admin-card">
58854
- <h2>\u{1F5D1}\uFE0F Data Management</h2>
58855
- <p style="color: var(--text-muted); margin-bottom: 1.5rem;">
58856
- Clear all embeddings to start fresh or to re-embed with new content.
58857
- </p>
58858
- <button class="btn btn-secondary" id="clearBtn">Clear All Embeddings</button>
58859
- </div>
58860
- </div>
58861
-
58862
- <div class="toast" id="toast"></div>
58863
-
58864
- <script>
58865
- // Toast notification
58866
- function showToast(message, duration = 3000) {
58867
- const toast = document.getElementById('toast');
58868
- toast.textContent = message;
58869
- toast.classList.add('show');
58870
- setTimeout(() => toast.classList.remove('show'), duration);
58871
- }
58872
-
58873
- // Load configuration on page load
58874
- async function loadConfig() {
58875
- try {
58876
- const response = await fetch('/api/config');
58877
- const data = await response.json();
58878
-
58879
- if (data.config) {
58880
- document.getElementById('apiKey').value = data.config.apiKey || '';
58881
- document.getElementById('providerUrl').value = data.config.providerUrl || 'https://api.openai.com/v1/chat/completions';
58882
- document.getElementById('embeddingsUrl').value = data.config.embeddingsUrl || 'https://api.openai.com/v1/embeddings';
58883
- document.getElementById('model').value = data.config.model || 'gpt-3.5-turbo';
58884
- document.getElementById('embeddingsModel').value = data.config.embeddingsModel || 'text-embedding-3-small';
58885
- }
58886
-
58887
- document.getElementById('embeddingCount').textContent = data.embeddingCount || 0;
58888
- document.getElementById('lastUpdated').textContent = data.lastUpdated || 'Never';
58889
- document.getElementById('configStatus').textContent = data.config?.apiKey ? '\u2705' : '\u274C';
58890
- } catch (error) {
58891
- console.error('Failed to load config:', error);
58892
- }
58893
- }
58894
-
58895
- // Save configuration
58896
- document.getElementById('configForm').addEventListener('submit', async (e) => {
58897
- e.preventDefault();
58898
-
58899
- const config = {
58900
- apiKey: document.getElementById('apiKey').value,
58901
- providerUrl: document.getElementById('providerUrl').value,
58902
- embeddingsUrl: document.getElementById('embeddingsUrl').value,
58903
- model: document.getElementById('model').value,
58904
- embeddingsModel: document.getElementById('embeddingsModel').value
58905
- };
58906
-
58907
- try {
58908
- const response = await fetch('/api/config', {
58909
- method: 'POST',
58910
- headers: { 'Content-Type': 'application/json' },
58911
- body: JSON.stringify(config)
58912
- });
58913
-
58914
- if (response.ok) {
58915
- showToast('\u2705 Configuration saved successfully!');
58916
- document.getElementById('configStatus').textContent = config.apiKey ? '\u2705' : '\u274C';
58917
- } else {
58918
- showToast('\u274C Failed to save configuration');
58919
- }
58920
- } catch (error) {
58921
- showToast('\u274C Error saving configuration');
58922
- }
58923
- });
58924
-
58925
- // Embed knowledge
58926
- document.getElementById('embedForm').addEventListener('submit', async (e) => {
58927
- e.preventDefault();
58928
-
58929
- const content = document.getElementById('faqContent').value.trim();
58930
- if (!content) {
58931
- showToast('\u26A0\uFE0F Please enter some content to embed');
58932
- return;
58933
- }
58934
-
58935
- const embedBtn = document.getElementById('embedBtn');
58936
- embedBtn.disabled = true;
58937
- embedBtn.textContent = '\u23F3 Embedding...';
58938
-
58939
- try {
58940
- const response = await fetch('/api/embed', {
58941
- method: 'POST',
58942
- headers: { 'Content-Type': 'application/json' },
58943
- body: JSON.stringify({ content })
58944
- });
58945
-
58946
- const data = await response.json();
58947
-
58948
- if (response.ok) {
58949
- showToast('\u2705 ' + (data.message || 'Content embedded successfully!'));
58950
- document.getElementById('embeddingCount').textContent = data.count || 0;
58951
- document.getElementById('lastUpdated').textContent = new Date().toLocaleString();
58952
- document.getElementById('faqContent').value = '';
58953
- } else {
58954
- showToast('\u274C ' + (data.error || 'Failed to embed content'));
58955
- }
58956
- } catch (error) {
58957
- showToast('\u274C Error embedding content');
58958
- } finally {
58959
- embedBtn.disabled = false;
58960
- embedBtn.textContent = '\u{1F52E} Embed Knowledge';
58961
- }
58962
- });
58963
-
58964
- // Clear embeddings
58965
- document.getElementById('clearBtn').addEventListener('click', async () => {
58966
- if (!confirm('Are you sure you want to clear all embeddings?')) return;
58967
-
58968
- try {
58969
- const response = await fetch('/api/embed', {
58970
- method: 'DELETE'
58971
- });
58972
-
58973
- if (response.ok) {
58974
- showToast('\u2705 All embeddings cleared');
58975
- document.getElementById('embeddingCount').textContent = '0';
58976
- } else {
58977
- showToast('\u274C Failed to clear embeddings');
58978
- }
58979
- } catch (error) {
58980
- showToast('\u274C Error clearing embeddings');
58981
- }
58982
- });
58983
-
58984
- // Load config on page load
58985
- loadConfig();
58986
- </script>
58987
- </body>
58988
- </html>
58989
- `;
58990
-
58991
- // ../../packages/template/demo/aichat/files/stylesTs.ts
58992
- var stylesCSS = `/* === CSS Variables === */
58993
- :root {
58994
- --primary: #22c55e;
58995
- --primary-dark: #16a34a;
58996
- --secondary: #f97316;
58997
- --bg-dark: #0f172a;
58998
- --bg-card: rgba(255, 255, 255, 0.05);
58999
- --text-light: #f8fafc;
59000
- --text-muted: #94a3b8;
59001
- --border-color: rgba(255, 255, 255, 0.1);
59002
- --shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
59003
- --glass: rgba(255, 255, 255, 0.1);
59004
- }
59005
-
59006
- * {
59007
- margin: 0;
59008
- padding: 0;
59009
- box-sizing: border-box;
59010
- }
59011
-
59012
- body {
59013
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
59014
- background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%);
59015
- color: var(--text-light);
59016
- min-height: 100vh;
59017
- line-height: 1.6;
59018
- }
59019
-
59020
- /* === Header === */
59021
- .header {
59022
- background: var(--glass);
59023
- backdrop-filter: blur(10px);
59024
- border-bottom: 1px solid var(--border-color);
59025
- padding: 1rem 2rem;
59026
- position: fixed;
59027
- top: 0;
59028
- left: 0;
59029
- right: 0;
59030
- z-index: 100;
59031
- }
59032
-
59033
- .header-content {
59034
- max-width: 1200px;
59035
- margin: 0 auto;
59036
- display: flex;
59037
- justify-content: space-between;
59038
- align-items: center;
59039
- }
59040
-
59041
- .logo {
59042
- font-size: 1.5rem;
59043
- font-weight: 700;
59044
- background: linear-gradient(135deg, var(--primary), var(--secondary));
59045
- -webkit-background-clip: text;
59046
- -webkit-text-fill-color: transparent;
59047
- background-clip: text;
59048
- }
59049
-
59050
- .nav-links {
59051
- display: flex;
59052
- gap: 2rem;
59053
- list-style: none;
59054
- }
59055
-
59056
- .nav-links a {
59057
- color: var(--text-muted);
59058
- text-decoration: none;
59059
- transition: color 0.3s;
59060
- }
59061
-
59062
- .nav-links a:hover {
59063
- color: var(--primary);
59064
- }
59065
-
59066
- /* === Hero Section === */
59067
- .hero {
59068
- padding: 8rem 2rem 4rem;
59069
- text-align: center;
59070
- max-width: 900px;
59071
- margin: 0 auto;
59072
- }
59073
-
59074
- .hero h1 {
59075
- font-size: 3.5rem;
59076
- font-weight: 800;
59077
- margin-bottom: 1rem;
59078
- background: linear-gradient(135deg, #fff, var(--primary));
59079
- -webkit-background-clip: text;
59080
- -webkit-text-fill-color: transparent;
59081
- background-clip: text;
59082
- }
59083
-
59084
- .hero p {
59085
- font-size: 1.25rem;
59086
- color: var(--text-muted);
59087
- margin-bottom: 2rem;
59088
- }
59089
-
59090
- .cta-button {
59091
- background: linear-gradient(135deg, var(--primary), var(--primary-dark));
59092
- color: white;
59093
- padding: 1rem 2.5rem;
59094
- border: none;
59095
- border-radius: 50px;
59096
- font-size: 1.1rem;
59097
- font-weight: 600;
59098
- cursor: pointer;
59099
- transition: transform 0.3s, box-shadow 0.3s;
59100
- }
59101
-
59102
- .cta-button:hover {
59103
- transform: translateY(-2px);
59104
- box-shadow: 0 10px 30px rgba(34, 197, 94, 0.3);
59105
- }
59106
-
59107
- /* === Products Grid === */
59108
- .products {
59109
- max-width: 1200px;
59110
- margin: 0 auto;
59111
- padding: 4rem 2rem;
59112
- }
59113
-
59114
- .products h2 {
59115
- text-align: center;
59116
- font-size: 2.5rem;
59117
- margin-bottom: 3rem;
59118
- }
59119
-
59120
- .products-grid {
59121
- display: grid;
59122
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
59123
- gap: 2rem;
59124
- }
59125
-
59126
- .product-card {
59127
- background: var(--bg-card);
59128
- border: 1px solid var(--border-color);
59129
- border-radius: 20px;
59130
- overflow: hidden;
59131
- transition: transform 0.3s, box-shadow 0.3s;
59132
- }
59133
-
59134
- .product-card:hover {
59135
- transform: translateY(-5px);
59136
- box-shadow: var(--shadow);
59137
- }
59138
-
59139
- .product-image {
59140
- width: 100%;
59141
- height: 200px;
59142
- object-fit: cover;
59143
- background: linear-gradient(135deg, var(--primary), var(--secondary));
59144
- display: flex;
59145
- align-items: center;
59146
- justify-content: center;
59147
- font-size: 4rem;
59148
- }
59149
-
59150
- .product-info {
59151
- padding: 1.5rem;
59152
- }
59153
-
59154
- .product-info h3 {
59155
- font-size: 1.25rem;
59156
- margin-bottom: 0.5rem;
59157
- }
59158
-
59159
- .product-info p {
59160
- color: var(--text-muted);
59161
- font-size: 0.9rem;
59162
- margin-bottom: 1rem;
59163
- }
59164
-
59165
- .product-price {
59166
- font-size: 1.5rem;
59167
- font-weight: 700;
59168
- color: var(--primary);
59169
- }
59170
-
59171
- /* === Chatbox Widget === */
59172
- .chat-widget {
59173
- position: fixed;
59174
- bottom: 20px;
59175
- right: 20px;
59176
- z-index: 1000;
59177
- }
59178
-
59179
- .chat-toggle {
59180
- width: 60px;
59181
- height: 60px;
59182
- border-radius: 50%;
59183
- background: linear-gradient(135deg, var(--primary), var(--secondary));
59184
- border: none;
59185
- cursor: pointer;
59186
- display: flex;
59187
- align-items: center;
59188
- justify-content: center;
59189
- box-shadow: 0 5px 30px rgba(34, 197, 94, 0.4);
59190
- transition: transform 0.3s;
59191
- }
59192
-
59193
- .chat-toggle:hover {
59194
- transform: scale(1.1);
59195
- }
59196
-
59197
- .chat-toggle svg {
59198
- width: 28px;
59199
- height: 28px;
59200
- fill: white;
59201
- }
59202
-
59203
- .chat-window {
59204
- position: absolute;
59205
- bottom: 80px;
59206
- right: 0;
59207
- width: 380px;
59208
- height: 500px;
59209
- background: var(--bg-dark);
59210
- border: 1px solid var(--border-color);
59211
- border-radius: 20px;
59212
- box-shadow: var(--shadow);
59213
- display: none;
59214
- flex-direction: column;
59215
- overflow: hidden;
59216
- animation: slideUp 0.3s ease;
59217
- }
59218
-
59219
- .chat-window.open {
59220
- display: flex;
59221
- }
59222
-
59223
- @keyframes slideUp {
59224
- from {
59225
- opacity: 0;
59226
- transform: translateY(20px);
59227
- }
59228
- to {
59229
- opacity: 1;
59230
- transform: translateY(0);
59231
- }
59232
- }
59233
-
59234
- .chat-header {
59235
- background: linear-gradient(135deg, var(--primary), var(--primary-dark));
59236
- padding: 1rem 1.5rem;
59237
- display: flex;
59238
- align-items: center;
59239
- gap: 0.75rem;
59240
- }
59241
-
59242
- .chat-header-avatar {
59243
- width: 40px;
59244
- height: 40px;
59245
- border-radius: 50%;
59246
- background: rgba(255, 255, 255, 0.2);
59247
- display: flex;
59248
- align-items: center;
59249
- justify-content: center;
59250
- font-size: 1.25rem;
59251
- }
59252
-
59253
- .chat-header-info h4 {
59254
- font-size: 1rem;
59255
- font-weight: 600;
59256
- }
59257
-
59258
- .chat-header-info span {
59259
- font-size: 0.75rem;
59260
- opacity: 0.8;
59261
- }
59262
-
59263
- .chat-messages {
59264
- flex: 1;
59265
- overflow-y: auto;
59266
- padding: 1rem;
59267
- display: flex;
59268
- flex-direction: column;
59269
- gap: 0.75rem;
59270
- }
59271
-
59272
- .message {
59273
- max-width: 80%;
59274
- padding: 0.75rem 1rem;
59275
- border-radius: 18px;
59276
- font-size: 0.9rem;
59277
- line-height: 1.4;
59278
- }
59279
-
59280
- .message.bot {
59281
- background: var(--glass);
59282
- border: 1px solid var(--border-color);
59283
- align-self: flex-start;
59284
- border-bottom-left-radius: 4px;
59285
- }
59286
-
59287
- .message.user {
59288
- background: linear-gradient(135deg, var(--primary), var(--primary-dark));
59289
- align-self: flex-end;
59290
- border-bottom-right-radius: 4px;
59291
- }
59292
-
59293
- .typing-indicator {
59294
- display: flex;
59295
- gap: 4px;
59296
- padding: 0.75rem 1rem;
59297
- background: var(--glass);
59298
- border: 1px solid var(--border-color);
59299
- border-radius: 18px;
59300
- align-self: flex-start;
59301
- border-bottom-left-radius: 4px;
59302
- }
59303
-
59304
- .typing-indicator span {
59305
- width: 8px;
59306
- height: 8px;
59307
- background: var(--text-muted);
59308
- border-radius: 50%;
59309
- animation: bounce 1.4s infinite ease-in-out;
59310
- }
59311
-
59312
- .typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
59313
- .typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
59314
-
59315
- @keyframes bounce {
59316
- 0%, 80%, 100% { transform: scale(0); }
59317
- 40% { transform: scale(1); }
59318
- }
59319
-
59320
- .chat-input-container {
59321
- padding: 1rem;
59322
- border-top: 1px solid var(--border-color);
59323
- display: flex;
59324
- gap: 0.5rem;
59325
- }
59326
-
59327
- .chat-input {
59328
- flex: 1;
59329
- background: var(--glass);
59330
- border: 1px solid var(--border-color);
59331
- border-radius: 25px;
59332
- padding: 0.75rem 1rem;
59333
- color: var(--text-light);
59334
- font-size: 0.9rem;
59335
- outline: none;
59336
- }
59337
-
59338
- .chat-input::placeholder {
59339
- color: var(--text-muted);
59340
- }
59341
-
59342
- .chat-send {
59343
- width: 44px;
59344
- height: 44px;
59345
- border-radius: 50%;
59346
- background: linear-gradient(135deg, var(--primary), var(--primary-dark));
59347
- border: none;
59348
- cursor: pointer;
59349
- display: flex;
59350
- align-items: center;
59351
- justify-content: center;
59352
- transition: transform 0.2s;
59353
- }
59354
-
59355
- .chat-send:hover {
59356
- transform: scale(1.05);
59357
- }
59358
-
59359
- .chat-send svg {
59360
- width: 20px;
59361
- height: 20px;
59362
- fill: white;
59363
- }
59364
-
59365
- /* === Admin Panel Styles === */
59366
- .admin-container {
59367
- max-width: 900px;
59368
- margin: 0 auto;
59369
- padding: 6rem 2rem 4rem;
59370
- }
59371
-
59372
- .admin-header {
59373
- text-align: center;
59374
- margin-bottom: 3rem;
59375
- }
59376
-
59377
- .admin-header h1 {
59378
- font-size: 2.5rem;
59379
- margin-bottom: 0.5rem;
59380
- }
59381
-
59382
- .admin-header p {
59383
- color: var(--text-muted);
59384
- }
59385
-
59386
- .admin-card {
59387
- background: var(--bg-card);
59388
- border: 1px solid var(--border-color);
59389
- border-radius: 20px;
59390
- padding: 2rem;
59391
- margin-bottom: 2rem;
59392
- }
59393
-
59394
- .admin-card h2 {
59395
- font-size: 1.25rem;
59396
- margin-bottom: 1.5rem;
59397
- display: flex;
59398
- align-items: center;
59399
- gap: 0.5rem;
59400
- }
59401
-
59402
- .form-group {
59403
- margin-bottom: 1.5rem;
59404
- }
59405
-
59406
- .form-group label {
59407
- display: block;
59408
- font-size: 0.9rem;
59409
- color: var(--text-muted);
59410
- margin-bottom: 0.5rem;
59411
- }
59412
-
59413
- .form-input {
59414
- width: 100%;
59415
- background: var(--glass);
59416
- border: 1px solid var(--border-color);
59417
- border-radius: 10px;
59418
- padding: 0.875rem 1rem;
59419
- color: var(--text-light);
59420
- font-size: 0.95rem;
59421
- outline: none;
59422
- transition: border-color 0.3s;
59423
- }
59424
-
59425
- .form-input:focus {
59426
- border-color: var(--primary);
59427
- }
59428
-
59429
- .form-input::placeholder {
59430
- color: var(--text-muted);
59431
- }
59432
-
59433
- textarea.form-input {
59434
- min-height: 200px;
59435
- resize: vertical;
59436
- font-family: inherit;
59437
- }
59438
-
59439
- .btn {
59440
- background: linear-gradient(135deg, var(--primary), var(--primary-dark));
59441
- color: white;
59442
- padding: 0.875rem 2rem;
59443
- border: none;
59444
- border-radius: 10px;
59445
- font-size: 1rem;
59446
- font-weight: 600;
59447
- cursor: pointer;
59448
- transition: transform 0.2s, box-shadow 0.2s;
59449
- display: inline-flex;
59450
- align-items: center;
59451
- gap: 0.5rem;
59452
- }
59453
-
59454
- .btn:hover {
59455
- transform: translateY(-2px);
59456
- box-shadow: 0 10px 30px rgba(34, 197, 94, 0.3);
59457
- }
59458
-
59459
- .btn:disabled {
59460
- opacity: 0.6;
59461
- cursor: not-allowed;
59462
- transform: none;
59463
- }
59464
-
59465
- .btn-secondary {
59466
- background: var(--glass);
59467
- border: 1px solid var(--border-color);
59468
- }
59469
-
59470
- .status-bar {
59471
- background: var(--glass);
59472
- border: 1px solid var(--border-color);
59473
- border-radius: 10px;
59474
- padding: 1rem 1.5rem;
59475
- display: flex;
59476
- justify-content: space-between;
59477
- align-items: center;
59478
- margin-top: 1.5rem;
59479
- }
59480
-
59481
- .status-item {
59482
- text-align: center;
59483
- }
59484
-
59485
- .status-item .value {
59486
- font-size: 1.5rem;
59487
- font-weight: 700;
59488
- color: var(--primary);
59489
- }
59490
-
59491
- .status-item .label {
59492
- font-size: 0.8rem;
59493
- color: var(--text-muted);
59494
- }
59495
-
59496
- .toast {
59497
- position: fixed;
59498
- bottom: 20px;
59499
- left: 50%;
59500
- transform: translateX(-50%);
59501
- background: var(--primary);
59502
- color: white;
59503
- padding: 1rem 2rem;
59504
- border-radius: 10px;
59505
- box-shadow: var(--shadow);
59506
- opacity: 0;
59507
- transition: opacity 0.3s;
59508
- z-index: 2000;
59509
- }
59510
-
59511
- .toast.show {
59512
- opacity: 1;
59513
- }
59514
-
59515
- /* === Responsive === */
59516
- @media (max-width: 768px) {
59517
- .hero h1 {
59518
- font-size: 2.5rem;
59519
- }
59520
-
59521
- .chat-window {
59522
- width: calc(100vw - 40px);
59523
- height: 60vh;
59524
- }
59525
-
59526
- .nav-links {
59527
- display: none;
59528
- }
59529
- }
59530
- `;
59531
-
59532
- // ../../packages/template/demo/aichat/files/serverTs.ts
59533
- var serverTs = `import express, { Request, Response } from 'express';
59534
- import path from 'path';
59535
- import chatRouter from './routes/chat';
59536
- import embedRouter from './routes/embed';
59537
- import configRouter from './routes/config';
59538
- import { loadEmbeddings } from './vectorStore';
59539
- import { loadConfig } from './aiClient';
59540
-
59541
- const app = express();
59542
- const port = 3500;
59543
-
59544
- // Middleware
59545
- app.use(express.static(path.join(__dirname, '../public')));
59546
- app.use(express.json());
59547
-
59548
- // Load persisted data
59549
- loadEmbeddings();
59550
- loadConfig();
59551
-
59552
- // API Routes
59553
- app.use('/api/chat', chatRouter);
59554
- app.use('/api/embed', embedRouter);
59555
- app.use('/api/config', configRouter);
59556
-
59557
- // Serve main page
59558
- app.get('/', (req: Request, res: Response) => {
59559
- res.sendFile(path.join(__dirname, '../public/index.html'));
59560
- });
58616
+ // ../../packages/template/demo/monochat.ts
58617
+ var MonoChat = {
58618
+ name: "Chat To MonoChat",
58619
+ description: "React Frontend, needs custom backend",
58620
+ notes: "Vite React + TailwindCSS + TypeScript",
58621
+ templating: [
58622
+ {
58623
+ action: "command",
58624
+ cmd: "rm -rf ./* ./.[!.]*",
58625
+ args: []
58626
+ },
58627
+ {
58628
+ action: "file",
58629
+ file: ".gitignore",
58630
+ filecontent: "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
58631
+ },
58632
+ {
58633
+ action: "file",
58634
+ file: "README.md",
58635
+ filecontent: "# React + TypeScript + Vite\n\nThis template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.\n\nCurrently, two official plugins are available:\n\n- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh\n- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh\n\n## React Compiler\n\nThe React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).\n\n## Expanding the ESLint configuration\n\nIf you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:\n\n```js\nexport default defineConfig([\n globalIgnores(['dist']),\n {\n files: ['**/*.{ts,tsx}'],\n extends: [\n // Other configs...\n\n // Remove tseslint.configs.recommended and replace with this\n tseslint.configs.recommendedTypeChecked,\n // Alternatively, use this for stricter rules\n tseslint.configs.strictTypeChecked,\n // Optionally, add this for stylistic rules\n tseslint.configs.stylisticTypeChecked,\n\n // Other configs...\n ],\n languageOptions: {\n parserOptions: {\n project: ['./tsconfig.node.json', './tsconfig.app.json'],\n tsconfigRootDir: import.meta.dirname,\n },\n // other options...\n },\n },\n])\n```\n\nYou can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:\n\n```js\n// eslint.config.js\nimport reactX from 'eslint-plugin-react-x'\nimport reactDom from 'eslint-plugin-react-dom'\n\nexport default defineConfig([\n globalIgnores(['dist']),\n {\n files: ['**/*.{ts,tsx}'],\n extends: [\n // Other configs...\n // Enable lint rules for React\n reactX.configs['recommended-typescript'],\n // Enable lint rules for React DOM\n reactDom.configs.recommended,\n ],\n languageOptions: {\n parserOptions: {\n project: ['./tsconfig.node.json', './tsconfig.app.json'],\n tsconfigRootDir: import.meta.dirname,\n },\n // other options...\n },\n },\n])\n```\n"
58636
+ },
58637
+ {
58638
+ action: "file",
58639
+ file: "eslint.config.js",
58640
+ filecontent: "import js from '@eslint/js'\nimport globals from 'globals'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport reactRefresh from 'eslint-plugin-react-refresh'\nimport tseslint from 'typescript-eslint'\nimport { defineConfig, globalIgnores } from 'eslint/config'\n\nexport default defineConfig([\n globalIgnores(['dist']),\n {\n files: ['**/*.{ts,tsx}'],\n extends: [\n js.configs.recommended,\n tseslint.configs.recommended,\n reactHooks.configs.flat.recommended,\n reactRefresh.configs.vite,\n ],\n languageOptions: {\n ecmaVersion: 2020,\n globals: globals.browser,\n },\n },\n])\n"
58641
+ },
58642
+ {
58643
+ action: "file",
58644
+ file: "index.html",
58645
+ filecontent: '<!doctype html>\n<html lang="en">\n <head>\n <meta charset="UTF-8" />\n <link rel="icon" type="image/svg+xml" href="/logo.svg" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <title>MonoChat</title>\n </head>\n <body>\n <div id="root"></div>\n <script type="module" src="/src/main.tsx"></script>\n </body>\n</html>\n'
58646
+ },
58647
+ {
58648
+ action: "file",
58649
+ file: "netlify.toml",
58650
+ filecontent: '[build]\n command = "npm run build"\n publish = "dist"\n\n[[redirects]]\n from = "/*"\n to = "/index.html"\n status = 200'
58651
+ },
58652
+ {
58653
+ action: "file",
58654
+ file: "package.json",
58655
+ filecontent: '{\n "name": "z-chat",\n "private": true,\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "tsc -b && vite build",\n "lint": "eslint .",\n "preview": "vite preview",\n "stop": ""\n },\n "dependencies": {\n "react": "^19.2.0",\n "react-dom": "^19.2.0",\n "zustand": "^4.5.7"\n },\n "devDependencies": {\n "@eslint/js": "^9.39.1",\n "@tailwindcss/postcss": "^4.0.0",\n "@types/node": "^24.10.1",\n "@types/react": "^19.2.5",\n "@types/react-dom": "^19.2.3",\n "@vitejs/plugin-react": "^5.1.1",\n "autoprefixer": "^10.4.20",\n "eslint": "^9.39.1",\n "eslint-plugin-react-hooks": "^7.0.1",\n "eslint-plugin-react-refresh": "^0.4.24",\n "globals": "^16.5.0",\n "jiti": "^2.4.2",\n "postcss": "^8.5.1",\n "tailwindcss": "^4.0.0",\n "typescript": "~5.9.3",\n "typescript-eslint": "^8.46.4",\n "vite": "^7.2.4"\n },\n "description": "Vite React TS",\n "fontawesomeIcon": "fab fa-react text-blue-500"\n}\n'
58656
+ },
58657
+ {
58658
+ action: "file",
58659
+ file: "postcss.config.js",
58660
+ filecontent: 'export default {\n plugins: {\n "@tailwindcss/postcss": {},\n autoprefixer: {},\n },\n}'
58661
+ },
58662
+ {
58663
+ action: "file",
58664
+ file: "public/logo.svg",
58665
+ filecontent: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">\n <defs>\n <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">\n <stop offset="0%" style="stop-color:#6366f1;stop-opacity:1" />\n <stop offset="100%" style="stop-color:#a855f7;stop-opacity:1" />\n </linearGradient>\n </defs>\n <rect width="512" height="512" rx="128" fill="url(#grad)" />\n <path d="M375.3 325.7c22.1-24.9 36.7-56.3 36.7-91.7c0-79.5-72.7-144-162-144S88 154.5 88 234s72.7 144 162 144c20.3 0 39.7-3.4 57.6-9.6L368 400l-45.7-31.3c18.5-12.7 34.6-28 47-45.7v2.7z" fill="white" />\n <circle cx="178" cy="234" r="20" fill="#6366f1"/>\n <circle cx="250" cy="234" r="20" fill="#6366f1"/>\n <circle cx="322" cy="234" r="20" fill="#6366f1"/>\n</svg>\n'
58666
+ },
58667
+ {
58668
+ action: "file",
58669
+ file: "render.yaml",
58670
+ filecontent: "services:\n - type: web\n name: vite-react-app\n env: static\n buildCommand: npm install && npm run build\n staticPublishPath: ./dist"
58671
+ },
58672
+ {
58673
+ action: "file",
58674
+ file: "src/App.tsx",
58675
+ filecontent: "import Background from './components/Background';\nimport ChatContainer from './components/ChatContainer';\nimport Header from './components/Header';\n\nexport default function App() {\n return (\n <div className={`\n h-[100vh]\n w-[100vw]\n overflow-hidden\n bg-zinc-900 \n `}>\n <div className='w-full h-full'>\n <Background />\n <Header />\n <ChatContainer />\n </div>\n </div>\n );\n}"
58676
+ },
58677
+ {
58678
+ action: "file",
58679
+ file: "src/_FetchToWho.ts",
58680
+ filecontent: 'import type { ChatItem } from "./app/chat";\n\n//@ts-ignore\nexport default async function FetchToWho( chats: ChatItem[]) {\n // const lastChat = chats[chats.length - 1];\n // if (!lastChat) return;\n // const response = await fetch("https://api.openai.com/v1/chat/completions", {\n // method: "POST",\n // headers: {\n // "Content-Type": "application/json",\n // "Authorization": `Bearer ${process.env.OPENAI_API_KEY}`\n // },\n // body: JSON.stringify({\n // model: "gpt-3.5-turbo",\n // messages: [\n // { role: "system", content: "You are a helpful assistant." },\n // { role: "user", content: lastChat.message }\n // ]\n // })\n // });\n // const data = await response.json();\n // return data.choices[0].message.content;\n\n const lorem = "Please edit the FetchToWho function. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quod. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quod. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quod. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quod. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quod.";\n return lorem;\n}\n'
58681
+ },
58682
+ {
58683
+ action: "file",
58684
+ file: "src/app/chat.ts",
58685
+ filecontent: `import { create } from 'zustand';
58686
+ import { createSelectors } from './zustandSelector';
59561
58687
 
59562
- // Start server
59563
- app.listen(port, () => {
59564
- console.log('');
59565
- console.log('\u{1F34A} ====================================');
59566
- console.log(' FreshFruit AI Chat Demo');
59567
- console.log('====================================');
59568
- console.log('');
59569
- console.log('\u{1F4CD} Store: http://localhost:' + port);
59570
- console.log('\u{1F527} Admin Panel: http://localhost:' + port + '/admin.html');
59571
- console.log('');
59572
- console.log('\u{1F4DD} Quick Start:');
59573
- console.log(' 1. Open the Admin Panel');
59574
- console.log(' 2. Configure your OpenAI API key');
59575
- console.log(' 3. Add FAQ content and click "Embed"');
59576
- console.log(' 4. Open the Store and chat with the AI!');
59577
- console.log('');
59578
- });
59579
- `;
59580
- var tsconfigJson = `{
59581
- "compilerOptions": {
59582
- "target": "es2016",
59583
- "module": "commonjs",
59584
- "outDir": "./dist",
59585
- "esModuleInterop": true,
59586
- "forceConsistentCasingInFileNames": true,
59587
- "strict": true,
59588
- "skipLibCheck": true
59589
- }
59590
- }`;
59591
- var tsupConfig = `import { defineConfig } from 'tsup';
59592
-
59593
- export default defineConfig({
59594
- entry: ['src/index.ts'],
59595
- splitting: false,
59596
- sourcemap: true,
59597
- clean: true,
59598
- format: ['cjs'],
59599
- });`;
59600
-
59601
- // ../../packages/template/demo/aichat/files/vectorStoreTs.ts
59602
- var vectorStoreTs = `import fs from 'fs';
59603
- import path from 'path';
59604
-
59605
- const EMBEDDINGS_FILE = path.join(__dirname, '../embeddings.json');
59606
-
59607
- interface EmbeddingEntry {
59608
- id: string;
59609
- text: string;
59610
- embedding: number[];
58688
+ export interface ChatItem {
58689
+ id: number;
58690
+ who: "user" | "system";
59611
58691
  timestamp: number;
58692
+ message: string;
59612
58693
  }
59613
58694
 
59614
- // In-memory vector database
59615
- let vectorStore: EmbeddingEntry[] = [];
59616
-
59617
- // Load embeddings from file on startup
59618
- export function loadEmbeddings(): void {
59619
- try {
59620
- if (fs.existsSync(EMBEDDINGS_FILE)) {
59621
- const data = fs.readFileSync(EMBEDDINGS_FILE, 'utf-8');
59622
- vectorStore = JSON.parse(data);
59623
- console.log('\u{1F4DA} Loaded ' + vectorStore.length + ' embeddings from file');
59624
- }
59625
- } catch (error) {
59626
- console.error('Failed to load embeddings:', error);
59627
- vectorStore = [];
59628
- }
59629
- }
59630
-
59631
- // Save embeddings to file
59632
- export function saveEmbeddings(): void {
59633
- try {
59634
- fs.writeFileSync(EMBEDDINGS_FILE, JSON.stringify(vectorStore, null, 2));
59635
- } catch (error) {
59636
- console.error('Failed to save embeddings:', error);
59637
- }
59638
- }
59639
-
59640
- // Add embedding to store
59641
- export function addEmbedding(text: string, embedding: number[]): void {
59642
- const entry: EmbeddingEntry = {
59643
- id: Date.now().toString(36) + Math.random().toString(36).substr(2),
59644
- text,
59645
- embedding,
59646
- timestamp: Date.now()
59647
- };
59648
- vectorStore.push(entry);
59649
- saveEmbeddings();
59650
- }
59651
-
59652
- // Clear all embeddings
59653
- export function clearEmbeddings(): void {
59654
- vectorStore = [];
59655
- saveEmbeddings();
59656
- }
59657
-
59658
- // Get embedding count
59659
- export function getEmbeddingCount(): number {
59660
- return vectorStore.length;
59661
- }
59662
-
59663
- // Cosine similarity between two vectors
59664
- function cosineSimilarity(a: number[], b: number[]): number {
59665
- if (a.length !== b.length) return 0;
59666
-
59667
- let dotProduct = 0;
59668
- let normA = 0;
59669
- let normB = 0;
59670
-
59671
- for (let i = 0; i < a.length; i++) {
59672
- dotProduct += a[i] * b[i];
59673
- normA += a[i] * a[i];
59674
- normB += b[i] * b[i];
59675
- }
59676
-
59677
- if (normA === 0 || normB === 0) return 0;
59678
- return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
58695
+ interface chatContext {
58696
+ chats: Array<ChatItem>;
58697
+ textinput: string;
58698
+ addChat: (chat: ChatItem) => void;
58699
+ setTextinput: (textinput: string) => void;
59679
58700
  }
59680
58701
 
59681
- // Search for similar embeddings
59682
- export function searchSimilar(queryEmbedding: number[], topK: number = 3): { text: string; score: number }[] {
59683
- const results = vectorStore
59684
- .map(entry => ({
59685
- text: entry.text,
59686
- score: cosineSimilarity(queryEmbedding, entry.embedding)
59687
- }))
59688
- .sort((a, b) => b.score - a.score)
59689
- .slice(0, topK);
59690
-
59691
- return results;
59692
- }
59693
-
59694
- // Get last update time
59695
- export function getLastUpdated(): string | null {
59696
- if (vectorStore.length === 0) return null;
59697
- const latest = Math.max(...vectorStore.map(e => e.timestamp));
59698
- return new Date(latest).toLocaleString();
59699
- }
59700
- `;
59701
-
59702
- // ../../packages/template/demo/aichat/files/aiClientTs.ts
59703
- var aiClientTs = `import fs from 'fs';
59704
- import path from 'path';
59705
-
59706
- const CONFIG_FILE = path.join(__dirname, '../config.json');
59707
-
59708
- export interface AIConfig {
59709
- apiKey: string;
59710
- providerUrl: string;
59711
- embeddingsUrl: string;
59712
- model: string;
59713
- embeddingsModel: string;
59714
- }
59715
-
59716
- // Default configuration
59717
- const defaultConfig: AIConfig = {
59718
- apiKey: '',
59719
- providerUrl: 'https://api.openai.com/v1/chat/completions',
59720
- embeddingsUrl: 'https://api.openai.com/v1/embeddings',
59721
- model: 'gpt-3.5-turbo',
59722
- embeddingsModel: 'text-embedding-3-small'
59723
- };
59724
-
59725
- let currentConfig: AIConfig = { ...defaultConfig };
59726
-
59727
- // Load configuration from file
59728
- export function loadConfig(): void {
59729
- try {
59730
- if (fs.existsSync(CONFIG_FILE)) {
59731
- const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
59732
- currentConfig = { ...defaultConfig, ...JSON.parse(data) };
59733
- console.log('\u{1F511} Loaded AI configuration');
59734
- }
59735
- } catch (error) {
59736
- console.error('Failed to load config:', error);
59737
- currentConfig = { ...defaultConfig };
59738
- }
59739
- }
59740
-
59741
- // Save configuration to file
59742
- export function saveConfig(config: AIConfig): void {
59743
- currentConfig = { ...config };
59744
- try {
59745
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
59746
- } catch (error) {
59747
- console.error('Failed to save config:', error);
59748
- }
59749
- }
59750
-
59751
- // Get current configuration
59752
- export function getConfig(): AIConfig {
59753
- return currentConfig;
59754
- }
59755
-
59756
- // Call embeddings API
59757
- export async function callEmbeddingsAPI(text: string): Promise<number[]> {
59758
- const config = getConfig();
59759
-
59760
- if (!config.apiKey) {
59761
- throw new Error('API key not configured');
59762
- }
59763
-
59764
- const response = await fetch(config.embeddingsUrl, {
59765
- method: 'POST',
59766
- headers: {
59767
- 'Content-Type': 'application/json',
59768
- 'Authorization': 'Bearer ' + config.apiKey
59769
- },
59770
- body: JSON.stringify({
59771
- model: config.embeddingsModel,
59772
- input: text
59773
- })
59774
- });
59775
-
59776
- if (!response.ok) {
59777
- const error = await response.text();
59778
- throw new Error('Embeddings API error: ' + error);
59779
- }
59780
-
59781
- const data = await response.json();
59782
- return data.data[0].embedding;
59783
- }
59784
-
59785
- // Call chat completions API
59786
- export async function callChatAPI(systemPrompt: string, userMessage: string): Promise<string> {
59787
- const config = getConfig();
59788
-
59789
- if (!config.apiKey) {
59790
- throw new Error('API key not configured');
59791
- }
59792
-
59793
- const response = await fetch(config.providerUrl, {
59794
- method: 'POST',
59795
- headers: {
59796
- 'Content-Type': 'application/json',
59797
- 'Authorization': 'Bearer ' + config.apiKey
59798
- },
59799
- body: JSON.stringify({
59800
- model: config.model,
59801
- messages: [
59802
- { role: 'system', content: systemPrompt },
59803
- { role: 'user', content: userMessage }
59804
- ],
59805
- max_tokens: 500,
59806
- temperature: 0.7
59807
- })
59808
- });
59809
-
59810
- if (!response.ok) {
59811
- const error = await response.text();
59812
- throw new Error('Chat API error: ' + error);
59813
- }
59814
-
59815
- const data = await response.json();
59816
- return data.choices[0].message.content;
59817
- }
59818
- `;
59819
-
59820
- // ../../packages/template/demo/aichat/files/routesTs.ts
59821
- var chatRouteTs = `import { Router, Request, Response } from 'express';
59822
- import { searchSimilar } from '../vectorStore';
59823
- import { getConfig, callEmbeddingsAPI, callChatAPI } from '../aiClient';
59824
-
59825
- const router = Router();
59826
-
59827
- router.post('/', async (req: Request, res: Response) => {
59828
- try {
59829
- const { message } = req.body;
59830
-
59831
- if (!message) {
59832
- return res.status(400).json({ error: 'Message is required' });
59833
- }
59834
-
59835
- const config = getConfig();
59836
- if (!config.apiKey) {
59837
- return res.json({
59838
- reply: "I'm not configured yet. Please ask the admin to set up the AI provider in the admin panel."
59839
- });
59840
- }
59841
-
59842
- // Get embedding for the user's message
59843
- const queryEmbedding = await callEmbeddingsAPI(message);
59844
-
59845
- // Search for similar content in our knowledge base
59846
- const similarDocs = searchSimilar(queryEmbedding, 3);
59847
-
59848
- // Build context from similar documents
59849
- let context = '';
59850
- if (similarDocs.length > 0 && similarDocs[0].score > 0.3) {
59851
- context = 'Relevant information from our knowledge base:\\n' +
59852
- similarDocs
59853
- .filter(doc => doc.score > 0.3)
59854
- .map(doc => doc.text)
59855
- .join('\\n\\n');
59856
- }
59857
-
59858
- // Create the chat prompt
59859
- const systemPrompt = "You are a helpful customer support assistant for FreshFruit, " +
59860
- "a premium organic fruit delivery service. Be friendly, helpful, and concise. " +
59861
- "If you have relevant information from the knowledge base, use it to answer. " +
59862
- "If you don't know something, say so politely and suggest contacting human support.\\n\\n" +
59863
- (context ? 'Knowledge Base Context:\\n' + context : 'No specific knowledge base context available for this query.');
59864
-
59865
- // Call the chat API
59866
- const reply = await callChatAPI(systemPrompt, message);
59867
-
59868
- res.json({ reply });
59869
- } catch (error) {
59870
- console.error('Chat error:', error);
59871
- res.status(500).json({ error: 'Failed to process message' });
59872
- }
59873
- });
59874
-
59875
- export default router;
59876
- `;
59877
- var embedRouteTs = `import { Router, Request, Response } from 'express';
59878
- import { addEmbedding, clearEmbeddings, getEmbeddingCount } from '../vectorStore';
59879
- import { getConfig, callEmbeddingsAPI } from '../aiClient';
59880
-
59881
- const router = Router();
59882
-
59883
- // Embed new content
59884
- router.post('/', async (req: Request, res: Response) => {
59885
- try {
59886
- const { content } = req.body;
59887
-
59888
- if (!content) {
59889
- return res.status(400).json({ error: 'Content is required' });
59890
- }
59891
-
59892
- const config = getConfig();
59893
- if (!config.apiKey) {
59894
- return res.status(400).json({ error: 'API key not configured. Please configure in admin panel.' });
59895
- }
59896
-
59897
- // Split content into chunks (by double newline or paragraph)
59898
- const chunks = content
59899
- .split(/\\n\\n+/)
59900
- .map((chunk: string) => chunk.trim())
59901
- .filter((chunk: string) => chunk.length > 10);
59902
-
59903
- if (chunks.length === 0) {
59904
- return res.status(400).json({ error: 'No valid content chunks found' });
59905
- }
59906
-
59907
- // Embed each chunk
59908
- let embedded = 0;
59909
- for (const chunk of chunks) {
59910
- try {
59911
- const embedding = await callEmbeddingsAPI(chunk);
59912
- addEmbedding(chunk, embedding);
59913
- embedded++;
59914
- } catch (error) {
59915
- console.error('Failed to embed chunk:', error);
59916
- }
59917
- }
59918
-
59919
- res.json({
59920
- message: 'Successfully embedded ' + embedded + ' chunks',
59921
- count: getEmbeddingCount()
59922
- });
59923
- } catch (error) {
59924
- console.error('Embed error:', error);
59925
- res.status(500).json({ error: 'Failed to embed content' });
59926
- }
59927
- });
59928
-
59929
- // Clear all embeddings
59930
- router.delete('/', (req: Request, res: Response) => {
59931
- clearEmbeddings();
59932
- res.json({ message: 'All embeddings cleared', count: 0 });
59933
- });
58702
+ const chatstate = create<chatContext>()((set) => ({
58703
+ chats: [],
58704
+ textinput: "",
58705
+ addChat: (chat: ChatItem) => set((state) => ({
58706
+ chats: [...state.chats, chat]
58707
+ })),
58708
+ setTextinput: (textinput: string) => set(() => ({
58709
+ textinput: textinput
58710
+ }))
59934
58711
 
59935
- export default router;
59936
- `;
59937
- var configRouteTs = `import { Router, Request, Response } from 'express';
59938
- import { getConfig, saveConfig, AIConfig } from '../aiClient';
59939
- import { getEmbeddingCount, getLastUpdated } from '../vectorStore';
59940
-
59941
- const router = Router();
59942
-
59943
- // Get current config
59944
- router.get('/', (req: Request, res: Response) => {
59945
- const config = getConfig();
59946
- // Mask the API key for security
59947
- const maskedConfig = {
59948
- ...config,
59949
- apiKey: config.apiKey ? '\u2022\u2022\u2022\u2022\u2022\u2022' + config.apiKey.slice(-4) : ''
59950
- };
59951
-
59952
- res.json({
59953
- config: maskedConfig,
59954
- embeddingCount: getEmbeddingCount(),
59955
- lastUpdated: getLastUpdated()
59956
- });
59957
- });
59958
-
59959
- // Update config
59960
- router.post('/', (req: Request, res: Response) => {
59961
- try {
59962
- const { apiKey, providerUrl, embeddingsUrl, model, embeddingsModel } = req.body;
59963
-
59964
- const newConfig: AIConfig = {
59965
- apiKey: apiKey || '',
59966
- providerUrl: providerUrl || 'https://api.openai.com/v1/chat/completions',
59967
- embeddingsUrl: embeddingsUrl || 'https://api.openai.com/v1/embeddings',
59968
- model: model || 'gpt-3.5-turbo',
59969
- embeddingsModel: embeddingsModel || 'text-embedding-3-small'
59970
- };
59971
-
59972
- saveConfig(newConfig);
59973
- res.json({ message: 'Configuration saved' });
59974
- } catch (error) {
59975
- console.error('Config save error:', error);
59976
- res.status(500).json({ error: 'Failed to save configuration' });
59977
- }
59978
- });
58712
+ }));
59979
58713
 
59980
- export default router;
59981
- `;
58714
+ const useChatState = createSelectors(chatstate);
58715
+ export default useChatState;
59982
58716
 
59983
- // ../../packages/template/demo/aichat/index.ts
59984
- var AIChat = {
59985
- name: "AI Chat",
59986
- description: "Fullstack AI Customer Support Chat - Chat with your FAQ/Knowledge Base",
59987
- notes: "Requires Node.js, NPM, and an OpenAI-compatible API key",
59988
- templating: [
59989
- // Install dependencies
59990
- {
59991
- action: "command",
59992
- cmd: "npm",
59993
- args: ["init", "-y"]
58717
+ `
59994
58718
  },
59995
58719
  {
59996
- action: "command",
59997
- cmd: "npm",
59998
- args: ["install", "express"]
58720
+ action: "file",
58721
+ file: "src/app/zustandSelector.ts",
58722
+ filecontent: "//from: https://docs.pmnd.rs/zustand/guides/auto-generating-selectors\nimport type { StoreApi, UseBoundStore } from 'zustand'\n\ntype WithSelectors<S> = S extends { getState: () => infer T }\n ? S & { use: { [K in keyof T]: () => T[K] } }\n : never\n\nexport const createSelectors = <S extends UseBoundStore<StoreApi<object>>>(\n _store: S\n) => {\n let store = _store as WithSelectors<typeof _store>\n store.use = {}\n for (let k of Object.keys(store.getState())) {\n ;(store.use as any)[k] = () => store((s) => s[k as keyof typeof s])\n }\n\n return store\n}\n"
59999
58723
  },
60000
58724
  {
60001
- action: "command",
60002
- cmd: "npm",
60003
- args: ["install", "-D", "nodemon", "typescript", "ts-node", "@types/node", "@types/express", "tsup"]
58725
+ action: "file",
58726
+ file: "src/components/Background.tsx",
58727
+ filecontent: 'export default function Background() {\n return (\n <>\n <div className="absolute -top-[25%] -left-[10%] w-[80%] h-[80%] bg-blue-800/10 rounded-full blur-[128px]"></div>\n <div className="absolute bottom-[0%] right-[0%] w-[80%] h-[40%] bg-purple-800/10 rounded-full blur-[128px]"></div>\n </>\n )\n}'
60004
58728
  },
60005
- // Public files
60006
58729
  {
60007
58730
  action: "file",
60008
- file: "public/index.html",
60009
- filecontent: indexHTML
58731
+ file: "src/components/ChatContainer.tsx",
58732
+ filecontent: `import useChatState from '../app/chat';
58733
+ import TextInput from './TextInput';
58734
+ import ChatContents from './ChatContents';
58735
+
58736
+ export default function ChatContainer() {
58737
+ const chats = useChatState.use.chats();
58738
+
58739
+ return (
58740
+ <div className="flex flex-col h-[calc(100vh-6rem)] w-full max-w-[900px] mx-auto relative font-sans">
58741
+ {chats.length > 0 ? (
58742
+ <>
58743
+ <ChatContents />
58744
+ <TextInput />
58745
+ </>
58746
+ ) : (
58747
+ <div className="flex flex-col items-center justify-center h-full w-full p-4">
58748
+ <div className="flex gap-4 items-center justify-center">
58749
+ <div className="w-10 h-10 mb-8">
58750
+ <img src="/logo.svg" alt="MonoChat Logo" className="w-full h-full object-contain" />
58751
+ </div>
58752
+ <h2 className="text-xl font-bold text-white mb-8">How can I help you today?</h2>
58753
+ </div>
58754
+ <TextInput />
58755
+ </div>
58756
+ )}
58757
+ </div>
58758
+ );
58759
+ }
58760
+
58761
+ `
60010
58762
  },
60011
58763
  {
60012
58764
  action: "file",
60013
- file: "public/admin.html",
60014
- filecontent: adminHTML
58765
+ file: "src/components/ChatContents.tsx",
58766
+ filecontent: "import { useEffect, useRef } from \"react\";\nimport useChatState from \"../app/chat\";\n\nexport default function ChatContents() {\n const chats = useChatState.use.chats();\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [chats]);\n\n return (\n <div className=\"flex-1 overflow-y-auto w-full px-4 scrollbar-hide\">\n {chats.map((chat) => (\n <div\n key={chat.id}\n className={`group flex w-full ${chat.who === 'user' ? 'justify-end' : 'justify-start'}`}\n >\n <div className={`flex max-w-[85%] md:max-w-[80%] gap-4 ${chat.who === 'user' ? 'flex-row-reverse' : 'flex-row'}`}>\n <div className={`flex flex-col ${chat.who === 'user' ? 'items-end' : 'items-start'}`}>\n <div className={`flex items-center gap-2 mb-1.5 opacity-90 ${chat.who === 'user' ? 'flex-row-reverse' : 'flex-row'}`}>\n <span className=\"text-[10px] text-zinc-500\">\n {new Date(chat.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}\n </span>\n </div>\n <div\n className={`relative px-5 py-3 rounded-2xl text-md leading-relaxed transition-transform duration-200 ${chat.who === 'user'\n ? 'bg-zinc-800/80 text-white rounded-tr-sm border border-zinc-700/50 shadow-lg backdrop-blur-sm'\n : ' text-zinc-100'\n }`}\n >\n {chat.message}\n </div>\n </div>\n </div>\n </div>\n ))}\n <div ref={messagesEndRef} className=\"h-4\" />\n </div>\n )\n}"
60015
58767
  },
60016
58768
  {
60017
58769
  action: "file",
60018
- file: "public/styles.css",
60019
- filecontent: stylesCSS
58770
+ file: "src/components/Header.tsx",
58771
+ filecontent: '\nexport default function Header() {\n return (\n <div className="w-full h-12">\n <div className="container px-4 max-w-7xl mx-auto h-full flex items-center gap-3">\n <img src="/logo.svg" alt="MonoChat Logo" className="w-8 h-8" />\n <h1 className="font-bold text-lg tracking-wide text-white">\n MonoChat\n </h1>\n </div>\n </div>\n )\n}'
60020
58772
  },
60021
- // Source files
60022
58773
  {
60023
58774
  action: "file",
60024
- file: "src/index.ts",
60025
- filecontent: serverTs
58775
+ file: "src/components/SendIcon.tsx",
58776
+ filecontent: '\nexport default function SendIcon({ className }: { className?: string }) {\n return (\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className={className}>\n <path d="M3.478 2.404a.75.75 0 0 0-.926.941l2.432 7.905H13.5a.75.75 0 0 1 0 1.5H4.984l-2.432 7.905a.75.75 0 0 0 .926.94 60.519 60.519 0 0 0 18.445-8.986.75.75 0 0 0 0-1.218A60.517 60.517 0 0 0 3.478 2.404Z" />\n </svg>\n )\n}'
60026
58777
  },
60027
58778
  {
60028
58779
  action: "file",
60029
- file: "src/vectorStore.ts",
60030
- filecontent: vectorStoreTs
58780
+ file: "src/components/TextInput.tsx",
58781
+ filecontent: `import FetchToWho from "../_FetchToWho";
58782
+ import useChatState from "../app/chat";
58783
+ import SendIcon from "./SendIcon";
58784
+
58785
+ export default function TextInput() {
58786
+ const chats = useChatState.use.chats();
58787
+ const textinput = useChatState.use.textinput();
58788
+ const setTextinput = useChatState.use.setTextinput();
58789
+ const addChat = useChatState.use.addChat();
58790
+
58791
+ const handleSend = async () => {
58792
+ if (!textinput.trim()) return;
58793
+ addChat({
58794
+ id: Date.now(),
58795
+ who: "user",
58796
+ timestamp: Date.now(),
58797
+ message: textinput
58798
+ });
58799
+ setTextinput("");
58800
+
58801
+ const userChat = chats.filter((chat) => chat.who === "user");
58802
+ const response = await FetchToWho(userChat);
58803
+ addChat({
58804
+ id: Date.now(),
58805
+ who: "system",
58806
+ timestamp: Date.now(),
58807
+ message: response
58808
+ });
58809
+ };
58810
+
58811
+ const handleKeyDown = (e: React.KeyboardEvent) => {
58812
+ if (e.key === 'Enter' && !e.shiftKey) {
58813
+ e.preventDefault();
58814
+ handleSend();
58815
+ }
58816
+ };
58817
+
58818
+ return (
58819
+ <div className="p-4 w-full">
58820
+ <div className="w-full max-w-3xl mx-auto">
58821
+ <div className="relative flex items-end gap-2 bg-zinc-900/80 hover:bg-zinc-900/90 focus-within:bg-black/90 transition-all duration-300 border border-white/10 rounded-[24px] p-2 pr-2 shadow-xl backdrop-blur-xl ring-1 ring-white/5 focus-within:ring-indigo-500/30">
58822
+ <textarea
58823
+ value={textinput}
58824
+ onChange={(e) => setTextinput(e.target.value)}
58825
+ onKeyDown={handleKeyDown}
58826
+ placeholder="Message MonoChat..."
58827
+ className="w-full pl-4 py-3 bg-transparent active:bg-transparent border-none outline-none focus:outline-none text-zinc-100 placeholder:text-zinc-500 focus:ring-0 resize-none max-h-48 min-h-[44px] scrollbar-hide text-md leading-6"
58828
+ rows={1}
58829
+ style={{ height: 'auto', minHeight: '44px' }}
58830
+ onInput={(e) => {
58831
+ const target = e.target as HTMLTextAreaElement;
58832
+ target.style.height = 'auto';
58833
+ target.style.height = \`\${Math.min(target.scrollHeight, 192)}px\`;
58834
+ }}
58835
+ />
58836
+ <button
58837
+ onClick={handleSend}
58838
+ disabled={!textinput.trim()}
58839
+ className={\`p-2 rounded-full mb-1 transition-all duration-200 flex-shrink-0 \${textinput.trim()
58840
+ ? 'bg-indigo-600 text-white shadow-lg shadow-indigo-500/20 hover:bg-indigo-500'
58841
+ : 'bg-zinc-800 text-zinc-600 cursor-not-allowed'
58842
+ }\`}
58843
+ >
58844
+ <SendIcon className="w-5 h-5" />
58845
+ </button>
58846
+ </div>
58847
+ </div>
58848
+ </div>
58849
+ )
58850
+ }
58851
+ `
60031
58852
  },
60032
58853
  {
60033
58854
  action: "file",
60034
- file: "src/aiClient.ts",
60035
- filecontent: aiClientTs
58855
+ file: "src/index.css",
58856
+ filecontent: '@import "tailwindcss";'
60036
58857
  },
60037
58858
  {
60038
58859
  action: "file",
60039
- file: "src/routes/chat.ts",
60040
- filecontent: chatRouteTs
58860
+ file: "src/main.tsx",
58861
+ filecontent: "import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './index.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n)"
60041
58862
  },
60042
58863
  {
60043
58864
  action: "file",
60044
- file: "src/routes/embed.ts",
60045
- filecontent: embedRouteTs
58865
+ file: "tailwind.config.js",
58866
+ filecontent: `/** @type {import('tailwindcss').Config} */
58867
+ export default {
58868
+ content: [
58869
+ "./index.html",
58870
+ "./src/**/*.{js,ts,jsx,tsx}",
58871
+ ],
58872
+ theme: {
58873
+ extend: {},
58874
+ },
58875
+ plugins: [],
58876
+ };`
60046
58877
  },
60047
58878
  {
60048
58879
  action: "file",
60049
- file: "src/routes/config.ts",
60050
- filecontent: configRouteTs
58880
+ file: "tsconfig.app.json",
58881
+ filecontent: '{\n "compilerOptions": {\n "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",\n "target": "ES2022",\n "useDefineForClassFields": true,\n "lib": ["ES2022", "DOM", "DOM.Iterable"],\n "module": "ESNext",\n "types": ["vite/client"],\n "skipLibCheck": true,\n\n /* Bundler mode */\n "moduleResolution": "bundler",\n "allowImportingTsExtensions": true,\n "verbatimModuleSyntax": true,\n "moduleDetection": "force",\n "noEmit": true,\n "jsx": "react-jsx",\n\n /* Linting */\n "strict": true,\n "noUnusedLocals": true,\n "noUnusedParameters": true,\n "erasableSyntaxOnly": true,\n "noFallthroughCasesInSwitch": true,\n "noUncheckedSideEffectImports": true\n },\n "include": ["src"]\n}\n'
60051
58882
  },
60052
- // Config files
60053
58883
  {
60054
58884
  action: "file",
60055
58885
  file: "tsconfig.json",
60056
- filecontent: tsconfigJson
58886
+ filecontent: '{\n "files": [],\n "references": [\n { "path": "./tsconfig.app.json" },\n { "path": "./tsconfig.node.json" }\n ]\n}\n'
60057
58887
  },
60058
58888
  {
60059
58889
  action: "file",
60060
- file: "tsup.config.ts",
60061
- filecontent: tsupConfig
58890
+ file: "tsconfig.node.json",
58891
+ filecontent: '{\n "compilerOptions": {\n "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",\n "target": "ES2023",\n "lib": ["ES2023"],\n "module": "ESNext",\n "types": ["node"],\n "skipLibCheck": true,\n\n /* Bundler mode */\n "moduleResolution": "bundler",\n "allowImportingTsExtensions": true,\n "verbatimModuleSyntax": true,\n "moduleDetection": "force",\n "noEmit": true,\n\n /* Linting */\n "strict": true,\n "noUnusedLocals": true,\n "noUnusedParameters": true,\n "erasableSyntaxOnly": true,\n "noFallthroughCasesInSwitch": true,\n "noUncheckedSideEffectImports": true\n },\n "include": ["vite.config.ts"]\n}\n'
60062
58892
  },
60063
- // NPM scripts
60064
58893
  {
60065
- action: "command",
60066
- cmd: "npm",
60067
- args: ["pkg", "set", "scripts.dev=nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/index.ts"]
60068
- },
60069
- {
60070
- action: "command",
60071
- cmd: "npm",
60072
- args: ["pkg", "set", "scripts.build=tsup"]
60073
- },
60074
- {
60075
- action: "command",
60076
- cmd: "npm",
60077
- args: ["pkg", "set", "scripts.start=node dist/index.js"]
58894
+ action: "file",
58895
+ file: "vercel.json",
58896
+ filecontent: '{\n "rewrites": [\n {\n "source": "/(.*)",\n "destination": "/index.html"\n }\n ]\n}'
60078
58897
  },
60079
58898
  {
60080
- action: "command",
60081
- cmd: "npm",
60082
- args: ["pkg", "set", "scripts.stop=npx -y kill-port 3500"]
58899
+ action: "file",
58900
+ file: "vite.config.ts",
58901
+ filecontent: "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vite.dev/config/\nexport default defineConfig({\n plugins: [react()],\n})\n"
60083
58902
  },
60084
58903
  {
60085
58904
  action: "command",
60086
58905
  cmd: "npm",
60087
- args: ["pkg", "set", "fontawesomeIcon=fa-solid fa-comments"]
58906
+ args: ["install"]
60088
58907
  },
60089
58908
  {
60090
58909
  action: "command",
@@ -60093,16 +58912,16 @@ var AIChat = {
60093
58912
  }
60094
58913
  ]
60095
58914
  };
58915
+ var monochat_default = MonoChat;
60096
58916
 
60097
58917
  // ../../packages/template/demo.ts
60098
58918
  var templates2 = [
60099
- AIChat
58919
+ monochat_default
60100
58920
  ];
60101
58921
  var demo_default = templates2;
60102
58922
 
60103
- // ../../packages/template/projects/_viteapp.ts
60104
- var file = `import { useState } from 'react'
60105
-
58923
+ // ../../packages/template/projects/files/_viteapp.ts
58924
+ var file = `
60106
58925
  function App() {
60107
58926
  return (
60108
58927
  <div className="min-h-screen bg-slate-950 text-white font-sans selection:bg-indigo-500 selection:text-white">
@@ -60227,7 +59046,7 @@ var ViteReact = {
60227
59046
  {
60228
59047
  action: "command",
60229
59048
  cmd: "npm",
60230
- args: ["install", "-D", "tailwindcss", "@tailwindcss/postcss", "autoprefixer"]
59049
+ args: ["install", "-D", "tailwindcss", "@tailwindcss/postcss", "postcss", "autoprefixer"]
60231
59050
  },
60232
59051
  {
60233
59052
  action: "file",
@@ -60235,19 +59054,9 @@ var ViteReact = {
60235
59054
  filecontent: 'export default {\n plugins: {\n "@tailwindcss/postcss": {},\n autoprefixer: {},\n },\n}'
60236
59055
  },
60237
59056
  {
60238
- action: "file",
60239
- file: "tailwind.config.js",
60240
- filecontent: `/** @type {import('tailwindcss').Config} */
60241
- export default {
60242
- content: [
60243
- "./index.html",
60244
- "./src/**/*.{js,ts,jsx,tsx}",
60245
- ],
60246
- theme: {
60247
- extend: {},
60248
- },
60249
- plugins: [],
60250
- };`
59057
+ action: "command",
59058
+ cmd: "rm",
59059
+ args: ["src/App.css"]
60251
59060
  },
60252
59061
  {
60253
59062
  action: "file",
@@ -60268,16 +59077,23 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
60268
59077
  </React.StrictMode>,
60269
59078
  )`
60270
59079
  },
60271
- {
60272
- action: "command",
60273
- cmd: "rm",
60274
- args: ["src/App.css"]
60275
- },
60276
59080
  {
60277
59081
  action: "file",
60278
59082
  file: "src/App.tsx",
60279
59083
  filecontent: viteapp_default
60280
59084
  },
59085
+ {
59086
+ action: "file",
59087
+ file: "vercel.json",
59088
+ filecontent: `{
59089
+ "rewrites": [
59090
+ {
59091
+ "source": "/(.*)",
59092
+ "destination": "/index.html"
59093
+ }
59094
+ ]
59095
+ }`
59096
+ },
60281
59097
  {
60282
59098
  action: "command",
60283
59099
  cmd: "npm",
@@ -60288,15 +59104,42 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
60288
59104
  cmd: "npm",
60289
59105
  args: ["pkg", "set", "scripts.stop=npx -y kill-port 5173"]
60290
59106
  },
59107
+ {
59108
+ action: "file",
59109
+ file: "netlify.toml",
59110
+ filecontent: `[build]
59111
+ command = "npm run build"
59112
+ publish = "dist"
59113
+
59114
+ [[redirects]]
59115
+ from = "/*"
59116
+ to = "/index.html"
59117
+ status = 200`
59118
+ },
59119
+ {
59120
+ action: "file",
59121
+ file: "render.yaml",
59122
+ filecontent: `services:
59123
+ - type: web
59124
+ name: vite-react-app
59125
+ env: static
59126
+ buildCommand: npm install && npm run build
59127
+ staticPublishPath: ./dist`
59128
+ },
59129
+ {
59130
+ action: "command",
59131
+ cmd: "npm",
59132
+ args: ["pkg", "set", "description=Vite React TS"]
59133
+ },
60291
59134
  {
60292
59135
  action: "command",
60293
59136
  cmd: "npm",
60294
- args: ["pkg", "set", "fontawesomeIcon=fab fa-react text-blue-400"]
59137
+ args: ["pkg", "set", "fontawesomeIcon=fab fa-react text-blue-500"]
60295
59138
  }
60296
59139
  ]
60297
59140
  };
60298
59141
 
60299
- // ../../packages/template/projects/_nextapp.ts
59142
+ // ../../packages/template/projects/files/_nextapp.ts
60300
59143
  var app = `export default function Home() {
60301
59144
  return (
60302
59145
  <div className="min-h-screen bg-slate-950 text-white font-sans selection:bg-indigo-500 selection:text-white">
@@ -60494,11 +59337,44 @@ export default function RootLayout({
60494
59337
  file: "app/page.tsx",
60495
59338
  filecontent: nextapp_default
60496
59339
  },
59340
+ {
59341
+ action: "file",
59342
+ file: "vercel.json",
59343
+ filecontent: '{\n "framework": "nextjs"\n}'
59344
+ },
60497
59345
  {
60498
59346
  action: "command",
60499
59347
  cmd: "npm",
60500
59348
  args: ["pkg", "set", "name=$(basename $PWD)"]
60501
59349
  },
59350
+ {
59351
+ action: "file",
59352
+ file: "netlify.toml",
59353
+ filecontent: `[build]
59354
+ command = "npm run build"
59355
+ publish = ".next"`
59356
+ },
59357
+ {
59358
+ action: "file",
59359
+ file: "render.yaml",
59360
+ filecontent: `services:
59361
+ - type: web
59362
+ name: nextjs-app
59363
+ env: node
59364
+ plan: free
59365
+ buildCommand: npm install && npm run build
59366
+ startCommand: npm start`
59367
+ },
59368
+ {
59369
+ action: "file",
59370
+ file: "Procfile",
59371
+ filecontent: "web: npm start"
59372
+ },
59373
+ {
59374
+ action: "command",
59375
+ cmd: "npm",
59376
+ args: ["pkg", "set", "description=Next.js TS"]
59377
+ },
60502
59378
  {
60503
59379
  action: "command",
60504
59380
  cmd: "npm",
@@ -60507,14 +59383,16 @@ export default function RootLayout({
60507
59383
  ]
60508
59384
  };
60509
59385
 
60510
- // ../../packages/template/projects/express.ts
59386
+ // ../../packages/template/projects/files/_express.ts
60511
59387
  var expressFile = `import express, { Request, Response } from "express";
60512
59388
  import path from "path";
59389
+ import cors from "cors";
60513
59390
  import helloRouter from "./routes/hello";
60514
59391
 
60515
59392
  const app = express();
60516
- const port = 3500;
59393
+ const port = process.env.PORT || 3500;
60517
59394
 
59395
+ app.use(cors());
60518
59396
  app.use(express.static(path.join(__dirname, "../public")));
60519
59397
  app.use(express.json());
60520
59398
 
@@ -60604,11 +59482,21 @@ var htmlFile = `<!DOCTYPE html>
60604
59482
  <div class="container">
60605
59483
  <h1>Express Service</h1>
60606
59484
  <p>Your backend service is up and running successfully. Ready to handle API requests.</p>
59485
+ <p style="margin-top: 1rem; padding: 1rem; background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 8px; color: #ffc107; font-size: 0.9rem;">
59486
+ <strong>Caution:</strong> Any origin is allowed. Update the CORS configuration in <code>src/index.ts</code> and headers in <code>vercel.json</code> to whitelist only your frontend origin to prevent unauthorized usage.
59487
+ </p>
60607
59488
  <div class="status-badge">\u25CF System Online</div>
60608
59489
  </div>
60609
59490
  </body>
60610
59491
  </html>
60611
59492
  `;
59493
+ var express_default = {
59494
+ expressFile,
59495
+ helloRouterFile,
59496
+ htmlFile
59497
+ };
59498
+
59499
+ // ../../packages/template/projects/express.ts
60612
59500
  var ExpressTS = {
60613
59501
  name: "Express.js TS",
60614
59502
  description: "Express.js TS template",
@@ -60619,15 +59507,20 @@ var ExpressTS = {
60619
59507
  cmd: "npm",
60620
59508
  args: ["init", "-y"]
60621
59509
  },
59510
+ {
59511
+ action: "command",
59512
+ cmd: "node",
59513
+ args: ["-e", "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json'));if(p.name==='express'){p.name='express-app';fs.writeFileSync('package.json',JSON.stringify(p,null,2))}"]
59514
+ },
60622
59515
  {
60623
59516
  action: "command",
60624
59517
  cmd: "npm",
60625
- args: ["install", "express"]
59518
+ args: ["install", "express", "cors"]
60626
59519
  },
60627
59520
  {
60628
59521
  action: "command",
60629
59522
  cmd: "npm",
60630
- args: ["install", "-D", "nodemon", "typescript", "ts-node", "@types/node", "@types/express"]
59523
+ args: ["install", "-D", "nodemon", "typescript", "ts-node", "@types/node", "@types/express", "@types/cors"]
60631
59524
  },
60632
59525
  {
60633
59526
  action: "command",
@@ -60637,17 +59530,17 @@ var ExpressTS = {
60637
59530
  {
60638
59531
  action: "file",
60639
59532
  file: "public/index.html",
60640
- filecontent: htmlFile
59533
+ filecontent: express_default.htmlFile
60641
59534
  },
60642
59535
  {
60643
59536
  action: "file",
60644
59537
  file: "src/routes/hello.ts",
60645
- filecontent: helloRouterFile
59538
+ filecontent: express_default.helloRouterFile
60646
59539
  },
60647
59540
  {
60648
59541
  action: "file",
60649
59542
  file: "src/index.ts",
60650
- filecontent: expressFile
59543
+ filecontent: express_default.expressFile
60651
59544
  },
60652
59545
  {
60653
59546
  action: "file",
@@ -60679,6 +59572,54 @@ var ExpressTS = {
60679
59572
  cmd: "npm",
60680
59573
  args: ["pkg", "set", "scripts.stop=npx -y kill-port 3500"]
60681
59574
  },
59575
+ {
59576
+ action: "file",
59577
+ file: "vercel.json",
59578
+ filecontent: `{
59579
+ "version": 2,
59580
+ "rewrites": [
59581
+ {
59582
+ "source": "/(.*)",
59583
+ "destination": "/dist/index.js"
59584
+ }
59585
+ ],
59586
+ "headers": [
59587
+ {
59588
+ "source": "/(.*)",
59589
+ "headers": [
59590
+ { "key": "Access-Control-Allow-Credentials", "value": "true" },
59591
+ { "key": "Access-Control-Allow-Origin", "value": "*" },
59592
+ { "key": "Access-Control-Allow-Methods", "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
59593
+ { "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
59594
+ ]
59595
+ }
59596
+ ]
59597
+ }`
59598
+ },
59599
+ {
59600
+ action: "file",
59601
+ file: "render.yaml",
59602
+ filecontent: `services:
59603
+ - type: web
59604
+ name: express-service
59605
+ env: node
59606
+ plan: free
59607
+ buildCommand: npm install && npm run build
59608
+ startCommand: npm start
59609
+ envVars:
59610
+ - key: PORT
59611
+ value: 10000`
59612
+ },
59613
+ {
59614
+ action: "file",
59615
+ file: "Procfile",
59616
+ filecontent: "web: npm start"
59617
+ },
59618
+ {
59619
+ action: "command",
59620
+ cmd: "npm",
59621
+ args: ["pkg", "set", "description=Express.js TS"]
59622
+ },
60682
59623
  {
60683
59624
  action: "command",
60684
59625
  cmd: "npm",
@@ -60687,6 +59628,434 @@ var ExpressTS = {
60687
59628
  ]
60688
59629
  };
60689
59630
 
59631
+ // ../../packages/template/projects/files/_serverless.ts
59632
+ var htmlFile2 = `<!DOCTYPE html>
59633
+ <html lang="en">
59634
+ <head>
59635
+ <meta charset="UTF-8">
59636
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
59637
+ <title>Monorepo Time - Universal Express Service</title>
59638
+ <style>
59639
+ body {
59640
+ background: linear-gradient(135deg, #2d2a4e 0%, #1c1c38 100%);
59641
+ color: #ffffff;
59642
+ display: flex;
59643
+ justify-content: center;
59644
+ align-items: center;
59645
+ height: 100vh;
59646
+ margin: 0;
59647
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
59648
+ overflow: hidden;
59649
+ }
59650
+ .container {
59651
+ text-align: center;
59652
+ padding: 3rem;
59653
+ background: rgba(255, 255, 255, 0.08);
59654
+ border: 1px solid rgba(255, 255, 255, 0.15);
59655
+ border-radius: 20px;
59656
+ backdrop-filter: blur(12px);
59657
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
59658
+ max-width: 550px;
59659
+ width: 100%;
59660
+ animation: fadeIn 0.8s ease-out;
59661
+ }
59662
+ h1 {
59663
+ font-size: 2.5rem;
59664
+ margin-bottom: 0.5rem;
59665
+ background: linear-gradient(to right, #ff9966 0%, #ff5e62 100%);
59666
+ -webkit-background-clip: text;
59667
+ -webkit-text-fill-color: transparent;
59668
+ font-weight: 800;
59669
+ }
59670
+ p {
59671
+ font-size: 1.1rem;
59672
+ color: #c9d6ea;
59673
+ line-height: 1.6;
59674
+ }
59675
+ .status-badge {
59676
+ display: inline-block;
59677
+ margin-top: 1.5rem;
59678
+ padding: 0.5rem 1rem;
59679
+ background: rgba(255, 153, 102, 0.15);
59680
+ color: #ff9966;
59681
+ border-radius: 50px;
59682
+ font-weight: 600;
59683
+ border: 1px solid rgba(255, 153, 102, 0.25);
59684
+ text-transform: uppercase;
59685
+ font-size: 0.8rem;
59686
+ letter-spacing: 1px;
59687
+ }
59688
+ code {
59689
+ background: rgba(255, 255, 255, 0.1);
59690
+ padding: 2px 4px;
59691
+ border-radius: 4px;
59692
+ font-size: 0.9em;
59693
+ }
59694
+ @keyframes fadeIn {
59695
+ from { opacity: 0; transform: translateY(20px); }
59696
+ to { opacity: 1; transform: translateY(0); }
59697
+ }
59698
+ </style>
59699
+ </head>
59700
+ <body>
59701
+ <div class="container">
59702
+ <h1>Universal Express</h1>
59703
+ <p>Your serverless-ready service is active. Deploy to AWS, Vercel, Netlify, or Docker with zero config changes.</p>
59704
+ <div style="margin-top: 2rem; display: flex; justify-content: center; gap: 10px; flex-wrap: wrap;">
59705
+ <span class="status-badge">AWS</span>
59706
+ <span class="status-badge">Netlify</span>
59707
+ <span class="status-badge">Vercel</span>
59708
+ <span class="status-badge">Docker</span>
59709
+ </div>
59710
+ <p style="margin-top: 1.5rem; font-size: 0.9rem; opacity: 0.7;">
59711
+ Check <code>src/app.ts</code> for routing logic.
59712
+ </p>
59713
+ </div>
59714
+ </body>
59715
+ </html>
59716
+ `;
59717
+ var appFile = `import express, { Request, Response } from "express";
59718
+ import cors from "cors";
59719
+ import path from "path";
59720
+ import helloRouter from "./routes/hello";
59721
+
59722
+ const app = express();
59723
+
59724
+ app.use(cors());
59725
+ app.use(express.json());
59726
+ // Serve static files from the 'public' directory
59727
+ app.use(express.static(path.join(__dirname, "../public")));
59728
+
59729
+ const router = express.Router();
59730
+ router.get("/", (req: Request, res: Response) => {
59731
+ res.sendFile(path.join(__dirname, "../public/index.html"));
59732
+ });
59733
+
59734
+ router.use("/hello", helloRouter);
59735
+
59736
+ // Mount router at root and api paths to handle various gateway configurations
59737
+ app.use("/.netlify/functions/api", router);
59738
+ app.use("/api", router);
59739
+ app.use("/", router);
59740
+
59741
+ export default app;
59742
+ `;
59743
+ var localFile = `import app from "./app";
59744
+
59745
+ const port = process.env.PORT || 3500;
59746
+
59747
+ app.listen(port, () => {
59748
+ console.log("Server running locally at http://localhost:" + port);
59749
+ });
59750
+ `;
59751
+ var vercelApiFile = `import app from "../src/app";
59752
+ export default app;
59753
+ `;
59754
+ var netlifyFunctionFile = `import app from "../../src/app";
59755
+ import serverless from "serverless-http";
59756
+ export const handler = serverless(app);
59757
+ `;
59758
+ var helloRouterFile2 = `import { Router, Request, Response } from "express";
59759
+
59760
+ const router = Router();
59761
+
59762
+ router.get("/", (req: Request, res: Response) => {
59763
+ res.json({ message: "Hello World" });
59764
+ });
59765
+
59766
+ export default router;
59767
+ `;
59768
+ var lambdaFile = `import app from "./app";
59769
+ import serverless from "serverless-http";
59770
+ export const handler = serverless(app);
59771
+ `;
59772
+ var dockerFile = `# Use the official Node.js image.
59773
+ FROM node:18-alpine
59774
+
59775
+ # Create and change to the app directory.
59776
+ WORKDIR /usr/src/app
59777
+
59778
+ # Copy application dependency manifests to the container image.
59779
+ COPY package*.json ./
59780
+
59781
+ # Install production dependencies.
59782
+ # If you add a package-lock.json, speed your build by switching to 'npm ci'.
59783
+ RUN npm install --only=production
59784
+
59785
+ # Copy local code to the container image.
59786
+ COPY . .
59787
+
59788
+ # Compile TS (if you are running build inside docker)
59789
+ # RUN npm install typescript tsup
59790
+ # RUN npm run build
59791
+
59792
+ # Run the web service on container startup.
59793
+ CMD [ "npm", "start" ]
59794
+ `;
59795
+ var serverlessYmlFile = `service: my-express-service
59796
+
59797
+ provider:
59798
+ name: aws
59799
+ runtime: nodejs18.x
59800
+ region: us-east-1
59801
+
59802
+ functions:
59803
+ api:
59804
+ handler: dist/lambda.handler
59805
+ events:
59806
+ - httpApi: '*'
59807
+ `;
59808
+ var serverless_default = {
59809
+ htmlFile: htmlFile2,
59810
+ appFile,
59811
+ localFile,
59812
+ vercelApiFile,
59813
+ netlifyFunctionFile,
59814
+ helloRouterFile: helloRouterFile2,
59815
+ lambdaFile,
59816
+ dockerFile,
59817
+ serverlessYmlFile
59818
+ };
59819
+
59820
+ // ../../packages/template/projects/serverless-express.ts
59821
+ var ServerlessExpressTS = {
59822
+ name: "Serverless Express TS",
59823
+ description: "Serverless Express TS template optimized for Serverless (Netlify, Vercel, AWS) & Containers (Docker, Render, Fly.io)",
59824
+ notes: "Node.js and NPM must be installed.",
59825
+ templating: [
59826
+ {
59827
+ action: "command",
59828
+ cmd: "npm",
59829
+ args: ["init", "-y"]
59830
+ },
59831
+ {
59832
+ action: "command",
59833
+ cmd: "npm",
59834
+ args: ["install", "express", "cors", "serverless-http"]
59835
+ },
59836
+ {
59837
+ action: "command",
59838
+ cmd: "npm",
59839
+ args: ["install", "-D", "nodemon", "typescript", "ts-node", "tsup", "@types/node", "@types/express", "@types/cors"]
59840
+ },
59841
+ // App Files
59842
+ {
59843
+ action: "file",
59844
+ file: "public/index.html",
59845
+ filecontent: serverless_default.htmlFile
59846
+ },
59847
+ {
59848
+ action: "file",
59849
+ file: "src/app.ts",
59850
+ filecontent: serverless_default.appFile
59851
+ },
59852
+ {
59853
+ action: "file",
59854
+ file: "src/local.ts",
59855
+ filecontent: serverless_default.localFile
59856
+ },
59857
+ {
59858
+ action: "file",
59859
+ file: "src/routes/hello.ts",
59860
+ filecontent: serverless_default.helloRouterFile
59861
+ },
59862
+ // Serverless Entry Points
59863
+ {
59864
+ action: "file",
59865
+ file: "api/index.ts",
59866
+ filecontent: serverless_default.vercelApiFile
59867
+ },
59868
+ {
59869
+ action: "file",
59870
+ file: "netlify/functions/api.ts",
59871
+ filecontent: serverless_default.netlifyFunctionFile
59872
+ },
59873
+ {
59874
+ action: "file",
59875
+ file: "src/lambda.ts",
59876
+ filecontent: serverless_default.lambdaFile
59877
+ },
59878
+ // Container Configs
59879
+ {
59880
+ action: "file",
59881
+ file: "Dockerfile",
59882
+ filecontent: serverless_default.dockerFile
59883
+ },
59884
+ {
59885
+ action: "file",
59886
+ file: "app.yaml",
59887
+ filecontent: "runtime: nodejs18\nservice: default\n"
59888
+ },
59889
+ {
59890
+ action: "file",
59891
+ file: "serverless.yml",
59892
+ filecontent: serverless_default.serverlessYmlFile
59893
+ },
59894
+ // Config Files
59895
+ {
59896
+ action: "file",
59897
+ file: "tsup.config.ts",
59898
+ filecontent: "import { defineConfig } from 'tsup';\n\nexport default defineConfig({\n entry: ['src/local.ts', 'src/lambda.ts'],\n splitting: false,\n sourcemap: true,\n clean: true,\n format: ['cjs'],\n});"
59899
+ },
59900
+ {
59901
+ action: "file",
59902
+ file: "tsconfig.json",
59903
+ filecontent: '{\n "compilerOptions": {\n "target": "es2016",\n "module": "commonjs",\n "outDir": "./dist",\n "esModuleInterop": true,\n "forceConsistentCasingInFileNames": true,\n "strict": true,\n "skipLibCheck": true\n },\n "include": ["src/**/*", "api/**/*", "netlify/**/*"]\n}'
59904
+ },
59905
+ // Deployment Configs
59906
+ {
59907
+ action: "file",
59908
+ file: "render.yaml",
59909
+ filecontent: `services:
59910
+ - type: web
59911
+ name: express-serverless-service
59912
+ env: node
59913
+ plan: free
59914
+ buildCommand: npm install && npm run build
59915
+ startCommand: npm start
59916
+ envVars:
59917
+ - key: PORT
59918
+ value: 10000`
59919
+ },
59920
+ {
59921
+ action: "file",
59922
+ file: "Procfile",
59923
+ filecontent: "web: npm start"
59924
+ },
59925
+ {
59926
+ action: "file",
59927
+ file: "vercel.json",
59928
+ filecontent: `{
59929
+ "version": 2,
59930
+ "rewrites": [
59931
+ { "source": "/(.*)", "destination": "/api/index" }
59932
+ ]
59933
+ }`
59934
+ },
59935
+ {
59936
+ action: "file",
59937
+ file: "netlify.toml",
59938
+ filecontent: `[build]
59939
+ command = "npm run build"
59940
+ functions = "functions"
59941
+
59942
+ # Redirect all traffic to the lambda function
59943
+ [[redirects]]
59944
+ from = "/*"
59945
+ to = "/.netlify/functions/api"
59946
+ status = 200
59947
+ force = true`
59948
+ },
59949
+ // Scripts
59950
+ {
59951
+ action: "command",
59952
+ cmd: "npm",
59953
+ args: ["pkg", "set", "scripts.dev=nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/local.ts"]
59954
+ },
59955
+ {
59956
+ action: "command",
59957
+ cmd: "npm",
59958
+ args: ["pkg", "set", "scripts.build=tsup"]
59959
+ },
59960
+ {
59961
+ action: "command",
59962
+ cmd: "npm",
59963
+ args: ["pkg", "set", "scripts.start=node dist/local.js"]
59964
+ },
59965
+ {
59966
+ action: "command",
59967
+ cmd: "npm",
59968
+ args: ["pkg", "set", "scripts.stop=npx -y kill-port 3500"]
59969
+ },
59970
+ {
59971
+ action: "command",
59972
+ cmd: "npm",
59973
+ args: ["pkg", "set", "description=Serverless Express TS"]
59974
+ },
59975
+ {
59976
+ action: "command",
59977
+ cmd: "npm",
59978
+ args: ["pkg", "set", "fontawesomeIcon=fab fa-aws text-orange-500"]
59979
+ }
59980
+ ]
59981
+ };
59982
+
59983
+ // ../../packages/template/projects/files/_php.ts
59984
+ var phpContent = `<?php
59985
+ $file = 'visits.txt';
59986
+ if (!file_exists($file)) {
59987
+ file_put_contents($file, 0);
59988
+ }
59989
+ $count = (int)file_get_contents($file);
59990
+ $count++;
59991
+ file_put_contents($file, $count);
59992
+ ?>
59993
+ <!DOCTYPE html>
59994
+ <html lang="en">
59995
+ <head>
59996
+ <meta charset="UTF-8">
59997
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
59998
+ <title>Welcome to Monorepo Time</title>
59999
+ <script src="https://cdn.tailwindcss.com"></script>
60000
+ <style>
60001
+ @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&display=swap');
60002
+ body { font-family: 'Outfit', sans-serif; }
60003
+ </style>
60004
+ </head>
60005
+ <body class="bg-gray-900 text-white min-h-screen flex items-center justify-center relative overflow-hidden">
60006
+ <!-- Background Elements -->
60007
+ <div class="absolute top-0 left-0 w-full h-full overflow-hidden z-0">
60008
+ <div class="absolute top-[-10%] right-[-10%] w-96 h-96 bg-purple-600 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob"></div>
60009
+ <div class="absolute bottom-[-10%] left-[-10%] w-96 h-96 bg-blue-600 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob animation-delay-2000"></div>
60010
+ </div>
60011
+
60012
+ <div class="z-10 text-center p-8 bg-white/10 backdrop-blur-lg rounded-2xl border border-white/20 shadow-2xl max-w-lg w-full transform hover:scale-105 transition-transform duration-300">
60013
+ <div class="mb-6">
60014
+ <span class="text-4xl">\u{1F680}</span>
60015
+ </div>
60016
+ <h1 class="text-4xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-400">
60017
+ Monorepo Time
60018
+ </h1>
60019
+ <p class="text-gray-300 text-lg mb-6">
60020
+ Your PHP application is up and running.
60021
+ </p>
60022
+
60023
+ <div class="bg-black/30 rounded-xl p-4 mb-6">
60024
+ <p class="text-sm text-gray-400 uppercase tracking-widest mb-1">Total Visits</p>
60025
+ <p class="text-3xl font-mono font-bold text-green-400">
60026
+ <?php echo number_format($count); ?>
60027
+ </p>
60028
+ </div>
60029
+
60030
+ <div class="flex justify-center gap-4">
60031
+ <a href="#" class="px-6 py-2 bg-blue-600 hover:bg-blue-700 rounded-full font-medium transition-colors duration-200">
60032
+ Documentation
60033
+ </a>
60034
+ <a href="#" class="px-6 py-2 bg-transparent border border-white/30 hover:bg-white/10 rounded-full font-medium transition-colors duration-200">
60035
+ Learn More
60036
+ </a>
60037
+ </div>
60038
+ </div>
60039
+
60040
+ <!-- Animation Keyframes -->
60041
+ <style>
60042
+ @keyframes blob {
60043
+ 0% { transform: translate(0px, 0px) scale(1); }
60044
+ 33% { transform: translate(30px, -50px) scale(1.1); }
60045
+ 66% { transform: translate(-20px, 20px) scale(0.9); }
60046
+ 100% { transform: translate(0px, 0px) scale(1); }
60047
+ }
60048
+ .animate-blob {
60049
+ animation: blob 7s infinite;
60050
+ }
60051
+ .animation-delay-2000 {
60052
+ animation-delay: 2s;
60053
+ }
60054
+ </style>
60055
+ </body>
60056
+ </html>
60057
+ `;
60058
+
60690
60059
  // ../../packages/template/projects/php.ts
60691
60060
  var PHP = {
60692
60061
  name: "PHP",
@@ -60696,27 +60065,27 @@ var PHP = {
60696
60065
  {
60697
60066
  action: "file",
60698
60067
  file: "index.php",
60699
- filecontent: '<?php\n\necho "Hello World! Monorepo Time!";\n'
60068
+ filecontent: phpContent
60700
60069
  },
60701
60070
  {
60702
60071
  action: "command",
60703
60072
  cmd: "npm",
60704
- args: ["pkg", "set", "scripts.dev=php -S localhost:3000"]
60073
+ args: ["pkg", "set", "scripts.start=php -S localhost:3000"]
60705
60074
  },
60706
60075
  {
60707
60076
  action: "command",
60708
60077
  cmd: "npm",
60709
- args: ["pkg", "set", "scripts.start=php -S localhost:3000"]
60078
+ args: ["pkg", "set", "scripts.stop=npx kill-port 3000"]
60710
60079
  },
60711
60080
  {
60712
60081
  action: "command",
60713
60082
  cmd: "npm",
60714
- args: ["pkg", "set", "scripts.stop=npx kill-port 3000"]
60083
+ args: ["pkg", "set", "description=PHP"]
60715
60084
  },
60716
60085
  {
60717
60086
  action: "command",
60718
60087
  cmd: "npm",
60719
- args: ["pkg", "set", "fontawesomeIcon=fab fa-php text-indigo-400"]
60088
+ args: ["pkg", "set", "fontawesomeIcon=fab fa-php text-indigo-500"]
60720
60089
  }
60721
60090
  ]
60722
60091
  };
@@ -60757,6 +60126,11 @@ var Laravel = {
60757
60126
  cmd: "npm",
60758
60127
  args: ["pkg", "set", "scripts.stop=npx -y kill-port 8000"]
60759
60128
  },
60129
+ {
60130
+ action: "command",
60131
+ cmd: "npm",
60132
+ args: ["pkg", "set", "description=Laravel"]
60133
+ },
60760
60134
  {
60761
60135
  action: "command",
60762
60136
  cmd: "npm",
@@ -60765,31 +60139,171 @@ var Laravel = {
60765
60139
  ]
60766
60140
  };
60767
60141
 
60768
- // ../../packages/template/projects/python.ts
60769
- var pythonFile = `print("Monorepo Time Console!")
60770
- name = input("Please enter your name: ")
60771
- print("Hello " + name)
60142
+ // ../../packages/template/projects/files/_python.ts
60143
+ var mainPy = `import http.server
60144
+ import socketserver
60145
+ import os
60146
+ import sys
60147
+
60148
+ # Define port, default to 3000 or use environment variable
60149
+ PORT = int(os.environ.get('PORT', 3000))
60150
+
60151
+ class MyHandler(http.server.SimpleHTTPRequestHandler):
60152
+ def do_GET(self):
60153
+ # Route: / -> Load index.html
60154
+ if self.path == '/':
60155
+ self.path = 'index.html'
60156
+ return http.server.SimpleHTTPRequestHandler.do_GET(self)
60157
+
60158
+ # Route: /test -> Return "Hello World"
60159
+ elif self.path == '/test':
60160
+ self.send_response(200)
60161
+ self.send_header('Content-type', 'text/plain')
60162
+ self.end_headers()
60163
+ self.wfile.write(b"Hello World")
60164
+ return
60165
+
60166
+ # Default: Serve static files
60167
+ return http.server.SimpleHTTPRequestHandler.do_GET(self)
60168
+
60169
+ # Ensure index.html exists (though template should create it)
60170
+ if not os.path.exists('index.html'):
60171
+ with open('index.html', 'w') as f:
60172
+ f.write("<h1>Error: index.html not found</h1>")
60173
+
60174
+ print(f"Python server is running at http://localhost:{PORT}")
60175
+ print("Press Ctrl+C to stop.")
60176
+
60177
+ try:
60178
+ # Allow address reuse to prevent "Address already in use" errors on restart
60179
+ socketserver.TCPServer.allow_reuse_address = True
60180
+ with socketserver.TCPServer(("", PORT), MyHandler) as httpd:
60181
+ httpd.serve_forever()
60182
+ except KeyboardInterrupt:
60183
+ print("^C received, shutting down server")
60184
+ sys.exit(0)
60772
60185
  `;
60186
+ var indexHtml = `<!DOCTYPE html>
60187
+ <html lang="en">
60188
+ <head>
60189
+ <meta charset="UTF-8">
60190
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
60191
+ <title>Monorepo Time - Python Backend</title>
60192
+ <script src="https://cdn.tailwindcss.com"></script>
60193
+ <style>
60194
+ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
60195
+ body { font-family: 'JetBrains Mono', monospace; }
60196
+ .glass {
60197
+ background: rgba(255, 255, 255, 0.05);
60198
+ backdrop-filter: blur(10px);
60199
+ -webkit-backdrop-filter: blur(10px);
60200
+ border: 1px solid rgba(255, 255, 255, 0.1);
60201
+ }
60202
+ </style>
60203
+ </head>
60204
+ <body class="bg-[#0f172a] text-gray-200 min-h-screen flex items-center justify-center relative overflow-hidden">
60205
+ <!-- Background Decor -->
60206
+ <div class="absolute top-0 left-0 w-full h-full overflow-hidden z-0 pointer-events-none">
60207
+ <div class="absolute top-1/4 left-1/4 w-96 h-96 bg-blue-500/20 rounded-full blur-[100px]"></div>
60208
+ <div class="absolute bottom-1/4 right-1/4 w-96 h-96 bg-yellow-500/10 rounded-full blur-[100px]"></div>
60209
+ </div>
60210
+
60211
+ <!-- Main Content -->
60212
+ <div class="z-10 w-full max-w-2xl px-4">
60213
+ <div class="glass rounded-2xl p-8 md:p-12 shadow-2xl border border-white/5 transform transition-all hover:scale-[1.01]">
60214
+ <div class="flex items-center justify-between mb-8">
60215
+ <div class="flex items-center space-x-3">
60216
+ <span class="text-4xl">\u{1F40D}</span>
60217
+ <h1 class="text-3xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-yellow-300">
60218
+ Python Backend
60219
+ </h1>
60220
+ </div>
60221
+ <div class="flex items-center space-x-2">
60222
+ <div class="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
60223
+ <span class="text-xs font-mono text-green-400 uppercase tracking-widest">Online</span>
60224
+ </div>
60225
+ </div>
60226
+
60227
+ <div class="space-y-6">
60228
+ <p class="text-xl text-gray-300 leading-relaxed font-light">
60229
+ Python server is running.
60230
+ </p>
60231
+
60232
+ <div class="p-4 rounded-lg bg-black/30 border border-white/10 font-mono text-sm text-gray-400">
60233
+ <p>$ python main.py</p>
60234
+ <p class="text-green-400">>> Server started at http://localhost:3000</p>
60235
+ </div>
60236
+
60237
+ <div class="pt-4 flex flex-col sm:flex-row gap-4">
60238
+ <a href="/test" class="group relative px-8 py-3 bg-blue-600 hover:bg-blue-500 rounded-lg font-bold text-white transition-all shadow-[0_0_20px_rgba(37,99,235,0.3)] hover:shadow-[0_0_30px_rgba(37,99,235,0.5)] overflow-hidden">
60239
+ <span class="relative z-10 flex items-center justify-center gap-2">
60240
+ Test Endpoint
60241
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 transition-transform group-hover:translate-x-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
60242
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
60243
+ </svg>
60244
+ </span>
60245
+ </a>
60246
+
60247
+ <a href="https://docs.python.org/3/library/http.server.html" target="_blank" class="px-8 py-3 bg-transparent border border-white/20 hover:bg-white/5 rounded-lg font-bold text-gray-300 transition-colors text-center">
60248
+ Docs
60249
+ </a>
60250
+ </div>
60251
+ </div>
60252
+ </div>
60253
+
60254
+ <div class="mt-8 text-center text-sm text-gray-500">
60255
+ <p>Powered by Python Standard Library & Tailwind CSS</p>
60256
+ </div>
60257
+ </div>
60258
+ </body>
60259
+ </html>
60260
+ `;
60261
+ var python_default = {
60262
+ mainPy,
60263
+ indexHtml
60264
+ };
60265
+
60266
+ // ../../packages/template/projects/python.ts
60773
60267
  var PythonConsole = {
60774
- name: "Python Console",
60775
- description: "Simple Python Console Application",
60268
+ name: "Python Backend",
60269
+ description: "Simple Python Backend Application",
60776
60270
  notes: "Python 3 must be installed in your system.",
60777
60271
  templating: [
60778
60272
  {
60779
60273
  action: "file",
60780
60274
  file: "main.py",
60781
- filecontent: pythonFile
60275
+ filecontent: python_default.mainPy
60276
+ },
60277
+ {
60278
+ action: "file",
60279
+ file: "index.html",
60280
+ filecontent: python_default.indexHtml
60782
60281
  },
60783
60282
  {
60784
60283
  action: "command",
60785
60284
  cmd: "npm",
60786
- args: ["pkg", "set", "scripts.dev=python3 main.py"]
60285
+ args: ["install", "-D", "nodemon"]
60286
+ },
60287
+ {
60288
+ action: "command",
60289
+ cmd: "npm",
60290
+ args: ["pkg", "set", "scripts.dev=nodemon --watch . --ext py --exec python3 main.py"]
60787
60291
  },
60788
60292
  {
60789
60293
  action: "command",
60790
60294
  cmd: "npm",
60791
60295
  args: ["pkg", "set", "scripts.start=python3 main.py"]
60792
60296
  },
60297
+ {
60298
+ action: "command",
60299
+ cmd: "npm",
60300
+ args: ["pkg", "set", "description=Python Backend"]
60301
+ },
60302
+ {
60303
+ action: "command",
60304
+ cmd: "npm",
60305
+ args: ["pkg", "set", "scripts.stop=npx kill-port 3000"]
60306
+ },
60793
60307
  {
60794
60308
  action: "command",
60795
60309
  cmd: "npm",
@@ -60833,7 +60347,12 @@ var DotNetConsole = {
60833
60347
  {
60834
60348
  action: "command",
60835
60349
  cmd: "npm",
60836
- args: ["pkg", "set", "fontawesomeIcon=fab fa-windows text-blue-600"]
60350
+ args: ["pkg", "set", "description=.NET Console"]
60351
+ },
60352
+ {
60353
+ action: "command",
60354
+ cmd: "npm",
60355
+ args: ["pkg", "set", "fontawesomeIcon=fab fa-windows text-blue-500"]
60837
60356
  }
60838
60357
  ]
60839
60358
  };
@@ -60843,6 +60362,7 @@ var templates3 = [
60843
60362
  ViteReact,
60844
60363
  NextJS,
60845
60364
  ExpressTS,
60365
+ ServerlessExpressTS,
60846
60366
  PHP,
60847
60367
  Laravel,
60848
60368
  PythonConsole,
@@ -60881,6 +60401,11 @@ var N8NLocal = {
60881
60401
  cmd: "npm",
60882
60402
  args: ["pkg", "set", "fontawesomeIcon=fas fa-project-diagram text-red-500"]
60883
60403
  },
60404
+ {
60405
+ action: "command",
60406
+ cmd: "npm",
60407
+ args: ["pkg", "set", "description=N8N (Local)"]
60408
+ },
60884
60409
  {
60885
60410
  action: "command",
60886
60411
  cmd: "npm",
@@ -61122,7 +60647,7 @@ networks:
61122
60647
  driver: bridge`;
61123
60648
 
61124
60649
  // ../../packages/template/services_list/aws/indexHtml.ts
61125
- var indexHtml = `<!DOCTYPE html>
60650
+ var indexHtml2 = `<!DOCTYPE html>
61126
60651
  <html lang="en" class="dark">
61127
60652
  <head>
61128
60653
  <meta charset="UTF-8">
@@ -61458,7 +60983,7 @@ spawn('docker', ['compose', 'down'], { stdio: 'inherit' });`;
61458
60983
 
61459
60984
  // ../../packages/template/services_list/aws.ts
61460
60985
  var AWSTemplate = {
61461
- name: "AWS Local",
60986
+ name: "Localstack (Experimental)",
61462
60987
  description: "AWS LocalStack Environment with Manager",
61463
60988
  notes: "Requires Docker, Node.js, and AWS CLI installed.",
61464
60989
  templating: [
@@ -61485,7 +61010,7 @@ var AWSTemplate = {
61485
61010
  {
61486
61011
  action: "file",
61487
61012
  file: "index.html",
61488
- filecontent: indexHtml
61013
+ filecontent: indexHtml2
61489
61014
  },
61490
61015
  {
61491
61016
  action: "command",
@@ -61515,7 +61040,12 @@ var AWSTemplate = {
61515
61040
  {
61516
61041
  action: "command",
61517
61042
  cmd: "npm",
61518
- args: ["pkg", "set", "fontawesomeIcon=fab fa-aws text-orange-400"]
61043
+ args: ["pkg", "set", "fontawesomeIcon=fab fa-aws text-orange-500"]
61044
+ },
61045
+ {
61046
+ action: "command",
61047
+ cmd: "npm",
61048
+ args: ["pkg", "set", "description=AWS LocalStack Environment with Manager"]
61519
61049
  },
61520
61050
  {
61521
61051
  action: "command",
@@ -61696,7 +61226,7 @@ const stripe = new Stripe('sk_test_mock_123', {
61696
61226
  })();
61697
61227
  `;
61698
61228
  var StripeTemplate = {
61699
- name: "Stripe Mock",
61229
+ name: "Stripe Mock (Experimental)",
61700
61230
  description: "Stripe API Mock Server",
61701
61231
  notes: "Runs the official stripe-mock image. Requires Docker.",
61702
61232
  templating: [
@@ -61745,6 +61275,11 @@ var StripeTemplate = {
61745
61275
  cmd: "npm",
61746
61276
  args: ["pkg", "set", "fontawesomeIcon=fas fa-credit-card text-green-500"]
61747
61277
  },
61278
+ {
61279
+ action: "command",
61280
+ cmd: "npm",
61281
+ args: ["pkg", "set", "description=Stripe Mock"]
61282
+ },
61748
61283
  {
61749
61284
  action: "command",
61750
61285
  cmd: "npm",
@@ -61771,7 +61306,7 @@ var MonorepoTemplates = {
61771
61306
  var template_default = MonorepoTemplates;
61772
61307
 
61773
61308
  // src/routes/availabletemplates.ts
61774
- var router18 = import_express21.default.Router();
61309
+ var router18 = import_express22.default.Router();
61775
61310
  router18.get("/", (req, res) => {
61776
61311
  try {
61777
61312
  const stripTemplating = (templates5) => {
@@ -61792,7 +61327,7 @@ router18.get("/", (req, res) => {
61792
61327
  var availabletemplates_default = router18;
61793
61328
 
61794
61329
  // src/routes/setworkspace/index.ts
61795
- var import_express22 = __toESM(require_express2());
61330
+ var import_express23 = __toESM(require_express2());
61796
61331
 
61797
61332
  // src/routes/setworkspace/template.ts
61798
61333
  var import_path17 = __toESM(require("path"));
@@ -61815,6 +61350,33 @@ async function writeFile(filePath, content) {
61815
61350
  await import_fs3.promises.writeFile(filePath, content, { encoding: "utf8" });
61816
61351
  }
61817
61352
  var isWindows = process.platform === "win32";
61353
+ async function findMonorepoRoot4(startDir) {
61354
+ let currentDir = startDir;
61355
+ while (true) {
61356
+ const markers = ["pnpm-workspace.yaml", "turbo.json", "lerna.json", ".git"];
61357
+ for (const marker of markers) {
61358
+ try {
61359
+ await import_fs3.promises.stat(import_path15.default.join(currentDir, marker));
61360
+ return currentDir;
61361
+ } catch {
61362
+ }
61363
+ }
61364
+ try {
61365
+ const pkgPath = import_path15.default.join(currentDir, "package.json");
61366
+ const content = await import_fs3.promises.readFile(pkgPath, "utf8");
61367
+ const pkg = JSON.parse(content);
61368
+ if (pkg.workspaces) {
61369
+ return currentDir;
61370
+ }
61371
+ } catch {
61372
+ }
61373
+ const parentDir = import_path15.default.dirname(currentDir);
61374
+ if (parentDir === currentDir) {
61375
+ return startDir;
61376
+ }
61377
+ currentDir = parentDir;
61378
+ }
61379
+ }
61818
61380
 
61819
61381
  // src/routes/setworkspace/command.ts
61820
61382
  var import_path16 = __toESM(require("path"));
@@ -61912,11 +61474,17 @@ async function executeTemplate(template, workspacePath, onProgress) {
61912
61474
  await ensureDirectory(workspacePath);
61913
61475
  const progress = onProgress || ((msg) => console.log(`[Template] ${msg}`));
61914
61476
  for (const step of template.templating) {
61915
- if (step.action === "command" && step.cmd) {
61477
+ if ((step.action === "command" || step.action === "root-command") && step.cmd) {
61916
61478
  const cmd = step.cmd;
61917
61479
  let args2 = step.args || [];
61480
+ let cwd = workspacePath;
61481
+ if (step.action === "root-command") {
61482
+ cwd = await findMonorepoRoot4(workspacePath);
61483
+ }
61484
+ let relativePath = import_path17.default.relative(cwd, workspacePath);
61485
+ if (relativePath === "") relativePath = ".";
61918
61486
  const rawFullCmd = args2.length > 0 ? `${cmd} ${args2.join(" ")}` : cmd;
61919
- const processedCmd = preprocessCommand(rawFullCmd, workspacePath);
61487
+ const processedCmd = preprocessCommand(rawFullCmd, cwd);
61920
61488
  let finalCmd = cmd;
61921
61489
  let finalArgs = args2;
61922
61490
  let useShell = false;
@@ -61924,13 +61492,16 @@ async function executeTemplate(template, workspacePath, onProgress) {
61924
61492
  finalCmd = processedCmd;
61925
61493
  finalArgs = [];
61926
61494
  useShell = true;
61495
+ finalCmd = finalCmd.replace(/\{\{RELATIVE_PATH\}\}/g, relativePath);
61927
61496
  } else {
61928
- const dirName = import_path17.default.basename(workspacePath);
61929
- finalArgs = finalArgs.map((arg) => arg.replace(/\$\(basename \$PWD\)/g, dirName));
61497
+ const dirName = import_path17.default.basename(cwd);
61498
+ finalArgs = finalArgs.map(
61499
+ (arg) => arg.replace(/\$\(basename \$PWD\)/g, dirName).replace(/\{\{RELATIVE_PATH\}\}/g, relativePath)
61500
+ );
61930
61501
  }
61931
- progress(`Running: ${finalCmd} ${finalArgs.join(" ")}`);
61502
+ progress(`Running in ${step.action === "root-command" ? "Root" : "Workspace"}: ${finalCmd} ${finalArgs.join(" ")}`);
61932
61503
  try {
61933
- const result = await runCommand2(finalCmd, finalArgs, workspacePath, useShell, (data) => {
61504
+ const result = await runCommand2(finalCmd, finalArgs, cwd, useShell, (data) => {
61934
61505
  const trimmed = data.trim();
61935
61506
  if (trimmed) progress(trimmed);
61936
61507
  });
@@ -61950,7 +61521,7 @@ ${cmdErr.message}`);
61950
61521
  }
61951
61522
 
61952
61523
  // src/routes/setworkspace/index.ts
61953
- var router19 = import_express22.default.Router();
61524
+ var router19 = import_express23.default.Router();
61954
61525
  router19.post("/", async (req, res) => {
61955
61526
  try {
61956
61527
  const { workspace, templatename } = req.body;
@@ -62005,12 +61576,12 @@ function setWorkspaceTemplateSocket(io3) {
62005
61576
  }
62006
61577
 
62007
61578
  // src/routes/stopTerminalWorkspace.ts
62008
- var import_express23 = __toESM(require_express2());
61579
+ var import_express24 = __toESM(require_express2());
62009
61580
  var import_fs_extra13 = __toESM(require_lib4());
62010
61581
  var import_path18 = __toESM(require("path"));
62011
61582
  init_execa();
62012
61583
  var import_tree_kill = __toESM(require_tree_kill());
62013
- var router20 = (0, import_express23.Router)();
61584
+ var router20 = (0, import_express24.Router)();
62014
61585
  async function killProcessTree(pid, signal = "SIGKILL") {
62015
61586
  return new Promise((resolve, reject) => {
62016
61587
  (0, import_tree_kill.default)(pid, signal, (err) => {
@@ -62185,14 +61756,14 @@ if (command === "init") {
62185
61756
  process.exit(1);
62186
61757
  });
62187
61758
  }
62188
- var app2 = (0, import_express24.default)();
61759
+ var app2 = (0, import_express25.default)();
62189
61760
  var port2 = config_default.apiPort;
62190
61761
  app2.use((0, import_cors.default)({
62191
61762
  origin: true,
62192
61763
  credentials: true
62193
61764
  }));
62194
- app2.use(import_express24.default.static("public"));
62195
- app2.use(import_express24.default.json());
61765
+ app2.use(import_express25.default.static("public"));
61766
+ app2.use(import_express25.default.json());
62196
61767
  app2.use("/", tester_default);
62197
61768
  app2.use("/" + api_default.scanWorkspace, scanworkspace_default);
62198
61769
  app2.use("/" + api_default.stopProcess, stopcmd_default);
@@ -62216,7 +61787,7 @@ app2.use("/" + api_default.docker, apidocker_default);
62216
61787
  app2.use("/" + api_default.availabletemplates, availabletemplates_default);
62217
61788
  app2.use("/" + api_default.setWorkspaceTemplate, setworkspace_default);
62218
61789
  var frontendPath = import_path19.default.join(__dirname, "../public");
62219
- app2.use(import_express24.default.static(frontendPath));
61790
+ app2.use(import_express25.default.static(frontendPath));
62220
61791
  app2.get("*", (req, res) => {
62221
61792
  res.sendFile(import_path19.default.join(frontendPath, "index.html"));
62222
61793
  });