designnn 0.2.0 → 0.4.0

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/cli.js CHANGED
@@ -2629,11 +2629,236 @@ var init_source = __esm(() => {
2629
2629
  source_default = chalk;
2630
2630
  });
2631
2631
 
2632
+ // src/utils/i18n.ts
2633
+ function setLang(lang) {
2634
+ currentLang = lang;
2635
+ }
2636
+ function detectLang() {
2637
+ const envLang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || "";
2638
+ if (envLang.startsWith("ja"))
2639
+ return "ja";
2640
+ return "en";
2641
+ }
2642
+ function t(key) {
2643
+ const msg = messages[key];
2644
+ if (!msg)
2645
+ return key;
2646
+ return msg[currentLang] || msg.en;
2647
+ }
2648
+ function getCategoryName(category) {
2649
+ const key = `cat${category.charAt(0).toUpperCase() + category.slice(1)}`;
2650
+ return t(key);
2651
+ }
2652
+ var currentLang = "en", messages;
2653
+ var init_i18n = __esm(() => {
2654
+ messages = {
2655
+ tagline: {
2656
+ en: "Trend-driven design prompt engine for Figma AI",
2657
+ ja: "Figma AI のためのトレンド駆動デザインプロンプトエンジン"
2658
+ },
2659
+ chatNoMessage: {
2660
+ en: `Please provide a design description. Example:
2661
+ designnn chat "SaaS pricing page with dark theme"`,
2662
+ ja: `デザインの説明を入力してください。例:
2663
+ designnn chat "ダークテーマのSaaS料金ページ"`
2664
+ },
2665
+ chatAnalyzing: {
2666
+ en: "Analyzing your request and generating Figma AI prompt...",
2667
+ ja: "リクエストを分析し、Figma AIプロンプトを生成中..."
2668
+ },
2669
+ chatPromptFor: {
2670
+ en: "Prompt for",
2671
+ ja: "プロンプト:"
2672
+ },
2673
+ chatApiKeyError: {
2674
+ en: `OpenAI API key not found. Set it with:
2675
+ export OPENAI_API_KEY=your-key
2676
+ or: designnn config --api-key your-key`,
2677
+ ja: `OpenAI APIキーが見つかりません。以下で設定してください:
2678
+ export OPENAI_API_KEY=your-key
2679
+ または: designnn config --api-key your-key`
2680
+ },
2681
+ chatFailed: {
2682
+ en: "Generation failed",
2683
+ ja: "生成に失敗しました"
2684
+ },
2685
+ exploreAllTrends: {
2686
+ en: "All Design Trends",
2687
+ ja: "全デザイントレンド"
2688
+ },
2689
+ exploreTrendsInDb: {
2690
+ en: "trends in database",
2691
+ ja: "件のトレンドがデータベースに登録済み"
2692
+ },
2693
+ exploreMatching: {
2694
+ en: "Trends matching",
2695
+ ja: "検索結果"
2696
+ },
2697
+ exploreTrends: {
2698
+ en: "Trends",
2699
+ ja: "トレンド"
2700
+ },
2701
+ exploreTop: {
2702
+ en: "Top",
2703
+ ja: "トップ"
2704
+ },
2705
+ exploreDesignTrends: {
2706
+ en: "Design Trends",
2707
+ ja: "デザイントレンド"
2708
+ },
2709
+ exploreNoResults: {
2710
+ en: "No trends found matching your query.",
2711
+ ja: "該当するトレンドが見つかりませんでした。"
2712
+ },
2713
+ exploreResults: {
2714
+ en: "results",
2715
+ ja: "件"
2716
+ },
2717
+ exploreInvalidCategory: {
2718
+ en: "Invalid category. Choose from",
2719
+ ja: "無効なカテゴリです。以下から選択してください"
2720
+ },
2721
+ exploreTipFilter: {
2722
+ en: "Tip: Use --category, --component, or --top to filter",
2723
+ ja: "ヒント: --category, --component, --top でフィルタリングできます"
2724
+ },
2725
+ exploreTipGenerate: {
2726
+ en: "Tip: Use --generate <trend-id> to create a prompt from a trend",
2727
+ ja: "ヒント: --generate <trend-id> でトレンドからプロンプトを生成できます"
2728
+ },
2729
+ exploreGenerating: {
2730
+ en: "Generating Figma AI prompt for",
2731
+ ja: "Figma AIプロンプトを生成中:"
2732
+ },
2733
+ exploreTrendNotFound: {
2734
+ en: "Trend not found",
2735
+ ja: "トレンドが見つかりません"
2736
+ },
2737
+ exploreAvailableIds: {
2738
+ en: "Available IDs",
2739
+ ja: "利用可能なID"
2740
+ },
2741
+ mixNoTrends: {
2742
+ en: `Please provide two trend names to mix. Example:
2743
+ designnn mix "Bento UI" "Glassmorphism"
2744
+ designnn mix bento-ui glassmorphism`,
2745
+ ja: `ミックスする2つのトレンド名を入力してください。例:
2746
+ designnn mix "Bento UI" "Glassmorphism"
2747
+ designnn mix bento-ui glassmorphism`
2748
+ },
2749
+ mixTrendNotFound: {
2750
+ en: "Trend not found",
2751
+ ja: "トレンドが見つかりません"
2752
+ },
2753
+ mixMixing: {
2754
+ en: "Mixing Trends",
2755
+ ja: "トレンドをミックス中"
2756
+ },
2757
+ mixTrendA: {
2758
+ en: "Trend A:",
2759
+ ja: "トレンドA:"
2760
+ },
2761
+ mixTrendB: {
2762
+ en: "Trend B:",
2763
+ ja: "トレンドB:"
2764
+ },
2765
+ mixContext: {
2766
+ en: "Context",
2767
+ ja: "コンテキスト"
2768
+ },
2769
+ mixGenerating: {
2770
+ en: "Mixing into a Figma AI prompt...",
2771
+ ja: "Figma AIプロンプトにミックス中..."
2772
+ },
2773
+ mixResult: {
2774
+ en: "Mix",
2775
+ ja: "ミックス"
2776
+ },
2777
+ mixDidYouMean: {
2778
+ en: "Did you mean:",
2779
+ ja: "もしかして:"
2780
+ },
2781
+ mixAvailableTrends: {
2782
+ en: "Available trends:",
2783
+ ja: "利用可能なトレンド:"
2784
+ },
2785
+ mixSeeAll: {
2786
+ en: "Use 'designnn explore' to see all trends.",
2787
+ ja: "'designnn explore' で全トレンドを表示できます。"
2788
+ },
2789
+ statsTitle: {
2790
+ en: "Trend Database Statistics",
2791
+ ja: "トレンドデータベース統計"
2792
+ },
2793
+ statsOverview: {
2794
+ en: "Overview",
2795
+ ja: "概要"
2796
+ },
2797
+ statsTotalTrends: {
2798
+ en: "Total Trends",
2799
+ ja: "総トレンド数"
2800
+ },
2801
+ statsBuiltin: {
2802
+ en: "Built-in",
2803
+ ja: "ビルトイン"
2804
+ },
2805
+ statsAiGenerated: {
2806
+ en: "AI-Generated",
2807
+ ja: "AI生成"
2808
+ },
2809
+ statsUserAdded: {
2810
+ en: "User-Added",
2811
+ ja: "ユーザー追加"
2812
+ },
2813
+ statsByCategory: {
2814
+ en: "By Category",
2815
+ ja: "カテゴリ別"
2816
+ },
2817
+ statsTop5: {
2818
+ en: "Top 5 Trends",
2819
+ ja: "トップ5トレンド"
2820
+ },
2821
+ statsRecentlyAdded: {
2822
+ en: "Recently Added",
2823
+ ja: "最近追加されたトレンド"
2824
+ },
2825
+ statsTipUpdate: {
2826
+ en: "Tip: Run 'designnn update' to research and add new trends",
2827
+ ja: "ヒント: 'designnn update' で新しいトレンドをリサーチ・追加できます"
2828
+ },
2829
+ statsTipReset: {
2830
+ en: "Tip: Run 'designnn update --reset' to clear custom trends",
2831
+ ja: "ヒント: 'designnn update --reset' でカスタムトレンドをリセットできます"
2832
+ },
2833
+ generatedPrompt: {
2834
+ en: "Generated Prompt",
2835
+ ja: "生成されたプロンプト"
2836
+ },
2837
+ copyHint: {
2838
+ en: "Copy the prompt above and paste it into Figma AI (Ctrl+I)",
2839
+ ja: "上のプロンプトをコピーしてFigma AI(Ctrl+I)に貼り付けてください"
2840
+ },
2841
+ keywords: {
2842
+ en: "Keywords",
2843
+ ja: "キーワード"
2844
+ },
2845
+ serveStarting: {
2846
+ en: "DESIGNNN Web UI running at",
2847
+ ja: "DESIGNNN Web UI 起動中:"
2848
+ },
2849
+ catStyle: { en: "style", ja: "スタイル" },
2850
+ catComponent: { en: "component", ja: "コンポーネント" },
2851
+ catPattern: { en: "pattern", ja: "パターン" },
2852
+ catLayout: { en: "layout", ja: "レイアウト" },
2853
+ catInteraction: { en: "interaction", ja: "インタラクション" }
2854
+ };
2855
+ });
2856
+
2632
2857
  // src/utils/display.ts
2633
2858
  function printBanner() {
2634
2859
  console.log("");
2635
2860
  console.log(` ${BRAND.name} ${BRAND.version}`);
2636
- console.log(` ${BRAND.tagline}`);
2861
+ console.log(` ${BRAND.tagline()}`);
2637
2862
  console.log("");
2638
2863
  }
2639
2864
  function printTrend(trend, index) {
@@ -2641,12 +2866,12 @@ function printTrend(trend, index) {
2641
2866
  const pop = getPopularityBar(trend.popularity);
2642
2867
  console.log(` ${prefix} ${source_default.bold(trend.name)} ${source_default.gray(`[${trend.category}]`)} ${pop}`);
2643
2868
  console.log(` ${source_default.dim(trend.description)}`);
2644
- console.log(` ${source_default.gray("Keywords:")} ${trend.keywords.map((k) => source_default.cyan(k)).join(", ")}`);
2869
+ console.log(` ${source_default.gray(t("keywords") + ":")} ${trend.keywords.map((k) => source_default.cyan(k)).join(", ")}`);
2645
2870
  console.log("");
2646
2871
  }
2647
2872
  function printPrompt(prompt, label) {
2648
2873
  console.log("");
2649
- console.log(source_default.hex("#CCFF00").bold(` ─── ${label || "Generated Prompt"} ───`));
2874
+ console.log(source_default.hex("#CCFF00").bold(` ─── ${label || t("generatedPrompt")} ───`));
2650
2875
  console.log("");
2651
2876
  console.log(source_default.white(` ${prompt.split(`
2652
2877
  `).join(`
@@ -2654,7 +2879,7 @@ function printPrompt(prompt, label) {
2654
2879
  console.log("");
2655
2880
  console.log(source_default.hex("#CCFF00")(" ─────────────────────────────────"));
2656
2881
  console.log("");
2657
- console.log(source_default.dim(" \uD83D\uDCCB Copy the prompt above and paste it into Figma AI (Ctrl+I)"));
2882
+ console.log(source_default.dim(` \uD83D\uDCCB ${t("copyHint")}`));
2658
2883
  console.log("");
2659
2884
  }
2660
2885
  function printError(message) {
@@ -2673,10 +2898,11 @@ function printSection(title) {
2673
2898
  var BRAND;
2674
2899
  var init_display = __esm(() => {
2675
2900
  init_source();
2901
+ init_i18n();
2676
2902
  BRAND = {
2677
2903
  name: source_default.bold.hex("#CCFF00")("DESIGNNN"),
2678
- tagline: source_default.gray("Trend-driven design prompt engine for Figma AI"),
2679
- version: source_default.gray("v0.1.0")
2904
+ tagline: () => source_default.gray(t("tagline")),
2905
+ version: source_default.gray("v0.4.0")
2680
2906
  };
2681
2907
  });
2682
2908
 
@@ -7293,7 +7519,7 @@ function hasAutoParseableInput(params) {
7293
7519
  if (isAutoParsableResponseFormat(params.response_format)) {
7294
7520
  return true;
7295
7521
  }
7296
- return params.tools?.some((t) => isAutoParsableTool(t) || t.type === "function" && t.function.strict === true) ?? false;
7522
+ return params.tools?.some((t2) => isAutoParsableTool(t2) || t2.type === "function" && t2.function.strict === true) ?? false;
7297
7523
  }
7298
7524
  function assertToolCallsAreChatCompletionFunctionToolCalls(toolCalls) {
7299
7525
  for (const toolCall of toolCalls || []) {
@@ -7609,15 +7835,15 @@ var init_AbstractChatCompletionRunner = __esm(() => {
7609
7835
  functionsByName[f.function.name || f.function.function.name] = f.function;
7610
7836
  }
7611
7837
  }
7612
- const tools = "tools" in params ? inputTools.map((t) => t.type === "function" ? {
7838
+ const tools = "tools" in params ? inputTools.map((t2) => t2.type === "function" ? {
7613
7839
  type: "function",
7614
7840
  function: {
7615
- name: t.function.name || t.function.function.name,
7616
- parameters: t.function.parameters,
7617
- description: t.function.description,
7618
- strict: t.function.strict
7841
+ name: t2.function.name || t2.function.function.name,
7842
+ parameters: t2.function.parameters,
7843
+ description: t2.function.description,
7844
+ strict: t2.function.strict
7619
7845
  }
7620
- } : t) : undefined;
7846
+ } : t2) : undefined;
7621
7847
  for (const message of params.messages) {
7622
7848
  this._addMessage(message, false);
7623
7849
  }
@@ -12012,7 +12238,7 @@ function saveCustomTrends(trends) {
12012
12238
  }
12013
12239
  function addCustomTrend(trend) {
12014
12240
  const customs = loadCustomTrends();
12015
- const idx = customs.findIndex((t) => t.id === trend.id);
12241
+ const idx = customs.findIndex((t2) => t2.id === trend.id);
12016
12242
  if (idx >= 0) {
12017
12243
  customs[idx] = trend;
12018
12244
  } else {
@@ -12025,13 +12251,13 @@ function getAllTrends() {
12025
12251
  }
12026
12252
  function searchTrends(query2) {
12027
12253
  const q = query2.toLowerCase();
12028
- return getAllTrends().filter((t) => t.name.toLowerCase().includes(q) || t.description.toLowerCase().includes(q) || t.keywords.some((k) => k.includes(q)) || t.category.includes(q)).sort((a, b) => b.popularity - a.popularity);
12254
+ return getAllTrends().filter((t2) => t2.name.toLowerCase().includes(q) || t2.description.toLowerCase().includes(q) || t2.keywords.some((k) => k.includes(q)) || t2.category.includes(q)).sort((a, b) => b.popularity - a.popularity);
12029
12255
  }
12030
12256
  function getTrendById(id) {
12031
- return getAllTrends().find((t) => t.id === id);
12257
+ return getAllTrends().find((t2) => t2.id === id);
12032
12258
  }
12033
12259
  function getTrendsByCategory(category) {
12034
- return getAllTrends().filter((t) => t.category === category).sort((a, b) => b.popularity - a.popularity);
12260
+ return getAllTrends().filter((t2) => t2.category === category).sort((a, b) => b.popularity - a.popularity);
12035
12261
  }
12036
12262
  function getTopTrends(limit2 = 10) {
12037
12263
  return getAllTrends().sort((a, b) => b.popularity - a.popularity).slice(0, limit2);
@@ -12039,8 +12265,8 @@ function getTopTrends(limit2 = 10) {
12039
12265
  function getTrendStats() {
12040
12266
  const all = getAllTrends();
12041
12267
  const categories = {};
12042
- for (const t of all) {
12043
- categories[t.category] = (categories[t.category] || 0) + 1;
12268
+ for (const t2 of all) {
12269
+ categories[t2.category] = (categories[t2.category] || 0) + 1;
12044
12270
  }
12045
12271
  return {
12046
12272
  total: all.length,
@@ -12754,6 +12980,246 @@ var init_trends = __esm(() => {
12754
12980
  ],
12755
12981
  source: "builtin"
12756
12982
  },
12983
+ {
12984
+ id: "3d-immersive",
12985
+ name: "3D Immersive Elements",
12986
+ category: "style",
12987
+ description: "WebGL-powered 3D objects, scroll-triggered 3D animations, and AR previews. Depth and interaction move beyond static images.",
12988
+ keywords: ["3d", "webgl", "immersive", "ar", "depth"],
12989
+ popularity: 89,
12990
+ year: 2026,
12991
+ figmaPromptHints: [
12992
+ "Include 3D product renders or interactive model placeholders",
12993
+ "Use layered depth with parallax scrolling effects",
12994
+ "Add perspective transforms on card hover",
12995
+ "Design AR preview button with camera icon"
12996
+ ],
12997
+ source: "builtin"
12998
+ },
12999
+ {
13000
+ id: "experimental-navigation",
13001
+ name: "Experimental Navigation",
13002
+ category: "interaction",
13003
+ description: "Non-linear navigation patterns: radial menus, hidden drawers, interactive maps, and exploration-based journeys.",
13004
+ keywords: ["navigation", "radial", "experimental", "nonlinear", "exploration"],
13005
+ popularity: 76,
13006
+ year: 2026,
13007
+ figmaPromptHints: [
13008
+ "Design a radial or circular navigation menu",
13009
+ "Use hidden drawer navigation revealed by gesture",
13010
+ "Create an interactive map-based navigation",
13011
+ "Include breadcrumb trail for nonlinear journeys"
13012
+ ],
13013
+ source: "builtin"
13014
+ },
13015
+ {
13016
+ id: "y2k-vibrant-palette",
13017
+ name: "Y2K Vibrant Palette",
13018
+ category: "style",
13019
+ description: "Bright, saturated color palettes inspired by Y2K nostalgia. Neon gradients, high-contrast pairings, and dopamine design aesthetics.",
13020
+ keywords: ["y2k", "vibrant", "neon", "dopamine", "nostalgia"],
13021
+ popularity: 83,
13022
+ year: 2026,
13023
+ figmaPromptHints: [
13024
+ "Use saturated neon colors: hot pink, electric blue, lime green",
13025
+ "Apply bold gradient combinations across backgrounds",
13026
+ "Include Y2K-inspired decorative elements: stars, bubbles, chrome",
13027
+ "Use high-contrast color pairings for maximum visual impact"
13028
+ ],
13029
+ source: "builtin"
13030
+ },
13031
+ {
13032
+ id: "scrollytelling",
13033
+ name: "Scrollytelling",
13034
+ category: "interaction",
13035
+ description: "Scroll-based narrative experiences where content unfolds as the user scrolls. Combines motion, text, and visuals into a story.",
13036
+ keywords: ["scroll", "storytelling", "narrative", "motion", "immersive"],
13037
+ popularity: 84,
13038
+ year: 2026,
13039
+ figmaPromptHints: [
13040
+ "Design a vertical narrative with scroll-triggered content reveals",
13041
+ "Use full-screen sections that transition on scroll",
13042
+ "Include progress indicator showing story position",
13043
+ "Combine text, images, and data visualizations in sequence"
13044
+ ],
13045
+ source: "builtin"
13046
+ },
13047
+ {
13048
+ id: "gamified-design",
13049
+ name: "Gamified Design",
13050
+ category: "pattern",
13051
+ description: "Game mechanics applied to UI: points, levels, badges, progress bars, leaderboards, and micro-rewards to boost engagement.",
13052
+ keywords: ["gamification", "badges", "points", "leaderboard", "rewards"],
13053
+ popularity: 80,
13054
+ year: 2026,
13055
+ figmaPromptHints: [
13056
+ "Include progress bars and level indicators",
13057
+ "Design achievement badges with unlock animations",
13058
+ "Add streak counters and daily challenge cards",
13059
+ "Create a leaderboard component with rank, avatar, and score"
13060
+ ],
13061
+ source: "builtin"
13062
+ },
13063
+ {
13064
+ id: "retrofuturism",
13065
+ name: "Retrofuturism",
13066
+ category: "style",
13067
+ description: "Vintage visions of the future: neon accents, chrome textures, pixel art, bold gradients inspired by sci-fi and arcade aesthetics.",
13068
+ keywords: ["retro", "futurism", "neon", "chrome", "sci-fi"],
13069
+ popularity: 72,
13070
+ year: 2026,
13071
+ figmaPromptHints: [
13072
+ "Use neon glow effects on text and borders",
13073
+ "Apply chrome/metallic gradient textures",
13074
+ "Include retro-futuristic typography with scan lines",
13075
+ "Combine dark backgrounds with vibrant neon accents"
13076
+ ],
13077
+ source: "builtin"
13078
+ },
13079
+ {
13080
+ id: "collage-design",
13081
+ name: "Collage Design",
13082
+ category: "style",
13083
+ description: "Scrapbook-style creativity with sticker graphics, torn textures, cutout photos, and hand-drawn elements. Messy on purpose.",
13084
+ keywords: ["collage", "scrapbook", "cutout", "handmade", "texture"],
13085
+ popularity: 68,
13086
+ year: 2026,
13087
+ figmaPromptHints: [
13088
+ "Layer cutout photos with torn paper edges",
13089
+ "Add sticker-like decorative elements",
13090
+ "Use hand-drawn fonts and doodle illustrations",
13091
+ "Mix textures: paper, tape, stamps, ink splashes"
13092
+ ],
13093
+ source: "builtin"
13094
+ },
13095
+ {
13096
+ id: "sustainable-web",
13097
+ name: "Sustainable Web Design",
13098
+ category: "pattern",
13099
+ description: "Eco-conscious design: optimized assets, reduced data transfer, dark themes for energy saving, and accessibility-first approach.",
13100
+ keywords: ["sustainable", "eco", "green", "accessible", "performance"],
13101
+ popularity: 71,
13102
+ year: 2026,
13103
+ figmaPromptHints: [
13104
+ "Use system fonts and minimal custom assets",
13105
+ "Design with dark mode default for OLED energy saving",
13106
+ "Include accessibility indicators: contrast ratios, focus states",
13107
+ "Minimize decorative elements, maximize content clarity"
13108
+ ],
13109
+ source: "builtin"
13110
+ },
13111
+ {
13112
+ id: "voice-ui",
13113
+ name: "Voice UI Interface",
13114
+ category: "component",
13115
+ description: "Voice-activated interface elements: waveform visualizers, voice command indicators, and conversational voice assistants.",
13116
+ keywords: ["voice", "speech", "audio", "waveform", "assistant"],
13117
+ popularity: 74,
13118
+ year: 2026,
13119
+ figmaPromptHints: [
13120
+ "Design a voice input button with pulsing animation",
13121
+ "Include audio waveform visualizer during speech",
13122
+ "Show voice command suggestions as floating chips",
13123
+ "Add visual feedback states: listening, processing, responding"
13124
+ ],
13125
+ source: "builtin"
13126
+ },
13127
+ {
13128
+ id: "agentic-ai-ui",
13129
+ name: "Agentic AI Interface",
13130
+ category: "pattern",
13131
+ description: "UI for autonomous AI agents: task delegation, progress monitoring, approval workflows, and multi-step agent pipelines.",
13132
+ keywords: ["agent", "ai", "autonomous", "workflow", "pipeline"],
13133
+ popularity: 92,
13134
+ year: 2026,
13135
+ figmaPromptHints: [
13136
+ "Design a task pipeline view with agent status indicators",
13137
+ "Include approval/rejection buttons for agent actions",
13138
+ "Show real-time progress with step-by-step breakdown",
13139
+ "Add confidence scores and decision explanations"
13140
+ ],
13141
+ source: "builtin"
13142
+ },
13143
+ {
13144
+ id: "sensory-maximalism",
13145
+ name: "Sensory Maximalism",
13146
+ category: "style",
13147
+ description: "Multi-sensory design that engages all senses: rich textures, bold colors, dynamic motion, and immersive high-energy compositions.",
13148
+ keywords: ["sensory", "maximalism", "immersive", "texture", "energy"],
13149
+ popularity: 75,
13150
+ year: 2026,
13151
+ figmaPromptHints: [
13152
+ "Layer multiple textures: gradients, grain, patterns",
13153
+ "Use bold, clashing color combinations intentionally",
13154
+ "Include dynamic motion indicators and animated elements",
13155
+ "Create visual density with overlapping elements"
13156
+ ],
13157
+ source: "builtin"
13158
+ },
13159
+ {
13160
+ id: "spatial-design",
13161
+ name: "Spatial Design (visionOS)",
13162
+ category: "layout",
13163
+ description: "Design for spatial computing: floating windows, depth layers, glass materials, and eye-tracking interactions for Apple Vision Pro.",
13164
+ keywords: ["spatial", "visionos", "ar", "vr", "floating"],
13165
+ popularity: 78,
13166
+ year: 2026,
13167
+ figmaPromptHints: [
13168
+ "Design floating window panels with glass material",
13169
+ "Use depth layers with z-axis spacing between elements",
13170
+ "Apply frosted glass with high blur and transparency",
13171
+ "Include gaze-based hover states and hand gesture indicators"
13172
+ ],
13173
+ source: "builtin"
13174
+ },
13175
+ {
13176
+ id: "variable-fonts",
13177
+ name: "Variable Font Typography",
13178
+ category: "style",
13179
+ description: "Dynamic typography using variable fonts that respond to interaction, scroll position, or data. Fluid weight and width transitions.",
13180
+ keywords: ["variable-font", "typography", "fluid", "responsive", "dynamic"],
13181
+ popularity: 77,
13182
+ year: 2026,
13183
+ figmaPromptHints: [
13184
+ "Use variable fonts with dramatic weight changes (100-900)",
13185
+ "Apply fluid font sizes that scale with viewport",
13186
+ "Design text that changes weight on hover or scroll",
13187
+ "Combine ultra-thin and ultra-bold weights in same layout"
13188
+ ],
13189
+ source: "builtin"
13190
+ },
13191
+ {
13192
+ id: "tactile-ui",
13193
+ name: "Tactile / Squishy UI",
13194
+ category: "interaction",
13195
+ description: "UI elements that feel physically responsive: bouncy animations, elastic deformations, and pressure-sensitive interactions.",
13196
+ keywords: ["tactile", "squishy", "bounce", "elastic", "physical"],
13197
+ popularity: 73,
13198
+ year: 2026,
13199
+ figmaPromptHints: [
13200
+ "Design buttons with spring-bounce press animation",
13201
+ "Use elastic deformation on drag interactions",
13202
+ "Include rubber-band scroll overscroll effects",
13203
+ "Apply soft, inflated appearance with subtle inner shadows"
13204
+ ],
13205
+ source: "builtin"
13206
+ },
13207
+ {
13208
+ id: "ai-design-system",
13209
+ name: "AI-Generated Design System",
13210
+ category: "pattern",
13211
+ description: "Design systems that adapt and generate components dynamically using AI. Self-evolving tokens, auto-generated variants, and smart theming.",
13212
+ keywords: ["design-system", "ai", "tokens", "auto-generate", "adaptive"],
13213
+ popularity: 86,
13214
+ year: 2026,
13215
+ figmaPromptHints: [
13216
+ "Design a token-based system with color, spacing, and type scales",
13217
+ "Include auto-generated component variants grid",
13218
+ "Show theme switching with AI-suggested palettes",
13219
+ "Add component documentation with usage guidelines"
13220
+ ],
13221
+ source: "builtin"
13222
+ },
12757
13223
  {
12758
13224
  id: "micro-interactions",
12759
13225
  name: "Micro-interactions",
@@ -12891,7 +13357,7 @@ async function updateCommand(options) {
12891
13357
  const stats = getTrendStats();
12892
13358
  console.log(source_default.dim(` Current DB: ${stats.total} trends (${stats.builtin} built-in, ${stats.custom} custom)
12893
13359
  `));
12894
- const existingNames = getAllTrends().map((t) => t.name.toLowerCase());
13360
+ const existingNames = getAllTrends().map((t2) => t2.name.toLowerCase());
12895
13361
  const spinner = ora({
12896
13362
  text: `Researching ${count} new design trends${category ? ` in "${category}"` : ""}...`,
12897
13363
  color: "yellow"
@@ -13028,15 +13494,15 @@ __export(exports_stats, {
13028
13494
  async function statsCommand() {
13029
13495
  const stats = getTrendStats();
13030
13496
  const customs = loadCustomTrends();
13031
- printSection("Trend Database Statistics");
13032
- console.log(source_default.bold(" Overview"));
13497
+ printSection(t("statsTitle"));
13498
+ console.log(source_default.bold(` ${t("statsOverview")}`));
13033
13499
  console.log(source_default.dim(" ─────────────────────────────────"));
13034
- console.log(` Total Trends: ${source_default.hex("#CCFF00").bold(String(stats.total))}`);
13035
- console.log(` Built-in: ${source_default.white(String(stats.builtin))}`);
13036
- console.log(` AI-Generated: ${source_default.cyan(String(customs.filter((t) => t.source === "ai-generated").length))}`);
13037
- console.log(` User-Added: ${source_default.magenta(String(customs.filter((t) => t.source === "user").length))}`);
13500
+ console.log(` ${t("statsTotalTrends")}: ${source_default.hex("#CCFF00").bold(String(stats.total))}`);
13501
+ console.log(` ${t("statsBuiltin")}: ${source_default.white(String(stats.builtin))}`);
13502
+ console.log(` ${t("statsAiGenerated")}: ${source_default.cyan(String(customs.filter((t2) => t2.source === "ai-generated").length))}`);
13503
+ console.log(` ${t("statsUserAdded")}: ${source_default.magenta(String(customs.filter((t2) => t2.source === "user").length))}`);
13038
13504
  console.log("");
13039
- console.log(source_default.bold(" By Category"));
13505
+ console.log(source_default.bold(` ${t("statsByCategory")}`));
13040
13506
  console.log(source_default.dim(" ─────────────────────────────────"));
13041
13507
  const categoryEmojis = {
13042
13508
  style: "◆",
@@ -13047,38 +13513,40 @@ async function statsCommand() {
13047
13513
  };
13048
13514
  for (const [cat, count] of Object.entries(stats.categories).sort((a, b) => b[1] - a[1])) {
13049
13515
  const bar = "█".repeat(Math.round(count / 2)) + source_default.dim("░".repeat(Math.max(0, 15 - Math.round(count / 2))));
13050
- console.log(` ${categoryEmojis[cat] || "○"} ${source_default.bold(cat.padEnd(14))} ${bar} ${source_default.hex("#CCFF00")(String(count))}`);
13516
+ const label = getCategoryName(cat);
13517
+ console.log(` ${categoryEmojis[cat] || "○"} ${source_default.bold(label.padEnd(14))} ${bar} ${source_default.hex("#CCFF00")(String(count))}`);
13051
13518
  }
13052
13519
  console.log("");
13053
13520
  const allTrends = [...BUILTIN_TRENDS, ...customs].sort((a, b) => b.popularity - a.popularity);
13054
- console.log(source_default.bold(" Top 5 Trends"));
13521
+ console.log(source_default.bold(` ${t("statsTop5")}`));
13055
13522
  console.log(source_default.dim(" ─────────────────────────────────"));
13056
- for (const t of allTrends.slice(0, 5)) {
13057
- const popBar = source_default.hex("#CCFF00")("█".repeat(Math.round(t.popularity / 5)));
13058
- console.log(` ${popBar} ${source_default.bold(t.name)} ${source_default.dim(`${t.popularity}%`)}`);
13523
+ for (const trend of allTrends.slice(0, 5)) {
13524
+ const popBar = source_default.hex("#CCFF00")("█".repeat(Math.round(trend.popularity / 5)));
13525
+ console.log(` ${popBar} ${source_default.bold(trend.name)} ${source_default.dim(`${trend.popularity}%`)}`);
13059
13526
  }
13060
13527
  console.log("");
13061
13528
  if (customs.length > 0) {
13062
- const recent = customs.filter((t) => t.addedAt).sort((a, b) => new Date(b.addedAt).getTime() - new Date(a.addedAt).getTime()).slice(0, 5);
13529
+ const recent = customs.filter((trend) => trend.addedAt).sort((a, b) => new Date(b.addedAt).getTime() - new Date(a.addedAt).getTime()).slice(0, 5);
13063
13530
  if (recent.length > 0) {
13064
- console.log(source_default.bold(" Recently Added"));
13531
+ console.log(source_default.bold(` ${t("statsRecentlyAdded")}`));
13065
13532
  console.log(source_default.dim(" ─────────────────────────────────"));
13066
- for (const t of recent) {
13067
- const date = new Date(t.addedAt).toLocaleDateString();
13068
- const source = t.source === "ai-generated" ? source_default.cyan("AI") : source_default.magenta("User");
13069
- console.log(` ${source} ${source_default.bold(t.name)} ${source_default.dim(`[${t.category}] ${date}`)}`);
13533
+ for (const trend of recent) {
13534
+ const date = new Date(trend.addedAt).toLocaleDateString();
13535
+ const source = trend.source === "ai-generated" ? source_default.cyan("AI") : source_default.magenta("User");
13536
+ console.log(` ${source} ${source_default.bold(trend.name)} ${source_default.dim(`[${trend.category}] ${date}`)}`);
13070
13537
  }
13071
13538
  console.log("");
13072
13539
  }
13073
13540
  }
13074
- console.log(source_default.dim(" Tip: Run 'designnn update' to research and add new trends"));
13075
- console.log(source_default.dim(` Tip: Run 'designnn update --reset' to clear custom trends
13541
+ console.log(source_default.dim(` ${t("statsTipUpdate")}`));
13542
+ console.log(source_default.dim(` ${t("statsTipReset")}
13076
13543
  `));
13077
13544
  }
13078
13545
  var init_stats = __esm(() => {
13079
13546
  init_source();
13080
13547
  init_trends();
13081
13548
  init_display();
13549
+ init_i18n();
13082
13550
  });
13083
13551
 
13084
13552
  // node_modules/ms/index.js
@@ -36351,10 +36819,14 @@ __export(exports_server, {
36351
36819
  });
36352
36820
  import path3 from "path";
36353
36821
  import { fileURLToPath } from "url";
36354
- function createServer(port = 3333) {
36822
+ async function createServer(port = 3333) {
36355
36823
  const app = import_express.default();
36356
36824
  app.use(import_express.default.json());
36357
- app.use(import_express.default.static(path3.join(__dirname2, "../../public")));
36825
+ const publicDir = path3.join(__dirname2, "../../public");
36826
+ const publicDirAlt = path3.join(__dirname2, "../public");
36827
+ const fs_check = await import("fs");
36828
+ const resolvedPublic = fs_check.existsSync(publicDir) ? publicDir : publicDirAlt;
36829
+ app.use(import_express.default.static(resolvedPublic));
36358
36830
  app.get("/api/trends", (req, res) => {
36359
36831
  const { category, search, top } = req.query;
36360
36832
  let results;
@@ -36378,7 +36850,7 @@ function createServer(port = 3333) {
36378
36850
  res.json({ trend });
36379
36851
  });
36380
36852
  app.get("/api/categories", (_req, res) => {
36381
- const categories = [...new Set(getAllTrends().map((t) => t.category))];
36853
+ const categories = [...new Set(getAllTrends().map((t2) => t2.category))];
36382
36854
  res.json({ categories });
36383
36855
  });
36384
36856
  app.get("/api/stats", (_req, res) => {
@@ -36440,12 +36912,12 @@ function createServer(port = 3333) {
36440
36912
  }
36441
36913
  });
36442
36914
  app.get("/{*splat}", (_req, res) => {
36443
- res.sendFile(path3.join(__dirname2, "../../public/index.html"));
36915
+ res.sendFile(path3.join(resolvedPublic, "index.html"));
36444
36916
  });
36445
36917
  return app;
36446
36918
  }
36447
- function startWebServer(port = 3333) {
36448
- const app = createServer(port);
36919
+ async function startWebServer(port = 3333) {
36920
+ const app = await createServer(port);
36449
36921
  app.listen(port, () => {
36450
36922
  const stats = getTrendStats();
36451
36923
  console.log("");
@@ -36469,8 +36941,8 @@ var init_server = __esm(() => {
36469
36941
 
36470
36942
  // node_modules/zod/v3/helpers/util.js
36471
36943
  var util, objectUtil, ZodParsedType, getParsedType = (data) => {
36472
- const t = typeof data;
36473
- switch (t) {
36944
+ const t2 = typeof data;
36945
+ switch (t2) {
36474
36946
  case "undefined":
36475
36947
  return ZodParsedType.undefined;
36476
36948
  case "string":
@@ -40877,8 +41349,8 @@ function getLengthableOrigin(input) {
40877
41349
  return "unknown";
40878
41350
  }
40879
41351
  function parsedType(data) {
40880
- const t = typeof data;
40881
- switch (t) {
41352
+ const t2 = typeof data;
41353
+ switch (t2) {
40882
41354
  case "number": {
40883
41355
  return Number.isNaN(data) ? "nan" : "number";
40884
41356
  }
@@ -40895,7 +41367,7 @@ function parsedType(data) {
40895
41367
  }
40896
41368
  }
40897
41369
  }
40898
- return t;
41370
+ return t2;
40899
41371
  }
40900
41372
  function issue(...args) {
40901
41373
  const [iss, input, inst] = args;
@@ -40956,8 +41428,8 @@ class Class {
40956
41428
  constructor(..._args) {}
40957
41429
  }
40958
41430
  var EVALUATING, captureStackTrace, allowsEval, getParsedType2 = (data) => {
40959
- const t = typeof data;
40960
- switch (t) {
41431
+ const t2 = typeof data;
41432
+ switch (t2) {
40961
41433
  case "undefined":
40962
41434
  return "undefined";
40963
41435
  case "string":
@@ -40996,7 +41468,7 @@ var EVALUATING, captureStackTrace, allowsEval, getParsedType2 = (data) => {
40996
41468
  }
40997
41469
  return "object";
40998
41470
  default:
40999
- throw new Error(`Unknown data type: ${t}`);
41471
+ throw new Error(`Unknown data type: ${t2}`);
41000
41472
  }
41001
41473
  }, propertyKeyTypes, primitiveTypes, NUMBER_FORMAT_RANGES, BIGINT_FORMAT_RANGES;
41002
41474
  var init_util2 = __esm(() => {
@@ -42090,12 +42562,12 @@ function handleCatchall(proms, input, payload, ctx, def, inst) {
42090
42562
  const unrecognized = [];
42091
42563
  const keySet = def.keySet;
42092
42564
  const _catchall = def.catchall._zod;
42093
- const t = _catchall.def.type;
42565
+ const t2 = _catchall.def.type;
42094
42566
  const isOptionalOut = _catchall.optout === "optional";
42095
42567
  for (const key in input) {
42096
42568
  if (keySet.has(key))
42097
42569
  continue;
42098
- if (t === "never") {
42570
+ if (t2 === "never") {
42099
42571
  unrecognized.push(key);
42100
42572
  continue;
42101
42573
  }
@@ -45792,16 +46264,16 @@ var error18 = () => {
45792
46264
  set: { unit: "פריטים", shortLabel: "קטן", longLabel: "גדול" },
45793
46265
  number: { unit: "", shortLabel: "קטן", longLabel: "גדול" }
45794
46266
  };
45795
- const typeEntry = (t) => t ? TypeNames[t] : undefined;
45796
- const typeLabel = (t) => {
45797
- const e = typeEntry(t);
46267
+ const typeEntry = (t2) => t2 ? TypeNames[t2] : undefined;
46268
+ const typeLabel = (t2) => {
46269
+ const e = typeEntry(t2);
45798
46270
  if (e)
45799
46271
  return e.label;
45800
- return t ?? TypeNames.unknown.label;
46272
+ return t2 ?? TypeNames.unknown.label;
45801
46273
  };
45802
- const withDefinite = (t) => `ה${typeLabel(t)}`;
45803
- const verbFor = (t) => {
45804
- const e = typeEntry(t);
46274
+ const withDefinite = (t2) => `ה${typeLabel(t2)}`;
46275
+ const verbFor = (t2) => {
46276
+ const e = typeEntry(t2);
45805
46277
  const gender = e?.gender ?? "m";
45806
46278
  return gender === "f" ? "צריכה להיות" : "צריך להיות";
45807
46279
  };
@@ -53702,8 +54174,8 @@ function convertBaseSchema(schema, ctx) {
53702
54174
  }
53703
54175
  const type = schema.type;
53704
54176
  if (Array.isArray(type)) {
53705
- const typeSchemas = type.map((t) => {
53706
- const typeSchema = { ...schema, type: t };
54177
+ const typeSchemas = type.map((t2) => {
54178
+ const typeSchema = { ...schema, type: t2 };
53707
54179
  return convertBaseSchema(typeSchema, ctx);
53708
54180
  });
53709
54181
  if (typeSchemas.length === 0) {
@@ -56613,12 +57085,12 @@ var init_esm = __esm(() => {
56613
57085
  });
56614
57086
 
56615
57087
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
56616
- function mapMiniTarget(t) {
56617
- if (!t)
57088
+ function mapMiniTarget(t2) {
57089
+ if (!t2)
56618
57090
  return "draft-7";
56619
- if (t === "jsonSchema7" || t === "draft-7")
57091
+ if (t2 === "jsonSchema7" || t2 === "draft-7")
56620
57092
  return "draft-7";
56621
- if (t === "jsonSchema2019-09" || t === "draft-2020-12")
57093
+ if (t2 === "jsonSchema2019-09" || t2 === "draft-2020-12")
56622
57094
  return "draft-2020-12";
56623
57095
  return "draft-7";
56624
57096
  }
@@ -57371,8 +57843,8 @@ class Protocol {
57371
57843
  }
57372
57844
  async _clearTaskQueue(taskId, sessionId) {
57373
57845
  if (this._taskMessageQueue) {
57374
- const messages = await this._taskMessageQueue.dequeueAll(taskId, sessionId);
57375
- for (const message of messages) {
57846
+ const messages2 = await this._taskMessageQueue.dequeueAll(taskId, sessionId);
57847
+ for (const message of messages2) {
57376
57848
  if (message.type === "request" && isJSONRPCRequest(message.message)) {
57377
57849
  const requestId = message.message.id;
57378
57850
  const resolver = this._requestResolvers.get(requestId);
@@ -58966,7 +59438,7 @@ var require_dataType = __commonJS((exports) => {
58966
59438
  exports.coerceAndCheckDataType = coerceAndCheckDataType;
58967
59439
  var COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]);
58968
59440
  function coerceToTypes(types2, coerceTypes) {
58969
- return coerceTypes ? types2.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : [];
59441
+ return coerceTypes ? types2.filter((t2) => COERCIBLE.has(t2) || coerceTypes === "array" && t2 === "array") : [];
58970
59442
  }
58971
59443
  function coerceData(it, types2, coerceTo) {
58972
59444
  const { gen, data, opts } = it;
@@ -58976,9 +59448,9 @@ var require_dataType = __commonJS((exports) => {
58976
59448
  gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(types2, data, opts.strictNumbers), () => gen.assign(coerced, data)));
58977
59449
  }
58978
59450
  gen.if((0, codegen_1._)`${coerced} !== undefined`);
58979
- for (const t of coerceTo) {
58980
- if (COERCIBLE.has(t) || t === "array" && opts.coerceTypes === "array") {
58981
- coerceSpecificType(t);
59451
+ for (const t2 of coerceTo) {
59452
+ if (COERCIBLE.has(t2) || t2 === "array" && opts.coerceTypes === "array") {
59453
+ coerceSpecificType(t2);
58982
59454
  }
58983
59455
  }
58984
59456
  gen.else();
@@ -58988,8 +59460,8 @@ var require_dataType = __commonJS((exports) => {
58988
59460
  gen.assign(data, coerced);
58989
59461
  assignParentData(it, coerced);
58990
59462
  });
58991
- function coerceSpecificType(t) {
58992
- switch (t) {
59463
+ function coerceSpecificType(t2) {
59464
+ switch (t2) {
58993
59465
  case "string":
58994
59466
  gen.elseIf((0, codegen_1._)`${dataType} == "number" || ${dataType} == "boolean"`).assign(coerced, (0, codegen_1._)`"" + ${data}`).elseIf((0, codegen_1._)`${data} === null`).assign(coerced, (0, codegen_1._)`""`);
58995
59467
  return;
@@ -59061,8 +59533,8 @@ var require_dataType = __commonJS((exports) => {
59061
59533
  }
59062
59534
  if (types2.number)
59063
59535
  delete types2.integer;
59064
- for (const t in types2)
59065
- cond = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct));
59536
+ for (const t2 in types2)
59537
+ cond = (0, codegen_1.and)(cond, checkDataType(t2, data, strictNums, correct));
59066
59538
  return cond;
59067
59539
  }
59068
59540
  exports.checkDataTypes = checkDataTypes;
@@ -59960,9 +60432,9 @@ var require_validate = __commonJS((exports) => {
59960
60432
  it.dataTypes = types2;
59961
60433
  return;
59962
60434
  }
59963
- types2.forEach((t) => {
59964
- if (!includesType(it.dataTypes, t)) {
59965
- strictTypesError(it, `type "${t}" not allowed by context "${it.dataTypes.join(",")}"`);
60435
+ types2.forEach((t2) => {
60436
+ if (!includesType(it.dataTypes, t2)) {
60437
+ strictTypesError(it, `type "${t2}" not allowed by context "${it.dataTypes.join(",")}"`);
59966
60438
  }
59967
60439
  });
59968
60440
  narrowSchemaTypes(it, types2);
@@ -59978,7 +60450,7 @@ var require_validate = __commonJS((exports) => {
59978
60450
  const rule = rules[keyword];
59979
60451
  if (typeof rule == "object" && (0, applicability_1.shouldUseRule)(it.schema, rule)) {
59980
60452
  const { type } = rule.definition;
59981
- if (type.length && !type.some((t) => hasApplicableType(ts, t))) {
60453
+ if (type.length && !type.some((t2) => hasApplicableType(ts, t2))) {
59982
60454
  strictTypesError(it, `missing type "${type.join(",")}" for keyword "${keyword}"`);
59983
60455
  }
59984
60456
  }
@@ -59987,15 +60459,15 @@ var require_validate = __commonJS((exports) => {
59987
60459
  function hasApplicableType(schTs, kwdT) {
59988
60460
  return schTs.includes(kwdT) || kwdT === "number" && schTs.includes("integer");
59989
60461
  }
59990
- function includesType(ts, t) {
59991
- return ts.includes(t) || t === "integer" && ts.includes("number");
60462
+ function includesType(ts, t2) {
60463
+ return ts.includes(t2) || t2 === "integer" && ts.includes("number");
59992
60464
  }
59993
60465
  function narrowSchemaTypes(it, withTypes) {
59994
60466
  const ts = [];
59995
- for (const t of it.dataTypes) {
59996
- if (includesType(withTypes, t))
59997
- ts.push(t);
59998
- else if (withTypes.includes("integer") && t === "number")
60467
+ for (const t2 of it.dataTypes) {
60468
+ if (includesType(withTypes, t2))
60469
+ ts.push(t2);
60470
+ else if (withTypes.includes("integer") && t2 === "number")
59999
60471
  ts.push("integer");
60000
60472
  }
60001
60473
  it.dataTypes = ts;
@@ -61539,7 +62011,7 @@ var require_core = __commonJS((exports) => {
61539
62011
  type: (0, dataType_1.getJSONTypes)(def.type),
61540
62012
  schemaType: (0, dataType_1.getJSONTypes)(def.schemaType)
61541
62013
  };
61542
- (0, util_1.eachItem)(keyword, definition.type.length === 0 ? (k) => addRule.call(this, k, definition) : (k) => definition.type.forEach((t) => addRule.call(this, k, definition, t)));
62014
+ (0, util_1.eachItem)(keyword, definition.type.length === 0 ? (k) => addRule.call(this, k, definition) : (k) => definition.type.forEach((t2) => addRule.call(this, k, definition, t2)));
61543
62015
  return this;
61544
62016
  }
61545
62017
  getKeyword(keyword) {
@@ -61733,7 +62205,7 @@ var require_core = __commonJS((exports) => {
61733
62205
  if (dataType && post)
61734
62206
  throw new Error('keyword with "post" flag cannot have "type"');
61735
62207
  const { RULES } = this;
61736
- let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t }) => t === dataType);
62208
+ let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t2 }) => t2 === dataType);
61737
62209
  if (!ruleGroup) {
61738
62210
  ruleGroup = { type: dataType, rules: [] };
61739
62211
  RULES.rules.push(ruleGroup);
@@ -62242,7 +62714,7 @@ var require_uniqueItems = __commonJS((exports) => {
62242
62714
  gen.if((0, codegen_1._)`${i} > 1`, () => (canOptimize() ? loopN : loopN2)(i, j));
62243
62715
  }
62244
62716
  function canOptimize() {
62245
- return itemTypes.length > 0 && !itemTypes.some((t) => t === "object" || t === "array");
62717
+ return itemTypes.length > 0 && !itemTypes.some((t2) => t2 === "object" || t2 === "array");
62246
62718
  }
62247
62719
  function loopN(i, j) {
62248
62720
  const item = gen.name("item");
@@ -64810,7 +65282,7 @@ class McpServer {
64810
65282
  return createCompletionResult(suggestions);
64811
65283
  }
64812
65284
  async handleResourceCompletion(request, ref) {
64813
- const template = Object.values(this._registeredResourceTemplates).find((t) => t.resourceTemplate.uriTemplate.toString() === ref.uri);
65285
+ const template = Object.values(this._registeredResourceTemplates).find((t2) => t2.resourceTemplate.uriTemplate.toString() === ref.uri);
64814
65286
  if (!template) {
64815
65287
  if (this._registeredResources[ref.uri]) {
64816
65288
  return EMPTY_COMPLETION_RESULT;
@@ -65438,14 +65910,14 @@ async function startMcpServer() {
65438
65910
  } else {
65439
65911
  results = getTopTrends(10);
65440
65912
  }
65441
- const formatted = results.map((t) => ({
65442
- id: t.id,
65443
- name: t.name,
65444
- category: t.category,
65445
- description: t.description,
65446
- popularity: t.popularity,
65447
- keywords: t.keywords,
65448
- figmaPromptHints: t.figmaPromptHints
65913
+ const formatted = results.map((t2) => ({
65914
+ id: t2.id,
65915
+ name: t2.name,
65916
+ category: t2.category,
65917
+ description: t2.description,
65918
+ popularity: t2.popularity,
65919
+ keywords: t2.keywords,
65920
+ figmaPromptHints: t2.figmaPromptHints
65449
65921
  }));
65450
65922
  return {
65451
65923
  content: [
@@ -65465,7 +65937,7 @@ async function startMcpServer() {
65465
65937
  content: [
65466
65938
  {
65467
65939
  type: "text",
65468
- text: `Trend "${trendId}" not found. Available trends: ${TRENDS.map((t) => t.id).join(", ")}`
65940
+ text: `Trend "${trendId}" not found. Available trends: ${TRENDS.map((t2) => t2.id).join(", ")}`
65469
65941
  }
65470
65942
  ],
65471
65943
  isError: true
@@ -65500,7 +65972,7 @@ async function startMcpServer() {
65500
65972
  content: [
65501
65973
  {
65502
65974
  type: "text",
65503
- text: `Trend "${trend1}" not found. Available trends: ${TRENDS.map((t) => t.id).join(", ")}`
65975
+ text: `Trend "${trend1}" not found. Available trends: ${TRENDS.map((t3) => t3.id).join(", ")}`
65504
65976
  }
65505
65977
  ],
65506
65978
  isError: true
@@ -65511,7 +65983,7 @@ async function startMcpServer() {
65511
65983
  content: [
65512
65984
  {
65513
65985
  type: "text",
65514
- text: `Trend "${trend2}" not found. Available trends: ${TRENDS.map((t) => t.id).join(", ")}`
65986
+ text: `Trend "${trend2}" not found. Available trends: ${TRENDS.map((t3) => t3.id).join(", ")}`
65515
65987
  }
65516
65988
  ],
65517
65989
  isError: true
@@ -65539,11 +66011,11 @@ async function startMcpServer() {
65539
66011
  {
65540
66012
  uri: uri.href,
65541
66013
  mimeType: "application/json",
65542
- text: JSON.stringify(TRENDS.map((t) => ({
65543
- id: t.id,
65544
- name: t.name,
65545
- category: t.category,
65546
- popularity: t.popularity
66014
+ text: JSON.stringify(TRENDS.map((t2) => ({
66015
+ id: t2.id,
66016
+ name: t2.name,
66017
+ category: t2.category,
66018
+ popularity: t2.popularity
65547
66019
  })), null, 2)
65548
66020
  }
65549
66021
  ]
@@ -65582,28 +66054,26 @@ init_display();
65582
66054
  init_ora();
65583
66055
  init_engine();
65584
66056
  init_display();
66057
+ init_i18n();
65585
66058
  async function chatCommand(message) {
65586
66059
  if (!message || message.trim().length === 0) {
65587
- printError("Please provide a design description. Example:");
65588
- console.log(' designnn chat "SaaS pricing page with dark theme"');
66060
+ printError(t("chatNoMessage"));
65589
66061
  process.exit(1);
65590
66062
  }
65591
66063
  const spinner = ora({
65592
- text: "Analyzing your request and generating Figma AI prompt...",
66064
+ text: t("chatAnalyzing"),
65593
66065
  color: "yellow"
65594
66066
  }).start();
65595
66067
  try {
65596
66068
  const prompt = await generateChatPrompt(message);
65597
66069
  spinner.stop();
65598
- printPrompt(prompt, `Prompt for: "${message}"`);
66070
+ printPrompt(prompt, `${t("chatPromptFor")} "${message}"`);
65599
66071
  } catch (error3) {
65600
66072
  spinner.stop();
65601
66073
  if (error3?.code === "ENOTFOUND" || error3?.message?.includes("API key")) {
65602
- printError(`OpenAI API key not found. Set it with:
65603
- export OPENAI_API_KEY=your-key
65604
- or: designnn config --api-key your-key`);
66074
+ printError(t("chatApiKeyError"));
65605
66075
  } else {
65606
- printError(`Generation failed: ${error3?.message || "Unknown error"}`);
66076
+ printError(`${t("chatFailed")}: ${error3?.message || "Unknown error"}`);
65607
66077
  }
65608
66078
  process.exit(1);
65609
66079
  }
@@ -65615,57 +66085,58 @@ init_source();
65615
66085
  init_trends();
65616
66086
  init_engine();
65617
66087
  init_display();
66088
+ init_i18n();
65618
66089
  async function exploreCommand(options) {
65619
66090
  let results = [];
65620
- let title = "Design Trends";
66091
+ let title = t("exploreDesignTrends");
65621
66092
  if (options.component) {
65622
66093
  results = searchTrends(options.component);
65623
- title = `Trends matching: "${options.component}"`;
66094
+ title = `${t("exploreMatching")}: "${options.component}"`;
65624
66095
  } else if (options.category) {
65625
66096
  const validCategories = ["style", "component", "pattern", "layout", "interaction"];
65626
66097
  if (!validCategories.includes(options.category)) {
65627
- printError(`Invalid category. Choose from: ${validCategories.join(", ")}`);
66098
+ printError(`${t("exploreInvalidCategory")}: ${validCategories.join(", ")}`);
65628
66099
  process.exit(1);
65629
66100
  }
65630
66101
  results = getTrendsByCategory(options.category);
65631
- title = `${options.category.charAt(0).toUpperCase() + options.category.slice(1)} Trends`;
66102
+ title = `${getCategoryName(options.category)} ${t("exploreTrends")}`;
65632
66103
  } else if (options.top) {
65633
66104
  const limit2 = parseInt(options.top) || 10;
65634
66105
  results = getTopTrends(limit2);
65635
- title = `Top ${limit2} Design Trends`;
66106
+ title = `${t("exploreTop")} ${limit2} ${t("exploreDesignTrends")}`;
65636
66107
  } else {
65637
- printSection("All Design Trends");
65638
- console.log(source_default.dim(` ${TRENDS.length} trends in database
66108
+ printSection(t("exploreAllTrends"));
66109
+ console.log(source_default.dim(` ${TRENDS.length} ${t("exploreTrendsInDb")}
65639
66110
  `));
65640
66111
  const categories = ["style", "component", "pattern", "layout", "interaction"];
65641
66112
  for (const cat of categories) {
65642
66113
  const catTrends = getTrendsByCategory(cat);
65643
- console.log(source_default.bold(` ${cat.toUpperCase()} (${catTrends.length})`));
65644
- catTrends.forEach((t, i) => printTrend(t, i));
66114
+ console.log(source_default.bold(` ${getCategoryName(cat).toUpperCase()} (${catTrends.length})`));
66115
+ catTrends.forEach((t2, i) => printTrend(t2, i));
65645
66116
  }
65646
- console.log(source_default.dim(" Tip: Use --category, --component, or --top to filter"));
65647
- console.log(source_default.dim(` Tip: Use --generate <trend-id> to create a prompt from a trend
66117
+ console.log(source_default.dim(` ${t("exploreTipFilter")}`));
66118
+ console.log(source_default.dim(` ${t("exploreTipGenerate")}
65648
66119
  `));
65649
66120
  return;
65650
66121
  }
65651
66122
  if (results.length === 0) {
65652
- printError("No trends found matching your query.");
66123
+ printError(t("exploreNoResults"));
65653
66124
  console.log(source_default.dim(" Try: designnn explore --top 10"));
65654
66125
  return;
65655
66126
  }
65656
66127
  printSection(title);
65657
- console.log(source_default.dim(` ${results.length} results
66128
+ console.log(source_default.dim(` ${results.length} ${t("exploreResults")}
65658
66129
  `));
65659
- results.forEach((t, i) => printTrend(t, i));
66130
+ results.forEach((t2, i) => printTrend(t2, i));
65660
66131
  if (options.generate) {
65661
- const trend = results.find((t) => t.id === options.generate) || TRENDS.find((t) => t.id === options.generate);
66132
+ const trend = results.find((t2) => t2.id === options.generate) || TRENDS.find((t2) => t2.id === options.generate);
65662
66133
  if (!trend) {
65663
- printError(`Trend "${options.generate}" not found.`);
65664
- console.log(source_default.dim(" Available IDs: " + results.map((t) => t.id).join(", ")));
66134
+ printError(`${t("exploreTrendNotFound")}: "${options.generate}"`);
66135
+ console.log(source_default.dim(` ${t("exploreAvailableIds")}: ` + results.map((t2) => t2.id).join(", ")));
65665
66136
  return;
65666
66137
  }
65667
66138
  const spinner = ora({
65668
- text: `Generating Figma AI prompt for "${trend.name}"...`,
66139
+ text: `${t("exploreGenerating")} "${trend.name}"...`,
65669
66140
  color: "yellow"
65670
66141
  }).start();
65671
66142
  try {
@@ -65674,7 +66145,7 @@ async function exploreCommand(options) {
65674
66145
  printPrompt(prompt, `Trend: ${trend.name}`);
65675
66146
  } catch (error3) {
65676
66147
  spinner.stop();
65677
- printError(`Generation failed: ${error3?.message || "Unknown error"}`);
66148
+ printError(`${t("chatFailed")}: ${error3?.message || "Unknown error"}`);
65678
66149
  }
65679
66150
  }
65680
66151
  }
@@ -65685,54 +66156,53 @@ init_source();
65685
66156
  init_trends();
65686
66157
  init_engine();
65687
66158
  init_display();
66159
+ init_i18n();
65688
66160
  async function mixCommand(trend1Name, trend2Name, options) {
65689
66161
  if (!trend1Name || !trend2Name) {
65690
- printError("Please provide two trend names to mix. Example:");
65691
- console.log(' designnn mix "Bento UI" "Glassmorphism"');
65692
- console.log(" designnn mix bento-ui glassmorphism");
66162
+ printError(t("mixNoTrends"));
65693
66163
  process.exit(1);
65694
66164
  }
65695
66165
  const trend1 = findTrend(trend1Name);
65696
66166
  const trend2 = findTrend(trend2Name);
65697
66167
  if (!trend1) {
65698
- printError(`Trend "${trend1Name}" not found.`);
66168
+ printError(`${t("mixTrendNotFound")}: "${trend1Name}"`);
65699
66169
  suggestTrends(trend1Name);
65700
66170
  process.exit(1);
65701
66171
  }
65702
66172
  if (!trend2) {
65703
- printError(`Trend "${trend2Name}" not found.`);
66173
+ printError(`${t("mixTrendNotFound")}: "${trend2Name}"`);
65704
66174
  suggestTrends(trend2Name);
65705
66175
  process.exit(1);
65706
66176
  }
65707
- printSection("Mixing Trends");
66177
+ printSection(t("mixMixing"));
65708
66178
  console.log("");
65709
- console.log(source_default.bold(" Trend A:"));
66179
+ console.log(source_default.bold(` ${t("mixTrendA")}`));
65710
66180
  printTrend(trend1);
65711
- console.log(source_default.bold(" Trend B:"));
66181
+ console.log(source_default.bold(` ${t("mixTrendB")}`));
65712
66182
  printTrend(trend2);
65713
66183
  if (options.context) {
65714
- console.log(source_default.dim(` Context: "${options.context}"
66184
+ console.log(source_default.dim(` ${t("mixContext")}: "${options.context}"
65715
66185
  `));
65716
66186
  }
65717
66187
  const spinner = ora({
65718
- text: `Mixing "${trend1.name}" × "${trend2.name}" into a Figma AI prompt...`,
66188
+ text: `"${trend1.name}" × "${trend2.name}" ${t("mixGenerating")}`,
65719
66189
  color: "yellow"
65720
66190
  }).start();
65721
66191
  try {
65722
66192
  const prompt = await generateMixPrompt(trend1, trend2, options.context);
65723
66193
  spinner.stop();
65724
- printPrompt(prompt, `Mix: ${trend1.name} × ${trend2.name}`);
66194
+ printPrompt(prompt, `${t("mixResult")}: ${trend1.name} × ${trend2.name}`);
65725
66195
  } catch (error3) {
65726
66196
  spinner.stop();
65727
- printError(`Generation failed: ${error3?.message || "Unknown error"}`);
66197
+ printError(`${t("chatFailed")}: ${error3?.message || "Unknown error"}`);
65728
66198
  process.exit(1);
65729
66199
  }
65730
66200
  }
65731
66201
  function findTrend(query2) {
65732
- const byId = TRENDS.find((t) => t.id === query2.toLowerCase());
66202
+ const byId = TRENDS.find((t2) => t2.id === query2.toLowerCase());
65733
66203
  if (byId)
65734
66204
  return byId;
65735
- const byName = TRENDS.find((t) => t.name.toLowerCase() === query2.toLowerCase());
66205
+ const byName = TRENDS.find((t2) => t2.name.toLowerCase() === query2.toLowerCase());
65736
66206
  if (byName)
65737
66207
  return byName;
65738
66208
  const results = searchTrends(query2);
@@ -65741,23 +66211,35 @@ function findTrend(query2) {
65741
66211
  function suggestTrends(query2) {
65742
66212
  const results = searchTrends(query2);
65743
66213
  if (results.length > 0) {
65744
- console.log(source_default.dim(" Did you mean:"));
65745
- results.slice(0, 3).forEach((t) => {
65746
- console.log(source_default.dim(` - ${t.name} (${t.id})`));
66214
+ console.log(source_default.dim(` ${t("mixDidYouMean")}`));
66215
+ results.slice(0, 3).forEach((trend) => {
66216
+ console.log(source_default.dim(` - ${trend.name} (${trend.id})`));
65747
66217
  });
65748
66218
  } else {
65749
- console.log(source_default.dim(" Available trends:"));
65750
- TRENDS.slice(0, 5).forEach((t) => {
65751
- console.log(source_default.dim(` - ${t.name} (${t.id})`));
66219
+ console.log(source_default.dim(` ${t("mixAvailableTrends")}`));
66220
+ TRENDS.slice(0, 5).forEach((trend) => {
66221
+ console.log(source_default.dim(` - ${trend.name} (${trend.id})`));
65752
66222
  });
65753
- console.log(source_default.dim(" Use 'designnn explore' to see all trends."));
66223
+ console.log(source_default.dim(` ${t("mixSeeAll")}`));
65754
66224
  }
65755
66225
  }
65756
66226
 
65757
66227
  // src/cli.ts
66228
+ init_i18n();
65758
66229
  var program2 = new Command;
66230
+ function initLang(options) {
66231
+ if (options?.lang) {
66232
+ setLang(options.lang);
66233
+ } else {
66234
+ setLang(detectLang());
66235
+ }
66236
+ }
65759
66237
  program2.name("designnn").description(`Trend-driven design prompt engine for Figma AI.
65760
- Generate high-quality UI/UX prompts powered by real-time design trend analysis.`).version("0.2.0").hook("preAction", (thisCommand) => {
66238
+ Generate high-quality UI/UX prompts powered by real-time design trend analysis.
66239
+
66240
+ Figma AI のためのトレンド駆動デザインプロンプトエンジン。`).version("0.4.0").option("--lang <language>", "Display language: en or ja (auto-detected from system)").hook("preAction", (thisCommand) => {
66241
+ const opts = thisCommand.opts();
66242
+ initLang(opts);
65761
66243
  if (thisCommand.name() !== "agent") {
65762
66244
  printBanner();
65763
66245
  }
@@ -65790,6 +66272,7 @@ program2.command("agent").description("Start DESIGNNN as an MCP server for AI ag
65790
66272
  });
65791
66273
  program2.parse(process.argv);
65792
66274
  if (!process.argv.slice(2).length) {
66275
+ setLang(detectLang());
65793
66276
  printBanner();
65794
66277
  program2.outputHelp();
65795
66278
  }