@akanjs/cli 0.0.135 → 0.0.137

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/cjs/index.js CHANGED
@@ -459,6 +459,11 @@ var Logger = class _Logger {
459
459
  }
460
460
  };
461
461
 
462
+ // pkgs/@akanjs/common/lowerlize.ts
463
+ var lowerlize = (str) => {
464
+ return str.charAt(0).toLowerCase() + str.slice(1);
465
+ };
466
+
462
467
  // pkgs/@akanjs/common/sleep.ts
463
468
  var sleep = async (ms) => {
464
469
  return new Promise((resolve) => {
@@ -1071,7 +1076,7 @@ var Executor = class {
1071
1076
  `${dirname}/${filename}`
1072
1077
  );
1073
1078
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
1074
- this.writeFile(convertedTargetPath, content);
1079
+ this.writeFile(convertedTargetPath, content, { overwrite });
1075
1080
  } else if (targetPath.endsWith(".template")) {
1076
1081
  const content = await import_promises.default.readFile(templatePath, "utf8");
1077
1082
  const convertedTargetPath = Object.entries(dict).reduce(
@@ -1083,7 +1088,7 @@ var Executor = class {
1083
1088
  content
1084
1089
  );
1085
1090
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
1086
- this.writeFile(convertedTargetPath, convertedContent);
1091
+ this.writeFile(convertedTargetPath, convertedContent, { overwrite });
1087
1092
  }
1088
1093
  }
1089
1094
  async applyTemplate({
@@ -2108,6 +2113,9 @@ var getArgumentValue = async (argMeta, value, workspace) => {
2108
2113
  throw new Error(`Invalid system type: ${argMeta.type}`);
2109
2114
  };
2110
2115
  var runCommands = async (...commands) => {
2116
+ process.on("unhandledRejection", (error) => {
2117
+ console.error(import_chalk2.default.red("[fatal]"), error);
2118
+ });
2111
2119
  const hasPackageJson = import_fs7.default.existsSync(`${__dirname}/../package.json`);
2112
2120
  const version = hasPackageJson ? JSON.parse(import_fs7.default.readFileSync(`${__dirname}/../package.json`, "utf8")).version : "0.0.1";
2113
2121
  import_commander.program.version(version).description("Akan CLI");
@@ -2166,6 +2174,7 @@ var runCommands = async (...commands) => {
2166
2174
 
2167
2175
  // pkgs/@akanjs/devkit/src/aiEditor.ts
2168
2176
  var import_prompts4 = require("@inquirer/prompts");
2177
+ var import_ora2 = __toESM(require("ora"));
2169
2178
  var import_messages = require("@langchain/core/messages");
2170
2179
  var import_openai2 = require("@langchain/openai");
2171
2180
  var import_chalk3 = __toESM(require("chalk"));
@@ -2239,23 +2248,28 @@ var AiSession = class _AiSession {
2239
2248
  await _AiSession.init();
2240
2249
  if (!_AiSession.#chat)
2241
2250
  throw new Error("Failed to initialize the AI session");
2251
+ const loader = (0, import_ora2.default)(`${_AiSession.#chat.model} is thinking...`).start();
2242
2252
  try {
2243
2253
  const humanMessage = new import_messages.HumanMessage(question);
2244
2254
  this.messageHistory.push(humanMessage);
2245
2255
  const stream = await _AiSession.#chat.stream(this.messageHistory);
2246
- let fullResponse = "";
2256
+ let fullResponse = "", tokenIdx = 0;
2247
2257
  for await (const chunk of stream) {
2258
+ if (loader.isSpinning)
2259
+ loader.succeed(`${_AiSession.#chat.model} responded`);
2248
2260
  const content = chunk.content;
2249
2261
  if (typeof content === "string") {
2250
2262
  fullResponse += content;
2251
2263
  onChunk(content);
2252
2264
  }
2265
+ tokenIdx++;
2253
2266
  }
2254
2267
  fullResponse += "\n";
2255
2268
  onChunk("\n");
2256
2269
  this.messageHistory.push(new import_messages.AIMessage(fullResponse));
2257
2270
  return { content: fullResponse, messageHistory: this.messageHistory };
2258
2271
  } catch (error) {
2272
+ loader.fail(`${_AiSession.#chat.model} failed to respond`);
2259
2273
  throw new Error("Failed to stream response");
2260
2274
  }
2261
2275
  }
@@ -2509,7 +2523,7 @@ var import_fs9 = __toESM(require("fs"));
2509
2523
  var import_promises2 = __toESM(require("fs/promises"));
2510
2524
  var import_js_yaml2 = __toESM(require("js-yaml"));
2511
2525
  var import_open = __toESM(require("open"));
2512
- var import_ora2 = __toESM(require("ora"));
2526
+ var import_ora3 = __toESM(require("ora"));
2513
2527
  var import_path4 = __toESM(require("path"));
2514
2528
  var vite = __toESM(require("vite"));
2515
2529
  var import_vite_plugin_commonjs = __toESM(require("vite-plugin-commonjs"));
@@ -2518,1031 +2532,6 @@ var import_vite_tsconfig_paths = __toESM(require("vite-tsconfig-paths"));
2518
2532
 
2519
2533
  // pkgs/@akanjs/cli/src/module/module.prompt.ts
2520
2534
  var frameworkDescription = `
2521
- \uB098\uB294 \uC880 \uB354 \uD6A8\uC728\uC801\uC73C\uB85C \uCF54\uB529\uC744 \uD558\uAE30 \uC704\uD574\uC11C \uC790\uCCB4 \uD504\uB808\uC784\uC6CC\uD06C\uB97C \uC81C\uC791\uD588\uC5B4.
2522
-
2523
- \uADF8\uB798\uC11C \uC6B0\uB9AC \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uC124\uBA85\uC744 \uD574\uC904\uD14C\uB2C8\uAE4C \uC798 \uC774\uD574\uD558\uB3C4\uB85D \uD574. \uC6B0\uB9AC \uD504\uB808\uC784\uC6CC\uD06C\uB294 next.js 13\uACFC nest.js, capacitor.js, nx, mongoDB \uAE30\uBC18\uC758 \uD504\uB860\uD2B8\uC5D4\uB4DC, \uC571, \uBC31\uC5D4\uB4DC \uD1B5\uD569 \uD504\uB808\uC784\uC6CC\uD06C\uC57C. \uADF8\uB798\uC11C \uBC31\uC5D4\uB4DC, \uD504\uB860\uD2B8\uC5D4\uB4DC, DB Schema\uC5D0 \uB300\uD55C \uCF54\uB4DC\uAC00 \uBAA8\uB450 \uD55C \uD3F4\uB354 \uC548\uC5D0 \uC788\uC5B4. \uADF8\uB798\uC11C \uC790\uBC14\uC2A4\uD06C\uB9BD\uD2B8, \uD0C0\uC785\uC2A4\uD06C\uB9BD\uD2B8, \uADF8\uB9AC\uACE0 \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uC774\uD574\uB9CC \uC788\uB2E4\uBA74 \uAD6C\uBD84\uC9D3\uC9C0 \uC54A\uACE0 \uC0AC\uC6A9\uD560 \uC218 \uC788\uB2E4\uB294 \uC7A5\uC810\uC774 \uC788\uC5B4.
2524
-
2525
-
2526
-
2527
- \uAC00\uC7A5 \uC678\uBD80\uC758 \uAD6C\uC870\uB294
2528
-
2529
- - app
2530
-
2531
- - project1
2532
-
2533
- - project2
2534
-
2535
- - project3
2536
-
2537
- - project4
2538
-
2539
- - lib
2540
-
2541
- - core
2542
-
2543
- - external
2544
-
2545
- - game
2546
-
2547
- - mint
2548
-
2549
- - platform
2550
-
2551
- - shared
2552
-
2553
- - social
2554
-
2555
- - util
2556
-
2557
- app\uC740 \uAC01 \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4DC\uB294 \uACF3\uC774\uC57C
2558
-
2559
- lib \uD558\uC704 \uD3F4\uB354\uB4E4\uC740 \uC5EC\uB7EC \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uACF5\uC6A9\uC73C\uB85C \uC0AC\uC6A9\uD560 \uC218 \uC788\uACA0\uB2E4 \uB77C\uACE0 \uD310\uB2E8\uD574\uC11C \uBD84\uB9AC\uD574\uB193\uC740 \uACF3\uC774\uC57C.
2560
-
2561
- \uB2E4\uC74C\uC740 app/project \uB0B4\uBD80\uC758 \uAD6C\uC870\uB97C \uC54C\uB824\uC904\uAC8C.
2562
-
2563
-
2564
-
2565
- project
2566
-
2567
- - app
2568
-
2569
- - [lang]
2570
-
2571
- - (projectName)
2572
-
2573
- - (user)
2574
-
2575
- - page.tsx
2576
-
2577
- - layout.tsx
2578
-
2579
- - (public)
2580
-
2581
- - page.tsx
2582
-
2583
- - layout.tsx
2584
-
2585
- - (admin)
2586
-
2587
- - page.tsx
2588
-
2589
- - layout.tsx
2590
-
2591
-
2592
-
2593
- - lib
2594
-
2595
- - dataName1
2596
-
2597
- - dataName2
2598
-
2599
- - dataName3
2600
-
2601
- - dataName4
2602
-
2603
- - dataName5
2604
-
2605
- - dataName6
2606
-
2607
- - dataName7
2608
-
2609
-
2610
-
2611
- app\uC740 \uC9C1\uC811\uC801\uC73C\uB85C \uC720\uC800\uAC00 \uBCF4\uB294 \uD398\uC774\uC9C0\uB97C \uC9DC\uB294 \uACF3\uC774\uC57C. \uD3F4\uB354 \uAD6C\uC870\uB294 next 13\uC758 app directory\uB97C \uB530\uB974\uACE0 \uC788\uC5B4.
2612
-
2613
- public/page.tsx\uAC00 \uAC00\uC7A5 \uCD5C\uCD08\uB85C \uC811\uADFC\uB418\uB294 Index \uD398\uC774\uC9C0\uAC00 \uB420 \uAC70\uC57C.
2614
-
2615
- \uC544\uB798\uB294 public/page.tsx\uC758 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uC18C\uC2A4\uCF54\uB4DC\uC57C.
2616
-
2617
- \`\`\`
2618
-
2619
- import {{ Image, Link }} from "@util/ui";
2620
-
2621
- import {{ getSelf }} from "@akanjs/client";
2622
-
2623
-
2624
-
2625
- export default function Page() {{
2626
-
2627
- const self = getSelf();
2628
-
2629
- return (
2630
-
2631
- <div className="relative w-full h-screen overflow-hidden flex items-center justify-center">
2632
-
2633
- <Image
2634
-
2635
- className="absolute left-0 right-0 top-0 bottom-0 w-full h-screen -z-50"
2636
-
2637
- width={{1920}}
2638
-
2639
- height={{1080}}
2640
-
2641
- src="/back.jpg"
2642
-
2643
- />
2644
-
2645
- <div className="max-w-md bg-base-100/50 shadow-lg rounded-xl backdrop-blur-xs w-full py-8 px-16 flex flex-col items-center justify-center gap-3">
2646
-
2647
- <h1 className="text-4xl mt-2"><%= project %></h1>
2648
-
2649
- <h2 className="text-lg"><%= project %> description</h2>
2650
-
2651
- <Link className="w-full" href={{self ? "/home" : "/signin"}}>
2652
-
2653
- <button className="btn w-full btn-primary">Go to dashboard</button>
2654
-
2655
- </Link>
2656
-
2657
- </div>
2658
-
2659
- </div>
2660
-
2661
- );
2662
-
2663
- }}
2664
-
2665
- \`\`\`
2666
-
2667
-
2668
-
2669
- lib\uC740 \uC11C\uBE44\uC2A4\uB97C \uB9CC\uB4E4\uB2E4\uBCF4\uBA74 \uD544\uC694\uD55C \uB370\uC774\uD130\uB97C \uC815\uC758\uD558\uB294 \uACF3\uC774\uC57C.
2670
-
2671
- \uB530\uB77C\uC11C dataName\uC740 DB\uC774\uB984. \uC989 \uB370\uC774\uD130\uBA85\uC744 \uB530\uB77C. \uB2E4\uB9CC \uD3F4\uB354 \uB0B4\uBD80\uC5D0\uB294 backend, frontend\uC5D0\uC11C \uD1B5\uD569\uC73C\uB85C \uC4F0\uB294 \uC18C\uC2A4\uCF54\uB4DC\uB4E4\uC774 \uC788\uC5B4.
2672
-
2673
-
2674
-
2675
- \uC6B0\uB9AC \uD504\uB808\uC784\uC6CC\uD06C\uC758 \uD575\uC2EC\uC740 \uC815\uC758\uD55C \uBA54\uD0C0\uB370\uC774\uD130\uB4E4\uC744 \uC774\uC6A9\uD574\uC11C \uAC01 \uB370\uC774\uD130\uAC04\uC758 \uC5F0\uAD00\uC810\uC774\uB098, \uB370\uC774\uD130 \uD0C0\uC785\uC744 \uBCF4\uACE0 \uBBF8\uB9AC \uC815\uB9AC\uB41C \uACB0\uACFC\uB97C \uB9CC\uB4E4\uC5B4\uC8FC\uACE0 \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4E0\uB2E4\uBA74 \uAC70\uC758 \uBAA8\uB450 \uD544\uC694\uD55C \uC791\uC5C5\uB4E4\uC744 \uBBF8\uB9AC \uC815\uB9AC\uD574\uB1A8\uC5B4. \uC608\uB97C \uB4E4\uC5B4 \uB370\uC774\uD130 \uD0C0\uC785\uC5D0 \uC774\uBBF8\uC9C0 \uD30C\uC77C\uC774 \uC788\uB2E4\uACE0 \uD55C\uB2E4\uBA74 \uBC31\uC5D4\uB4DC\uB97C \uD1B5\uD574 \uADF8 \uC774\uBBF8\uC9C0\uB97C \uC11C\uBC84\uC758 \uC2A4\uD1A0\uB9AC\uC9C0\uC5D0 \uC800\uC7A5\uD558\uACE0 \uADF8 \uC800\uC7A5\uB41C \uD30C\uC77C\uC744 \uD2B9\uC815 db\uC5D0 id\uB97C \uCD94\uAC00\uD574\uC8FC\uB294 \uD568\uC218\uB4E4 \uAC19\uC740 \uAC83\uB4E4\uC740 \uC5B4\uB290 \uD504\uB85C\uC81D\uD2B8\uB358 \uAC04\uC5D0 \uBAA8\uB450 \uC0AC\uC6A9\uB418\uB294 \uBC29\uC2DD\uC774\uB77C\uACE0 \uD310\uB2E8\uD588\uC5B4. \uD558\uC9C0\uB9CC \uC6B0\uB9AC\uB294 \uD504\uB85C\uC81D\uD2B8\uB97C \uC791\uC5C5\uD560 \uB54C\uB9C8\uB2E4 \uC774 \uBD88\uD544\uC694\uD55C \uC791\uC5C5\uB4E4\uC744 \uD56D\uC0C1 \uB9CC\uB4E4\uACE0 \uD14C\uC2A4\uD2B8\uB97C \uD558\uB294 \uBD88\uD544\uC694\uD55C \uC77C\uB4E4\uC744 \uD558\uACE0\uC788\uC9C0. \uADF8\uB798\uC11C \uC6B0\uB9AC\uB294 \uAC01 \uD504\uB85C\uC81D\uD2B8\uAC00 \uB9CC\uB4E4 \uB54C\uB9C8\uB2E4 \uD544\uC218\uB85C \uC788\uC5B4\uC57C\uD560 \uC774\uBBF8\uC9C0 \uC5C5\uB85C\uB4DC \uD504\uB85C\uC138\uC2A4\uB97C \uD504\uB808\uC784\uC6CC\uD06C\uB97C \uD1B5\uD574 \uBBF8\uB9AC \uC815\uC758\uB41C \uC0C1\uD0DC\uB85C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC5B4.
2676
-
2677
- \uACB0\uACFC\uC801\uC73C\uB85C \uC6B0\uB9B0 \uD544\uC694\uD558\uC9C0\uB9CC \uB9E4\uBC88 \uC791\uC5C5\uD558\uAE30\uC5D4 \uC190\uC774 \uB9CE\uC774\uAC00\uB294 \uC791\uC5C5\uB4E4\uC744 \uBBF8\uB9AC \uC815\uC758\uD574\uB1A8\uC5B4.
2678
-
2679
-
2680
-
2681
- \uB610\uD55C \uC6B0\uB9B0 CLI\uB97C \uD1B5\uD574\uC11C \uD2B9\uC815 \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354 \uD15C\uD50C\uB9BF\uC744 \uB9CC\uB4E4\uC5B4\uC8FC\uAC70\uB098, \uD2B9\uC815 \uBAA8\uB378\uC758 \uD544\uB4DC\uB4E4\uC744 \uB9CC\uB4E4 \uC218 \uC788\uB3C4\uB85D \uD574\uB1A8\uC5B4. Model\uC774\uB77C\uB294 \uBCC0\uC218\uC758 \uACB0\uACFC\uAC12\uC744 \uBC1B\uACE0 \uC800 \uD15C\uD50C\uB9BF\uC5D0 \uB9DE\uAC8C\uB054 \uC774\uB984\uC744 \uB123\uC5B4\uC8FC\uB294 \uAC70\uC9C0. \uB9CC\uC57D \uB0B4\uAC00 phone\uC774\uB77C\uB294 \uBCC0\uC218\uB97C \uB123\uC5C8\uB2E4\uBA74 \uC800 \uBAA8\uB378\uC5D0 Phone\uACFC \uAC19\uC740 \uACB0\uACFC\uAC12\uC73C\uB85C \uAC12\uC774 \uB4E4\uC5B4\uAC00\uAC8C \uB428\uC73C\uB85C\uC11C \uD15C\uD50C\uB9BF\uC774 \uC0DD\uC131\uB3FC. \uC774\uB294 \uC6B0\uB9AC\uAC00 \uC774\uBBF8 \uB9CC\uB4E4\uC5B4\uB193\uC740 \uCEE4\uB9E8\uB4DC\uAC00 \uC788\uC5B4.
2682
-
2683
-
2684
-
2685
- \uC608\uB97C \uB4E4\uC5B4 \uC5F0\uD544\uC774\uB77C\uB294 \uBAA8\uB378\uC744 \uCEE4\uB9E8\uB4DC\uB97C \uD1B5\uD574 \uC0DD\uC131\uD558\uBA74 \uC544\uB798\uC640 \uAC19\uC740 \uD30C\uC77C\uB4E4\uC774 \uC0DD\uC131\uB3FC.
2686
-
2687
-
2688
-
2689
- pencil
2690
-
2691
- - pencil.constant.ts ( pencil\uC5D0 \uB300\uD55C db schema, query, sort \uAD6C\uBB38\uC744 \uC815\uC758 )
2692
-
2693
- - pencil.document.ts ( model\uC5D0\uC11C \uC790\uC8FC \uC0AC\uC6A9\uD558\uB294 method, static function, middleware\uB85C \uAC04\uB2E8\uD55C db \uAD00\uB9AC \uD568\uC218\uB4F1\uC744 \uAD00\uB9AC\uD568.)
2694
-
2695
- - pencil.dictionary.ts (\uB2E4\uAD6D\uC5B4\uB97C \uC704\uD574\uC11C schema field, enum, service name\uC758 \uB2E4\uAD6D\uC5B4 \uC815\uBCF4 \uB4F1\uC744 \uBAA8\uB450 \uAD00\uB9AC. )
2696
-
2697
- - pencil.signal.ts (frontend\uC758 fetch\uB97C \uC5F4\uC5B4\uC8FC\uB294 \uC77C\uC885\uC758 \uCC3D\uAD6C, \uAD8C\uD55C\uAD00\uB9AC\uB4F1\uC744 \uD568 nestjs\uC758 resolver\uC640 \uC720\uC0AC )
2698
-
2699
- - pencil.service.ts (\uC2E4\uC9C8\uC801\uC778 \uC11C\uBE44\uC2A4 \uCF54\uB4DC\uB97C \uC791\uC131\uD558\uB294 \uACF3. \uAC01\uC885 \uBE44\uC988\uB2C8\uC2A4 \uB85C\uC9C1\uC744 \uCC98\uB9AC\uD568.)
2700
-
2701
- - pencil.store.ts (pencil field\uC5D0 \uB300\uD55C \uC0C1\uD0DC\uAD00\uB9AC \uC800\uC7A5\uC18C.)
2702
-
2703
- - pencil.Zone.tsx (\uC544\uB798 Templete, Unit, Util, View, Util\uB4F1\uC744 \uC870\uD569\uD574\uC11C \uB2E4\uC74C \uB808\uBCA8 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC791\uC131\uD558\uB294 \uACF3 client component\uB85C \uC815\uC758\uB428. )
2704
-
2705
- - pencil.Templete.tsx (\uC8FC\uB85C edit\uC5D0 \uC5F0\uAD00\uB41C \uCEF4\uD3EC\uB10C\uD2B8 \uC815\uC758\uD558\uB294 \uACF3. client component\uB85C \uC815\uC758\uB428. )
2706
-
2707
- - pencil.Unit.tsx (list \uCC98\uB7FC \uBCF5\uC218\uAC1C\uC758 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC815\uC758\uD558\uB294 \uACF3. server component\uB85C \uC815\uC758\uB428. )
2708
-
2709
- - pencil.Util.tsx (Action \uBC84\uD2BC\uC744 \uC815\uC758\uD558\uB294 \uACF3. client component\uB85C \uC815\uC758\uB428. )
2710
-
2711
- - pencil.View.tsx (\uB2E8\uC77C view \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC815\uC758\uD558\uB294 \uACF3. server component\uB85C \uC815\uC758\uB428. )
2712
-
2713
-
2714
-
2715
-
2716
- \uC5EC\uAE30\uC11C constant\uC5D0 \uC815\uC758 \uB418\uC5B4\uC788\uB294 schema\uB97C \uAE30\uBCF8\uC73C\uB85C \uD574\uC11C \uAC01 document, dictionary, signal, service, store\uB97C \uC0DD\uC131\uD574.
2717
-
2718
-
2719
-
2720
- document, signal, service\uB294 \uBC31\uC5D4\uB4DC\uC640 \uC5F0\uAD00\uB418\uC5B4\uC788\uB294 \uC18C\uC2A4\uCF54\uB4DC\uC57C.
2721
-
2722
- \uC11C\uB85C\uAC04\uC758 \uAD00\uACC4\uB294 \uC8FC\uB85C signal\uC5D0\uC11C \uAC00\uC7A5 \uBA3C\uC800 \uC694\uCCAD\uC744 \uBC1B\uACE0 \uC774\uD6C4 \uB85C\uC9C1\uC758 \uBCF5\uC7A1\uB3C4\uC5D0 \uB530\uB77C\uC11C service\uB85C \uCC98\uB9AC\uD560 \uC9C0, docuemnt\uB85C \uCC98\uB9AC\uD560 \uC9C0\uB97C \uACB0\uC815\uD574.
2723
-
2724
- signal - document
2725
-
2726
- signal - service - document
2727
-
2728
-
2729
-
2730
- store\uB294 \uD504\uB860\uD2B8\uC5D4\uB4DC\uC5D0\uC11C \uC0C1\uD0DC\uAD00\uB9AC\uB97C \uD560 \uB54C \uC0AC\uC6A9\uB3FC.
2731
-
2732
- \uC6B0\uB9AC\uB294 signal\uC758 \uCF54\uB4DC\uB85C \uB098\uC628 \uBA54\uD0C0\uB370\uC774\uD130\uB97C \uC774\uC6A9\uD574\uC11C store\uC5D0\uC11C \uD754\uD788 \uC0AC\uC6A9\uB420\uB9CC\uD55C \uD568\uC218\uB4E4\uC744 \uBBF8\uB9AC \uC815\uC758\uD558\uAC8C\uB054 \uB9CC\uB4E4\uC5C8\uC5B4.
2733
-
2734
-
2735
-
2736
- \uC774\uB807\uAC8C 11\uAC1C\uC758 \uD30C\uC77C\uB85C \uAD6C\uBD84\uD574\uC11C \uC815\uC758\uAC00 \uB3FC.
2737
-
2738
- \uC9C0\uAE08\uBD80\uD134 \uD55C \uD30C\uC77C\uB4E4\uB9C8\uB2E4 \uC0C1\uC138\uD55C \uC124\uBA85\uC744 \uD574\uC904\uAC8C.
2739
-
2740
-
2741
-
2742
- model.constant.ts
2743
-
2744
-
2745
-
2746
- constant\uB294 \uAE30\uBCF8 \uC2A4\uD0A4\uB9C8\uAC00 \uAD6C\uC131\uB418\uB294 \uACF3\uC774\uC57C. \uADF8\uB798\uC11C \uC774 \uACF3\uC5D0\uC11C DB \uC2A4\uD0A4\uB9C8, \uCFFC\uB9AC, \uC815\uB82C \uAD6C\uBB38\uC744 \uC815\uC758\uD574.
2747
-
2748
- \uC8FC\uC758\uD574\uC57C\uD560 \uAC74 decorator\uC5D0\uC11C \uC815\uC758\uD558\uB294 \uD0C0\uC785\uACFC \uC544\uB798\uC5D0 \uC815\uC758\uD558\uB294 \uD0C0\uC785\uC774 \uC57D\uAC04 \uB2E4\uB97C \uC218 \uC788\uC5B4
2749
-
2750
- \uC608\uB97C \uB4E4\uC5B4 decorator\uC5D0\uC11C\uB294 Int\uB85C \uC815\uC758\uD558\uACE0 constant\uC5D0\uC11C\uB294 number\uB85C \uC815\uC758\uD574\uC57C\uD574.
2751
-
2752
- \uCD94\uAC00\uB85C createdAt\uACFC updatedAt, status\uB294 \uAE30\uBCF8\uC801\uC73C\uB85C \uC0DD\uC131\uB418\uB294 \uD544\uB4DC\uC774\uB2C8\uAE4C \uB530\uB85C \uC815\uC758\uD558\uC9C0 \uC54A\uC544\uB3C4 \uB3FC.
2753
-
2754
- \uC774\uB294 decorator\uC5D0\uC11C\uB294 GraphQL\uC5D0\uC11C \uC0AC\uC6A9\uD560 \uD0C0\uC785\uC744 \uC815\uC758\uD558\uACE0 constant\uC5D0\uC11C\uB294 \uC2E4\uC81C \uD0C0\uC785\uC744 \uC815\uC758\uD558\uB294 \uAC70\uC9C0.
2755
-
2756
-
2757
-
2758
- \`\`\`
2759
-
2760
- import { enumOf, Int } from "@akanjs/base";
2761
-
2762
- import { Field, Filter, Model, sortOf, via } from "@akanjs/constant";
2763
-
2764
-
2765
-
2766
- export const \${dict.Model}Statuses = ["active"] as const;
2767
-
2768
- export type \${dict.Model}Status = (typeof \${dict.Model}Statuses)[number];
2769
-
2770
-
2771
-
2772
- //\uB370\uC774\uD130\uB97C \uB9CC\uB4E4 \uB54C \uD544\uC694\uD55C \uB370\uC774\uD130 \uD0C0\uC785\uC744 \uC815\uC758\uD558\uB294 \uACF3
2773
-
2774
- @Model.Input("\${dict.Model}Input")
2775
-
2776
- export class \${dict.Model}Input {{
2777
-
2778
- @Field.Prop(() => String)
2779
-
2780
- field: string;
2781
-
2782
-
2783
-
2784
- @Field.Prop(() => Int, {{ nullable: true }})
2785
-
2786
- fieldInt: number | null;
2787
-
2788
-
2789
-
2790
- @Field.Prop(() => Date, {{default: dayjs()}})
2791
-
2792
- fieldInt: Dayjs;
2793
-
2794
- }}
2795
-
2796
-
2797
-
2798
- //\uB370\uC774\uD130\uAC00 \uB9CC\uB4E4\uC5B4\uC9C4 \uC774\uD6C4 \uC0DD\uC131\uB418\uAC70\uB098 \uC800\uC7A5\uC73C\uB85C \uCD94\uAC00\uD560 \uC218 \uC788\uB294 \uACF3
2799
-
2800
- @Model.Object("\${dict.Model}Object")
2801
-
2802
- export class \${dict.Model}Object extends via(\${dict.Model}Input) {
2803
-
2804
- @Field.Prop(() => String, {{ enum: \${dict.Model}Statuses, default: "active" }})
2805
-
2806
- status: \${dict.Model}Status;
2807
-
2808
- }}
2809
-
2810
-
2811
-
2812
- //\uB370\uC774\uD130\uB97C \uB9AC\uC2A4\uD2B8\uB85C \uBCF4\uC5EC\uC904 \uB54C \uD544\uC694\uD55C \uB370\uC774\uD130\uB9CC \uC81C\uACF5\uD558\uB294 \uACF3
2813
-
2814
- @Model.Light("Light\${dict.Model}")
2815
-
2816
- export class Light\${dict.Model} extends via(\${dict.Model}Object, [
2817
-
2818
- "field",
2819
-
2820
- "status",
2821
-
2822
- ] as const) {}
2823
-
2824
-
2825
-
2826
- //\uCD5C\uC885 \uB370\uC774\uD130\uC758 Full \uD0C0\uC785\uC774 \uC815\uC758\uB418\uB294 \uACF3
2827
-
2828
- @Model.Full("\${dict.Model}")
2829
-
2830
- export class \${dict.Model} extends via(\${dict.Model}Object, Light\${dict.Model}) {}
2831
-
2832
-
2833
-
2834
- //\uB370\uC774\uD130\uC758 \uC778\uC0AC\uC774\uD2B8\uB97C \uCE21\uC815\uD558\uAE30 \uC704\uD574\uC11C \uD544\uC694\uD55C \uB370\uC774\uD130\uB97C \uC815\uC758\uD558\uB294 \uACF3
2835
-
2836
- @Model.Insight("\${dict.Model}Insight")
2837
-
2838
- export class \${dict.Model}Insight {{
2839
-
2840
- @Field.Prop(() => Int, {{ default: 0, accumulate: {{ $sum: 1 }} }})
2841
-
2842
- count: number;
2843
-
2844
- }}
2845
-
2846
-
2847
-
2848
- //\uB370\uC774\uD130\uC758 \uD1B5\uACC4\uB97C \uACC4\uC0B0\uD558\uB294 \uACF3
2849
-
2850
- @Model.Summary("\${dict.Model}Summary")
2851
-
2852
- export class \${dict.Model}Summary {{
2853
-
2854
- @Field.Prop(() => Int, {{ min: 0, default: 0, query: {{ status: {{}} }} }})
2855
-
2856
- total\${dict.Model}: number;
2857
-
2858
- }}
2859
-
2860
-
2861
-
2862
- @Model.Filter("\${dict.Model}Filter")
2863
-
2864
- export class \${dict.Model}Filter extends sortOf(\${dict.Model}, {}) {}
2865
-
2866
- \`\`\`
2867
-
2868
-
2869
-
2870
-
2871
- model.dictonary.ts
2872
-
2873
- \`\`\`
2874
-
2875
- import {
2876
-
2877
- baseTrans,
2878
-
2879
- getBaseSignalTrans,
2880
-
2881
- ModelDictionary,
2882
-
2883
- SignalDictionary,
2884
-
2885
- SummaryDictionary,
2886
-
2887
- } from "@akanjs/dictionary";
2888
-
2889
-
2890
-
2891
- import type { \${dict.Model}, \${dict.Model}Filter, \${dict.Model}Insight, \${dict.Model}Summary } from "./\${dict.model}.constant";
2892
-
2893
- import type { \${dict.Model}Signal } from "./\${dict.model}.signal";
2894
-
2895
-
2896
-
2897
- const modelDictionary = {{
2898
-
2899
- ...baseTrans,
2900
-
2901
- modelName: ["\${dict.Model}", "\${dict.Model}"],
2902
-
2903
- modelDesc: [
2904
-
2905
- "\${dict.Model} description",
2906
-
2907
- "\${dict.Model} \uC124\uBA85",
2908
-
2909
- ],
2910
-
2911
-
2912
-
2913
- // * ==================== Model ==================== * //
2914
-
2915
- field: ["Field", "\uD544\uB4DC"],
2916
-
2917
- "desc-field": ["Field", "\uD544\uB4DC"],
2918
-
2919
- // * ==================== Model ==================== * //
2920
-
2921
-
2922
-
2923
- // * ==================== Insight ==================== * //
2924
-
2925
- count: ["Count", "\uAC1C\uC218"],
2926
-
2927
- "desc-count": ["\${dict.Model} count in current query settting", "\uD604\uC7AC \uCFFC\uB9AC \uC124\uC815\uC5D0 \uB9DE\uB294 \${dict.Model} \uC218"],
2928
-
2929
- // * ==================== Insight ==================== * //
2930
-
2931
-
2932
-
2933
- // * ==================== Filter ==================== * //
2934
-
2935
- // * ==================== Filter ==================== * //
2936
-
2937
-
2938
-
2939
- // * ==================== Etc ==================== * //
2940
-
2941
- "enum-status-active": ["Active", "\uD65C\uC131"],
2942
-
2943
- "enumdesc-status-active": ["Active status", "\uD65C\uC131 \uC0C1\uD0DC"],
2944
-
2945
- // * ==================== Etc ==================== * //
2946
-
2947
- }} satisfies ModelDictionary<\${dict.Model}, \${dict.Model}Insight, typeof \${dict.Model}Sort>;
2948
-
2949
-
2950
-
2951
- export const \${dict.Model}SummaryDictionary = {{
2952
-
2953
- // * ==================== Summary ==================== * //
2954
-
2955
- total\${dict.Model}: ["Total \${dict.Model}", "\uCD1D \${dict.Model} \uC218"],
2956
-
2957
- "desc-total\${dict.Model}": ["Total \${dict.Model} count in the database", "\uB370\uC774\uD130\uBCA0\uC774\uC2A4\uC5D0 \uC800\uC7A5\uB41C \uCD1D \${dict.Model} \uC218"],
2958
-
2959
- // * ==================== Summary ==================== * //
2960
-
2961
- }} satisfies SummaryDictionary<\${dict.Model}Summary>;
2962
-
2963
-
2964
-
2965
- const signalDictionary = {{
2966
-
2967
- ...getBaseSignalTrans("\${dict.Model}" as const),
2968
-
2969
- // * ==================== Endpoint ==================== * //
2970
-
2971
- "api-\${dict.Model}ListInPublic": ["\${dict.Model} List In Public", "\uACF5\uAC1C\uB41C \${dict.Model} \uB9AC\uC2A4\uD2B8"],
2972
-
2973
- "apidesc-\${dict.Model}ListInPublic": ["Get a list of public \${dict.Model}", "\uACF5\uAC1C\uB41C \${dict.Model}\uC758 \uB9AC\uC2A4\uD2B8\uB97C \uAC00\uC838\uC635\uB2C8\uB2E4"],
2974
-
2975
- "arg-\${dict.Model}ListInPublic-statuses": ["Statuses", "\uC0C1\uD0DC"],
2976
-
2977
- "argdesc-\${dict.Model}ListInPublic-statuses": ["Statuses to filter", "\uD544\uD130\uB9C1\uD560 \uC0C1\uD0DC"],
2978
-
2979
- "arg-\${dict.Model}ListInPublic-skip": ["Skip", "\uAC74\uB108\uB6F0\uAE30"],
2980
-
2981
- "argdesc-\${dict.Model}ListInPublic-skip": ["Number of items to skip", "\uAC74\uB108\uB6F8 \uC544\uC774\uD15C \uC218"],
2982
-
2983
- "arg-\${dict.Model}ListInPublic-limit": ["Limit", "\uC81C\uD55C"],
2984
-
2985
- "argdesc-\${dict.Model}ListInPublic-limit": ["Maximum number of items to return", "\uBC18\uD658\uD560 \uCD5C\uB300 \uC544\uC774\uD15C \uC218"],
2986
-
2987
- "arg-\${dict.Model}ListInPublic-sort": ["Sort", "\uC815\uB82C"],
2988
-
2989
- "argdesc-\${dict.Model}ListInPublic-sort": ["Sort order of the items", "\uC544\uC774\uD15C\uC758 \uC815\uB82C \uC21C\uC11C"],
2990
-
2991
-
2992
-
2993
- "api-\${dict.Model}InsightInPublic": ["\${dict.Model} Insight In Public", "\uACF5\uAC1C\uB41C \${dict.Model} \uC778\uC0AC\uC774\uD2B8"],
2994
-
2995
- "apidesc-\${dict.Model}InsightInPublic": [
2996
-
2997
- "Get insight data for public \${dict.Model}",
2998
-
2999
- "\uACF5\uAC1C\uB41C \${dict.Model}\uC5D0 \uB300\uD55C \uC778\uC0AC\uC774\uD2B8 \uB370\uC774\uD130\uB97C \uAC00\uC838\uC635\uB2C8\uB2E4",
3000
-
3001
- ],
3002
-
3003
- "arg-\${dict.Model}InsightInPublic-statuses": ["Statuses", "\uC0C1\uD0DC"],
3004
-
3005
- "argdesc-\${dict.Model}InsightInPublic-statuses": ["Statuses to filter", "\uD544\uD130\uB9C1\uD560 \uC0C1\uD0DC"],
3006
-
3007
- // * ==================== Endpoint ==================== * //
3008
-
3009
- }} satisfies SignalDictionary<\${dict.Model}Signal, \${dict.Model}>;
3010
-
3011
- \`\`\`
3012
-
3013
- export const \${dict.model}Dictionary = {{ ...modelDictionary, ...signalDictionary }};
3014
-
3015
-
3016
-
3017
-
3018
-
3019
- model.document.ts
3020
-
3021
- \`\`\`
3022
-
3023
- import { beyond, by, Database, into, type SchemaOf } from "@akanjs/document";
3024
-
3025
-
3026
-
3027
- import { cnst } from "../cnst";
3028
-
3029
- @Database.Input(() => cnst.\${dict.Model}Input)
3030
-
3031
- export class \${dict.Model}Input extends by(cnst.\${dict.Model}Input) {}
3032
-
3033
-
3034
-
3035
- @Database.Document(() => cnst.\${dict.Model})
3036
-
3037
- export class \${dict.Model} extends by(cnst.\${dict.Model}) {}
3038
-
3039
-
3040
-
3041
- @Database.Model(() => cnst.\${dict.Model})
3042
-
3043
- export class \${dict.Model}Model extends into(\${dict.Model}, cnst.\${dict.Model}Cnst) {
3044
-
3045
- async getSummary(): Promise<cnst.\${dict.Model}Summary> {
3046
-
3047
- return {
3048
-
3049
- ...(await this.getDefaultSummary()),
3050
-
3051
- };
3052
-
3053
- }
3054
-
3055
- }
3056
-
3057
-
3058
-
3059
- @Database.Middleware(() => cnst.\${dict.Model})
3060
-
3061
- export class \${dict.Model}Middleware extends beyond(\${dict.Model}Model, \${dict.Model}) {
3062
-
3063
- onSchema(schema: SchemaOf<\${dict.Model}Model, \${dict.Model}>) {
3064
-
3065
- // schema.index({ status: 1 })
3066
-
3067
- }
3068
-
3069
- }
3070
-
3071
- \`\`\`
3072
-
3073
-
3074
-
3075
-
3076
- model.signal.ts
3077
-
3078
- \`\`\`
3079
-
3080
- import { Int } from "@akanjs/base";
3081
-
3082
- import { SortOf } from "@akanjs/constant";
3083
-
3084
- import { Arg, DbSignal, Mutation, Query, resolve, Signal } from "@akanjs/signal";
3085
-
3086
-
3087
-
3088
- import { cnst, Srvs } from "../cnst";
3089
-
3090
-
3091
-
3092
- @Signal(() => cnst.\${dict.Model})
3093
-
3094
- export class \${dict.Model}Signal extends DbSignal(cnst.\${dict.model}Cnst, Srvs, {
3095
-
3096
- guards: { get: Query.Public, cru: Mutation.Public },
3097
-
3098
- }) {
3099
-
3100
- // * /////////////////////////////////////
3101
-
3102
- // * Public Slice
3103
-
3104
- @Query.Public(() => [cnst.\${dict.Model}])
3105
-
3106
- async \${dict.model}ListInPublic(
3107
-
3108
- @Arg.Query("statuses", () => [String], { nullable: true }) statuses: cnst.\${dict.Model}Status[] | null,
3109
-
3110
- @Arg.Query("skip", () => Int, { nullable: true }) skip: number | null,
3111
-
3112
- @Arg.Query("limit", () => Int, { nullable: true }) limit: number | null,
3113
-
3114
- @Arg.Query("sort", () => String, { nullable: true }) sort: SortOf<cnst.\${dict.Model}Filter> | null
3115
-
3116
- ) {
3117
-
3118
- const \${dict.models} = await this.\${dict.model}Service.listByStatuses(statuses, { skip, limit, sort });
3119
-
3120
- return resolve<cnst.\${dict.Model}[]>(\${dict.models});
3121
-
3122
- }
3123
-
3124
- @Query.Public(() => cnst.\${dict.Model}Insight)
3125
-
3126
- async \${dict.model}InsightInPublic(
3127
-
3128
- @Arg.Query("statuses", () => [String], { nullable: true }) statuses: cnst.\${dict.Model}Status[] | null
3129
-
3130
- ) {
3131
-
3132
- const \${dict.model}Insight = await this.\${dict.model}Service.insightByStatuses(statuses);
3133
-
3134
- return resolve<cnst.\${dict.Model}Insight>(\${dict.model}Insight);
3135
-
3136
- }
3137
-
3138
- // * Public Slice
3139
-
3140
- // * /////////////////////////////////////
3141
-
3142
- }
3143
-
3144
- \`\`\`
3145
-
3146
-
3147
-
3148
-
3149
- model.service.ts
3150
-
3151
- \`\`\`
3152
-
3153
- import { DbService, Service } from "@akanjs/service";
3154
-
3155
-
3156
-
3157
- import { cnst } from "../cnst";
3158
-
3159
- import * as db from "../db";
3160
-
3161
-
3162
-
3163
- @Service("\${dict.Model}Service")
3164
-
3165
- export class \${dict.Model}Service extends DbService(db.\${dict.Model}Db) {
3166
-
3167
- async summarize(): Promise<cnst.\${dict.Model}Summary> {
3168
-
3169
- return {
3170
-
3171
- ...(await this.\${dict.Model}Model.getSummary()),
3172
-
3173
- };
3174
-
3175
- }
3176
-
3177
- }
3178
-
3179
- \`\`\`
3180
-
3181
-
3182
-
3183
-
3184
- model.store.ts
3185
-
3186
- \`\`\`
3187
-
3188
- import { stateOf, Store } from "@akanjs/store";
3189
-
3190
-
3191
-
3192
- import { cnst } from "../cnst";
3193
-
3194
- import { fetch } from "../fetch";
3195
-
3196
-
3197
-
3198
- @Store(() => cnst.\${dict.Model})
3199
-
3200
- export class \${dict.Model}Store extends stateOf(fetch.\${dict.model}Gql, {
3201
-
3202
- // state
3203
-
3204
- }) {
3205
-
3206
- // action
3207
-
3208
- }
3209
-
3210
- \`\`\`
3211
-
3212
-
3213
-
3214
-
3215
- model.Zone.tsx
3216
-
3217
- \`\`\`
3218
-
3219
- "use client";
3220
-
3221
- import { Data, Load } from "@shared/ui";
3222
-
3223
- import { ModelsProps } from "@akanjs/client";
3224
-
3225
- import { cnst, \${dict.Model} } from "@\${dict.appName}/client";
3226
-
3227
- import { ClientInit, ClientView, DefaultOf } from "@akanjs/signal";
3228
-
3229
-
3230
-
3231
- export const Admin = ({ sliceName = "\${dict.model}", init, query }: ModelsProps<cnst.\${dict.Model}>>) => {
3232
-
3233
- return (
3234
-
3235
- <Data.ListContainer
3236
-
3237
- init={init}
3238
-
3239
- query={query}
3240
-
3241
- sliceName={sliceName}
3242
-
3243
- renderItem={\${dict.Model}.Unit.Card}
3244
-
3245
- renderDashboard={\${dict.Model}.Util.Stat}
3246
-
3247
- renderInsight={\${dict.Model}.Util.Insight}
3248
-
3249
- renderTemplate={\${dict.Model}.Template.General}
3250
-
3251
- renderTitle={(\${dict.model}: DefaultOf<cnst.\${dict.Model}>>) => \`\${dict.Model} - \${\${dict.model}.id ? \${dict.model}.id : "New"}\`}
3252
-
3253
- renderView={(\${dict.model}: cnst.\${dict.Model}>) => <\${dict.Model}.View.General \${dict.model}={\${dict.model}} />}
3254
-
3255
- columns={[
3256
-
3257
- "id",
3258
-
3259
- "status",
3260
-
3261
- "createdAt",
3262
-
3263
- "updatedAt",
3264
-
3265
- ]}
3266
-
3267
- actions={(\${dict.model}: cnst.Light\${dict.Model}, idx) => ["remove", "edit", "view"]}
3268
-
3269
- />
3270
-
3271
- );
3272
-
3273
- };
3274
-
3275
-
3276
-
3277
- interface CardProps {
3278
-
3279
- className?: string;
3280
-
3281
- init: ClientInit<"\${dict.model}", cnst.Light\${dict.Model}>;
3282
-
3283
- }
3284
-
3285
- export const Card = ({ className, init }: CardProps) => {
3286
-
3287
- return (
3288
-
3289
- <Load.Units
3290
-
3291
- className={className}
3292
-
3293
- init={init}
3294
-
3295
- renderItem={(\${dict.model}: cnst.Light\${dict.Model}) => (
3296
-
3297
- <\${dict.Model}.Unit.Card key={\${dict.model}.id} href={\`/\${dict.model}/\${\${dict.model}.id}\`} \${dict.model}={\${dict.model}} />
3298
-
3299
- )}
3300
-
3301
- />
3302
-
3303
- );
3304
-
3305
- };
3306
-
3307
-
3308
-
3309
- interface ViewProps {
3310
-
3311
- className?: string;
3312
-
3313
- view: ClientView<"\${dict.model}", cnst.\${dict.Model}>;
3314
-
3315
- }
3316
-
3317
- export const View = ({ view }: ViewProps) => {
3318
-
3319
- return <Load.View view={view} renderView={(\${dict.model}) => <\${dict.Model}.View.General \${dict.model}={\${dict.model}} />} />;
3320
-
3321
- };
3322
-
3323
- \`\`\`
3324
-
3325
-
3326
-
3327
- model.Templete.tsx
3328
-
3329
- \`\`\`
3330
-
3331
- "use client";
3332
-
3333
- import { cnst, st, usePage } from "@\${dict.appName}/client";
3334
-
3335
- import { Field } from "@shared/ui";
3336
-
3337
- import { Layout } from "@util/ui";
3338
-
3339
-
3340
-
3341
- interface \${dict.Model}EditProps {
3342
-
3343
- \${dict.model}Id?: string | null;
3344
-
3345
- }
3346
-
3347
-
3348
-
3349
- export const General = ({ \${dict.model}Id = undefined }: \${dict.Model}EditProps) => {
3350
-
3351
- const \${dict.model}Form = st.use.\${dict.model}Form();
3352
-
3353
- const { l } = usePage();
3354
-
3355
- return (
3356
-
3357
- <Layout.Template>
3358
-
3359
- <Field.Text
3360
-
3361
- label={l.field("\${dict.model}", "id")}
3362
-
3363
- desc={l.desc("\${dict.model}", "id")}
3364
-
3365
- value={\${dict.model}Form.id}
3366
-
3367
- onChange={st.do.setIdOn\${dict.Model}}
3368
-
3369
- />
3370
-
3371
- </Layout.Template>
3372
-
3373
- );
3374
-
3375
- };
3376
-
3377
- \`\`\`
3378
-
3379
-
3380
-
3381
-
3382
- model.Unit.tsx
3383
-
3384
- \`\`\`
3385
-
3386
- import { ModelProps } from "@akanjs/client";
3387
-
3388
- import { cnst, \${dict.Model} } from "@\${dict.appName}/client";
3389
-
3390
- import { Link } from "@util/ui";
3391
-
3392
-
3393
-
3394
- export const Card = ({ \${dict.model}, href }: ModelProps<"\${dict.model}", cnst.Light\${dict.Model}>>) => {
3395
-
3396
- return (
3397
-
3398
- <Link href={href} className="animate-fadeIn w-full h-36 flex rounded-lg shadow-sm hover:shadow-lg duration-300">
3399
-
3400
- <div>{\${dict.model}.id}</div>
3401
-
3402
- </Link>
3403
-
3404
- );
3405
-
3406
- };
3407
-
3408
- \`\`\`
3409
-
3410
-
3411
-
3412
-
3413
- model.Util.tsx
3414
-
3415
- \`\`\`
3416
-
3417
- "use client";
3418
-
3419
- import { ModelDashboardProps, ModelInsightProps } from "@akanjs/client";
3420
-
3421
- import { getQueryMap } from "@akanjs/constant";
3422
-
3423
- import { cnst } from "@\${dict.appName}/client";
3424
-
3425
- import { Data } from "@shared/ui";
3426
-
3427
-
3428
-
3429
- export const Stat = ({
3430
-
3431
- className,
3432
-
3433
- summary,
3434
-
3435
- sliceName = "\${dict.model}",
3436
-
3437
- queryMap = getQueryMap(cnst.\${dict.Model}Summary),
3438
-
3439
- hidePresents,
3440
-
3441
- }: ModelDashboardProps<cnst.Summary>) => {
3442
-
3443
- return (
3444
-
3445
- <Data.Dashboard
3446
-
3447
- className={className}
3448
-
3449
- summary={summary}
3450
-
3451
- sliceName={sliceName}
3452
-
3453
- queryMap={queryMap}
3454
-
3455
- columns={["total\${dict.Model}"]}
3456
-
3457
- hidePresents={hidePresents}
3458
-
3459
- />
3460
-
3461
- );
3462
-
3463
- };
3464
-
3465
-
3466
-
3467
- export const Insight = ({
3468
-
3469
- className,
3470
-
3471
- insight,
3472
-
3473
- sliceName = "\${dict.model}",
3474
-
3475
- }: ModelInsightProps<cnst.\${dict.Model}Insight>) => {
3476
-
3477
- return (
3478
-
3479
- <Data.Insight
3480
-
3481
- className={className}
3482
-
3483
- insight={insight}
3484
-
3485
- sliceName={sliceName}
3486
-
3487
- columns={["count"]}
3488
-
3489
- />
3490
-
3491
- );
3492
-
3493
- };
3494
-
3495
- \`\`\`
3496
-
3497
-
3498
-
3499
-
3500
- model.View.tsx
3501
-
3502
- \`\`\`
3503
-
3504
- import { clsx } from "@akanjs/client";
3505
-
3506
- import { cnst } from "@\${dict.appName}/client";
3507
-
3508
- import { Image } from "@util/ui";
3509
-
3510
-
3511
-
3512
- interface \${dict.Model}ViewProps {
3513
-
3514
- className?: string;
3515
-
3516
- \${dict.model}: cnst.\${dict.Model};
3517
-
3518
- self?: { id?: string } | null;
3519
-
3520
- }
3521
-
3522
-
3523
-
3524
- export const General = ({ className, \${dict.model}, self }: \${dict.Model}ViewProps) => {
3525
-
3526
- return (
3527
-
3528
- <div className={clsx(className, "animate-fadeIn w-full")}>
3529
-
3530
- <div>{\${dict.model}.id}</div>
3531
-
3532
- </div>
3533
-
3534
- );
3535
-
3536
- };
3537
-
3538
- \`\`\`
3539
-
3540
- \uC774\uB7F0\uC2DD\uC73C\uB85C \uC6B0\uB9AC\uAC00 \uC9C1\uC811 \uB9CC\uB4E0 \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uAE30\uBC18\uD574\uC11C db\uC640 \uC11C\uBE44\uC2A4 \uC2A4\uD1A0\uC5B4\uAE4C\uC9C0 \uBAA8\uB450 \uC5F0\uB3D9\uD574\uC11C \uD558\uB098\uC758 \uBAA8\uB378\uC5D0 \uB9DE\uAC8C\uB054 \uBAA8\uB4C8\uC744 \uD55C \uD3F4\uB354\uC5D0\uC11C \uC791\uC5C5\uD560 \uC218 \uC788\uAC8C\uB054 \uAD6C\uC870\uB97C \uB9CC\uB4E4\uC5C8\uC5B4.
3541
-
3542
-
3543
-
3544
- \uC77C\uB2E8 \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uC124\uBA85\uC740 \uC774 \uC815\uB3C4\uB85C \uD558\uACE0 \uC774 \uC815\uBCF4\uB4E4\uC744 \uAE30\uBC18\uC73C\uB85C \uB0B4\uAC00 \uC6D0\uD558\uB294 \uC694\uAD6C\uB97C \uB4E4\uC5B4\uC918.
3545
-
3546
2535
  `;
3547
2536
  var moduleDesription = `
3548
2537
 
@@ -3978,6 +2967,767 @@ var utilUiDescription = `
3978
2967
  - Modern React patterns (hooks, context, providers)
3979
2968
  - Error handling and validation systems
3980
2969
  `;
2970
+ var shardUiDescription = `
2971
+ Shared UI Library Analysis
2972
+
2973
+ Library Overview
2974
+
2975
+ The shared UI library in the akansoft project is a comprehensive React component collection designed for building data-driven
2976
+ applications. It provides a complete toolkit with consistent patterns, extensive customization options, and deep integration with the
2977
+ application's store system.
2978
+
2979
+ Key Characteristics
2980
+
2981
+ - Store-Centric Architecture: Deep integration with standardized store slices and naming conventions
2982
+ - Type-Safe Components: Extensive TypeScript generic usage for compile-time safety
2983
+ - Internationalization Ready: Built-in multi-language support throughout all components
2984
+ - Responsive Design: Mobile-first approach with adaptive breakpoints
2985
+ - Performance Optimized: Memoization, lazy loading, and efficient re-rendering
2986
+ - Accessibility Compliant: Proper ARIA attributes and keyboard navigation
2987
+ - Extensible Architecture: Plugin systems and customizable renderers
2988
+ - Unidirectional Data Flow: Predictable state management patterns
2989
+
2990
+ Component Categories
2991
+
2992
+ 1. Field Components (Field.tsx)
2993
+
2994
+ Field.tsx is a core form component library for the akansoft project, providing more than 25 specialized input fields.
2995
+
2996
+ ## Basic Structure and Common Features
2997
+
2998
+ ### Label Component
2999
+
3000
+ \`\`\`typescript
3001
+ interface LabelProps {
3002
+ className?: string;
3003
+ label: string;
3004
+ desc?: string;
3005
+ unit?: string;
3006
+ nullable?: boolean;
3007
+ mode?: "view" | "edit";
3008
+ }
3009
+ \`\`\`
3010
+ - Purpose: Consistent label display for all fields
3011
+ - Features:
3012
+ - Help tooltip support (desc)
3013
+ - Unit display (unit)
3014
+ - Optional field display (nullable)
3015
+ - Required indicator is commented out (design decision)
3016
+
3017
+ ## Text Input Components
3018
+
3019
+ ### 1. Field.Text
3020
+
3021
+ \`\`\`typescript
3022
+ interface TextProps {
3023
+ label?: string;
3024
+ desc?: string;
3025
+ value: string | null;
3026
+ onChange: (value: string) => void;
3027
+ placeholder?: string;
3028
+ nullable?: boolean;
3029
+ disabled?: boolean;
3030
+ minlength?: number; // Default: nullable ? 0 : 2
3031
+ maxlength?: number; // Default: 200
3032
+ transform?: (value: string) => string;
3033
+ validate?: (text: string) => boolean | string;
3034
+ cache?: boolean; // Form caching support
3035
+ onPressEnter?: () => void;
3036
+ inputStyleType?: "bordered" | "borderless" | "underline";
3037
+ }
3038
+ \`\`\`
3039
+ Key features:
3040
+ - Real-time transformation (transform)
3041
+ - Automatic cache key generation: \${label}-\${desc}-text
3042
+ - Length validation and custom validation
3043
+ - Various input styles supported
3044
+
3045
+ ### 2. Field.TextArea
3046
+
3047
+ \`\`\`typescript
3048
+ interface TextAreaProps {
3049
+ // Same basic props as Text +
3050
+ rows?: number; // Default: 3
3051
+ maxlength?: number; // Default: 1000
3052
+ }
3053
+ \`\`\`
3054
+ Key features:
3055
+ - Multi-line text input
3056
+ - Adjustable height (rows)
3057
+ - Longer maximum length than Text
3058
+
3059
+ ### 3. Field.Price (to be deprecated)
3060
+
3061
+ \`\`\`typescript
3062
+ interface PriceProps {
3063
+ // Similar to Text but specialized for prices
3064
+ maxlength?: number; // Default: 80
3065
+ placeholder?: string; // Default: "~ \${l('shared.priceUnit')}"
3066
+ }
3067
+ \`\`\`
3068
+ Key features:
3069
+ - Automatic removal of commas and spaces
3070
+ - Automatic price unit placeholder
3071
+ - Marked as "delete" in comments - legacy component
3072
+
3073
+ ## List Management Components
3074
+
3075
+ ### 4. Field.List
3076
+
3077
+ \`\`\`typescript
3078
+ interface ListProps<Item> {
3079
+ label?: string;
3080
+ value: Item[];
3081
+ onChange: (value: Item[]) => void;
3082
+ onAdd: () => void;
3083
+ renderItem: (item: Item, idx: number) => ReactNode;
3084
+ }
3085
+ \`\`\`
3086
+ Key features:
3087
+ - Generic type supporting all item types
3088
+ - Custom rendering for each item
3089
+ - Automatic delete button addition
3090
+ - Automatic divider insertion
3091
+
3092
+ ### 5. Field.TextList
3093
+
3094
+ \`\`\`typescript
3095
+ interface TextListProps {
3096
+ value: string[];
3097
+ onChange: (value: string[]) => void;
3098
+ minlength?: number; // Minimum array length, Default: 0
3099
+ maxlength?: number; // Maximum array length, Default: 50
3100
+ minTextlength?: number; // Minimum individual text length, Default: 2
3101
+ maxTextlength?: number; // Maximum individual text length, Default: 200
3102
+ transform?: (value: string) => string;
3103
+ validate?: (text: string) => boolean | string;
3104
+ cache?: boolean;
3105
+ }
3106
+ \`\`\`
3107
+ Key features:
3108
+ - Drag and drop for order changes
3109
+ - Individual text input validation
3110
+ - Cache key: \${label}-\${desc}-textList-[\${idx}]
3111
+ - Conditional "New" button display based on maximum length limit
3112
+
3113
+ ### 6. Field.Tags
3114
+
3115
+ \`\`\`typescript
3116
+ interface TagsProps {
3117
+ value: string[];
3118
+ onChange: (value: string[]) => void;
3119
+ minlength?: number; // Default: 0
3120
+ maxlength?: number; // Default: 50
3121
+ minTextlength?: number; // Default: 2
3122
+ maxTextlength?: number; // Default: 10 (tags are shorter)
3123
+ transform?: (value: string) => string;
3124
+ validate?: (text: string) => boolean | string;
3125
+ }
3126
+ \`\`\`
3127
+ Key features:
3128
+ - Tag style UI (includes # prefix)
3129
+ - Inline editing mode
3130
+ - Creates additional input field when clicked
3131
+ - Cancel editing with ESC key
3132
+ - Complete tag addition with Enter/Blur
3133
+
3134
+ ## Numeric Input Components
3135
+
3136
+ ### 7. Field.Number
3137
+
3138
+ \`\`\`typescript
3139
+ interface NumberProps {
3140
+ value: number | null;
3141
+ onChange: (value: number) => void;
3142
+ min?: number;
3143
+ max?: number;
3144
+ unit?: string;
3145
+ transform?: (value: number) => number;
3146
+ validate?: (text: number) => boolean | string;
3147
+ formatter?: (value: string) => string; // Display format
3148
+ parser?: (value: string) => string; // Parsing format
3149
+ cache?: boolean;
3150
+ }
3151
+ \`\`\`
3152
+ Key features:
3153
+ - Automatic number validation and min/max checks
3154
+ - Customizable display format with formatter/parser
3155
+ - Unit display support
3156
+ - Cache key: \${label}-\${desc}-number
3157
+
3158
+ ### 8. Field.DoubleNumber
3159
+
3160
+ \`\`\`typescript
3161
+ interface DoubleNumberProps {
3162
+ value: [number, number] | null;
3163
+ onChange: (value: [number, number]) => void;
3164
+ min?: [number, number] | null;
3165
+ max?: [number, number] | null;
3166
+ separator?: ReactNode | string; // Separator between inputs
3167
+ }
3168
+ \`\`\`
3169
+ Key features:
3170
+ - Range input (start-end values)
3171
+ - Independent min/max validation for each
3172
+ - Custom separator (e.g., "~", "-")
3173
+ - Cache keys: \${label}-\${desc}-number-[0], \${label}-\${desc}-number-[1]
3174
+
3175
+ ## Selection Components
3176
+
3177
+ ### 9. Field.Switch
3178
+
3179
+ \`\`\`typescript
3180
+ interface SwitchProps {
3181
+ value: boolean;
3182
+ onChange: (value: boolean) => void;
3183
+ onDesc?: string; // Description when true
3184
+ offDesc?: string; // Description when false
3185
+ disabled?: boolean;
3186
+ }
3187
+ \`\`\`
3188
+ Key features:
3189
+ - DaisyUI toggle style
3190
+ - State-specific description text
3191
+ - Uses toggle-accent class
3192
+
3193
+ ### 10. Field.ToggleSelect
3194
+
3195
+ \`\`\`typescript
3196
+ interface ToggleSelectProps<I> {
3197
+ items: { label: string; value: I }[] | readonly I[] | I[] | Enum<I>;
3198
+ value: I | null;
3199
+ onChange: (value: I) => void;
3200
+ model?: string; // Model name for internationalization
3201
+ field?: string; // Field name for internationalization
3202
+ validate?: (value: I) => boolean | string;
3203
+ btnClassName?: string;
3204
+ }
3205
+ \`\`\`
3206
+ Key features:
3207
+ - Button-style single selection
3208
+ - Direct Enum type support
3209
+ - Automatic internationalization (l.enum(model, field, item))
3210
+ - Custom button styling
3211
+
3212
+ ### 11. Field.MultiToggleSelect
3213
+
3214
+ \`\`\`typescript
3215
+ interface MultiToggleSelectProps<I> {
3216
+ items: Enum<I> | { label: string; value: I }[] | readonly I[] | I[];
3217
+ value: I[];
3218
+ onChange: (value: I[]) => void;
3219
+ minlength?: number;
3220
+ maxlength?: number;
3221
+ validate?: (value: I[]) => boolean | string;
3222
+ }
3223
+ \`\`\`
3224
+ Key features:
3225
+ - Multiple selection button UI
3226
+ - Minimum/maximum selection limit
3227
+ - Array length validation
3228
+
3229
+ ## Relational Data Selection Components
3230
+
3231
+ ### 12. Field.Parent & Field.ParentId
3232
+
3233
+ \`\`\`typescript
3234
+ interface ParentProps<T, State, Input, Full, Light, Sort, QueryArgs> {
3235
+ sliceName: string; // Target slice name
3236
+ value: Light | null; // Parent returns object
3237
+ onChange: (value?: Light | null) => void;
3238
+ initArgs?: any[]; // Initialization arguments
3239
+ sortOption?: (a: Light, b: Light) => number;
3240
+ renderOption: (model: Light) => ReactNode; // Option renderer
3241
+ renderSelected?: (value: Light) => ReactNode; // Selected item renderer
3242
+ onSearch?: (text: string) => void; // Search handler
3243
+ }
3244
+
3245
+ interface ParentIdProps extends ParentProps {
3246
+ value: string | null; // ParentId returns only ID
3247
+ onChange: (id?: string | null, model?: Light | null) => void;
3248
+ }
3249
+ \`\`\`
3250
+ Key features:
3251
+ - Dynamic store slice integration
3252
+ - Automatic naming convention: \${modelName}List, init\${ModelName}, etc.
3253
+ - Searchable dropdown
3254
+ - Lazy loading (data fetched on onOpen)
3255
+ - Parent: returns full object, ParentId: returns only ID
3256
+
3257
+ ### 13. Field.Children & Field.ChildrenId
3258
+
3259
+ \`\`\`typescript
3260
+ interface ChildrenProps extends ParentProps {
3261
+ value: Light[]; // Multiple selection
3262
+ onChange: (value?: Light[] | null) => void;
3263
+ }
3264
+
3265
+ interface ChildrenIdProps extends ChildrenProps {
3266
+ value: string[]; // ID array
3267
+ onChange: (value: string[]) => void;
3268
+ }
3269
+ \`\`\`
3270
+ Key features:
3271
+ - Multiple relationship selection
3272
+ - Same store integration logic as Parent
3273
+ - Automatic initialization (useEffect)
3274
+
3275
+ ## Date/Time Components
3276
+
3277
+ ### 14. Field.Date
3278
+
3279
+ \`\`\`typescript
3280
+ interface DateProps<Nullable extends boolean> {
3281
+ value: Nullable extends true ? Dayjs | null : Dayjs;
3282
+ onChange: (value: Dayjs) => void;
3283
+ min?: Dayjs;
3284
+ max?: Dayjs;
3285
+ showTime?: boolean; // datetime-local vs date
3286
+ nullable?: boolean;
3287
+ }
3288
+ \`\`\`
3289
+ Key features:
3290
+ - Uses Dayjs objects
3291
+ - Conditional nullable type (using TypeScript generics)
3292
+ - Automatic format change based on showTime
3293
+ - Uses HTML5 date/datetime-local inputs
3294
+ - Includes comment about DaisyUI max value bug
3295
+
3296
+ ### 15. Field.DateRange
3297
+
3298
+ \`\`\`typescript
3299
+ interface DateRangeProps<Nullable extends boolean> {
3300
+ from: Nullable extends true ? Dayjs | null : Dayjs;
3301
+ to: Nullable extends true ? Dayjs | null : Dayjs;
3302
+ onChangeFrom: (value: Dayjs) => void;
3303
+ onChangeTo: (value: Dayjs) => void;
3304
+ onChange?: (from: Dayjs, to: Dayjs) => void; // Optional unified handler
3305
+ showTime?: boolean;
3306
+ }
3307
+ \`\`\`
3308
+ Key features:
3309
+ - Individual management of start/end dates
3310
+ - Optional unified change handler
3311
+ - Automatic "From"/"To" label display
3312
+ - Responsive layout (vertical on mobile)
3313
+
3314
+ ## File Upload Components
3315
+
3316
+ ### 16. Field.Img
3317
+
3318
+ \`\`\`typescript
3319
+ interface ImageProps {
3320
+ sliceName: string; // Determines file upload API
3321
+ value: cnst.File | null;
3322
+ onChange: (file: cnst.File | null) => void;
3323
+ styleType?: "circle" | "square"; // Default: "circle"
3324
+ aspectRatio?: number[]; // Aspect ratio restriction
3325
+ render?: (file: cnst.File) => ReactNode;
3326
+ disabled?: boolean;
3327
+ }
3328
+ \`\`\`
3329
+ Key features:
3330
+ - Dynamic upload API: add\${capitalize(sliceName)}Files
3331
+ - Upload status polling (1-second interval)
3332
+ - Aspect ratio restriction support
3333
+ - Circular/square preview
3334
+
3335
+ ### 17. Field.Imgs
3336
+
3337
+ \`\`\`typescript
3338
+ interface ImagesProps {
3339
+ sliceName: string;
3340
+ value: cnst.File[];
3341
+ onChange: (files: cnst.File[]) => void;
3342
+ minlength?: number; // Default: 1
3343
+ maxlength?: number; // Default: 30
3344
+ render?: (file: cnst.File) => ReactNode;
3345
+ }
3346
+ \`\`\`
3347
+ Key features:
3348
+ - Multiple image upload
3349
+ - Batch polling of upload status
3350
+ - Minimum/maximum file count limit
3351
+ - Fixed square style
3352
+
3353
+ ### 18. Field.File & Field.Files
3354
+
3355
+ \`\`\`typescript
3356
+ interface FileProps {
3357
+ sliceName: string;
3358
+ value: cnst.File | null; // File is single
3359
+ onChange: (file: cnst.File | null) => void;
3360
+ render?: (file: cnst.File) => ReactNode;
3361
+ }
3362
+
3363
+ interface FilesProps {
3364
+ sliceName: string;
3365
+ value: cnst.File[]; // Files is multiple
3366
+ onChange: (files: cnst.File[]) => void;
3367
+ minlength?: number; // Default: 1
3368
+ maxlength?: number; // Default: 30
3369
+ }
3370
+ \`\`\`
3371
+ Key features:
3372
+ - Support for all file types (beyond images)
3373
+ - Same upload logic as Img/Imgs
3374
+ - Custom file renderer support
3375
+
3376
+ ## Rich Text Editor
3377
+
3378
+ ### 19. Field.Slate
3379
+
3380
+ \`\`\`typescript
3381
+ interface SlateProps {
3382
+ sliceName: string; // Determines file upload API
3383
+ valuePath: string; // Store path
3384
+ onChange: (value: unknown) => void;
3385
+ addFile: (file: cnst.File | cnst.File[], options?) => void;
3386
+ placeholder?: string;
3387
+ disabled?: boolean;
3388
+ editorHeight?: string;
3389
+ }
3390
+ \`\`\`
3391
+ Key features:
3392
+ - Slate.js-based rich text editor
3393
+ - File drag and drop support
3394
+ - Dynamic store path access
3395
+ - Adjustable height
3396
+
3397
+ ### 20. Field.Yoopta
3398
+
3399
+ \`\`\`typescript
3400
+ interface YooptaProps {
3401
+ value: JSON;
3402
+ onChange: (value: JSON) => void;
3403
+ readonly?: boolean;
3404
+ }
3405
+ \`\`\`
3406
+ Key features:
3407
+ - Yoopta editor integration
3408
+ - JSON data format
3409
+ - Read-only mode support
3410
+
3411
+ ## Authentication and Contact Components
3412
+
3413
+ ### 21. Field.Email
3414
+
3415
+ \`\`\`typescript
3416
+ interface EmailProps {
3417
+ value: string | null;
3418
+ onChange: (value: string) => void;
3419
+ placeholder?: string; // Default: "example@email.com"
3420
+ minlength?: number; // Default: nullable ? 0 : 2
3421
+ maxlength?: number; // Default: 80
3422
+ inputStyleType?: "bordered" | "borderless" | "underline";
3423
+ cache?: boolean;
3424
+ }
3425
+ \`\`\`
3426
+ Key features:
3427
+ - Automatic email format validation
3428
+ - Uses Input.Email component
3429
+ - Cache key: \${label}-\${desc}-email
3430
+
3431
+ ### 22. Field.Phone
3432
+
3433
+ \`\`\`typescript
3434
+ interface PhoneProps {
3435
+ value: string | null;
3436
+ onChange: (value: string) => void;
3437
+ maxlength?: number; // Default: 13
3438
+ transform?: (value: string) => string; // Default: formatPhone
3439
+ cache?: boolean;
3440
+ }
3441
+ \`\`\`
3442
+ Key features:
3443
+ - Automatic phone number formatting (formatPhone)
3444
+ - isPhoneNumber validation
3445
+ - Cache key: \${label}-\${desc}-phone
3446
+
3447
+ ### 23. Field.Password
3448
+
3449
+ \`\`\`typescript
3450
+ interface PasswordProps {
3451
+ value: string | null;
3452
+ onChange: (value: string) => void;
3453
+ confirmValue?: string | null; // Confirmation input
3454
+ onChangeConfirm?: (value: string) => void;
3455
+ showConfirm?: boolean; // Show confirmation input
3456
+ minlength?: number; // Default: nullable ? 0 : 8
3457
+ maxlength?: number; // Default: 20
3458
+ cache?: boolean;
3459
+ }
3460
+ \`\`\`
3461
+ Key features:
3462
+ - Optional password confirmation input
3463
+ - Automatic match validation
3464
+ - Secure input (masking)
3465
+ - Cache key: \${label}-\${desc}-password
3466
+
3467
+ ## Geographic Location Components
3468
+
3469
+ ### 24. Field.Coordinate
3470
+
3471
+ \`\`\`typescript
3472
+ interface CoordinateProps {
3473
+ coordinate: cnst.util.Coordinate | null;
3474
+ onChange: (coordinate: cnst.util.Coordinate) => void;
3475
+ mapKey: string; // Google Maps API key
3476
+ mapClassName?: string;
3477
+ disabled?: boolean;
3478
+ }
3479
+ \`\`\`
3480
+ Key features:
3481
+ - Google Maps integration
3482
+ - Coordinate selection by clicking
3483
+ - Automatic marker display (AiTwotoneEnvironment icon)
3484
+ - Default zoom level 3
3485
+
3486
+ ### 25. Field.Postcode
3487
+
3488
+ \`\`\`typescript
3489
+ interface PostcodeProps {
3490
+ kakaoKey: string; // Kakao API key
3491
+ address: string | null;
3492
+ onChange: ({
3493
+ address: string;
3494
+ addressEn: string;
3495
+ zipcode: string;
3496
+ coordinate: cnst.util.Coordinate;
3497
+ }) => void;
3498
+ }
3499
+ \`\`\`
3500
+ Key features:
3501
+ - Daum postcode service (react-daum-postcode)
3502
+ - Coordinate conversion using Kakao Maps API
3503
+ - Simultaneous Korean/English address provision
3504
+ - Modal address search
3505
+
3506
+ ### 26. Field.KoreanCityDistrict
3507
+
3508
+ \`\`\`typescript
3509
+ interface KoreanCityDistrictProps {
3510
+ city: string | null;
3511
+ onChangeCity: (city: string | null) => void;
3512
+ district: string | null;
3513
+ onChangeDistrict: (district: string | null) => void;
3514
+ disabled?: boolean;
3515
+ }
3516
+ \`\`\`
3517
+ Key features:
3518
+ - Hardcoded Korean region data
3519
+ - Two-level selection (city/province \u2192 district/county)
3520
+ - Includes detailed regions: 25 districts in Seoul, 16 in Busan, etc.
3521
+ - Linked selection (district activates after city selection)
3522
+
3523
+ ## Common Patterns and Features
3524
+
3525
+ ### 1. Caching System
3526
+
3527
+ Most input components support form data persistence with the cache prop:
3528
+ cacheKey={cache ? \`\${label}-\${desc}-\${componentType}\` : undefined}
3529
+
3530
+ ### 2. Validation System
3531
+
3532
+ All components support multi-layer validation:
3533
+ - Basic validation (length, type, format)
3534
+ - Custom validation functions
3535
+ - Internationalized error messages
3536
+
3537
+ ### 3. Internationalization
3538
+
3539
+ - Labels, placeholders, error messages all use l() function
3540
+ - Automatic translation for Enum types
3541
+ - Multi-language error message templates
3542
+
3543
+ ### 4. Store Integration
3544
+
3545
+ Relational components automatically connect to the store through naming conventions:
3546
+ \`\`\`javascript
3547
+ const names = {
3548
+ model: modelName,
3549
+ modelList: \`\${modelName}List\`,
3550
+ initModel: \`init\${ModelName}\`,
3551
+ };
3552
+ \`\`\`
3553
+
3554
+ ### 5. Type Safety
3555
+
3556
+ - Type safety ensured with TypeScript generics
3557
+ - Conditional types for handling nullable properties
3558
+ - Strict props interface definitions
3559
+
3560
+ This comprehensive field library provides a consistent user experience and developer convenience, designed to make complex form compositions simple and safe.
3561
+ 2. Data Components (/Data/)
3562
+
3563
+ Purpose: Complete data visualization and management interfaces
3564
+
3565
+ ListContainer
3566
+
3567
+ - type?: "card" | "list" - Display mode selection
3568
+ - columns?: DataColumn<any>[] - Column configuration
3569
+ - tools?: DataTool[] - Toolbar actions
3570
+ - renderDashboard?: (props) => ReactNode - Dashboard customization
3571
+ - renderItem?: (props) => ReactNode - Item renderer
3572
+
3573
+ CardList
3574
+
3575
+ - sliceName: string - Store slice identifier
3576
+ - columns: DataColumn<any>[] - Data column definitions
3577
+ - actions?: DataAction<Light>[] - Item actions
3578
+ - renderItem: (args) => ReactNode - Card renderer
3579
+ - renderLoading?: () => ReactNode - Loading state
3580
+
3581
+ Dashboard
3582
+
3583
+ - summary: { [key: string]: any } - Statistics data
3584
+ - queryMap: { [key: string]: any } - Filter mappings
3585
+ - columns?: string[] - Clickable statistics
3586
+ - hidePresents?: boolean - Display control
3587
+
3588
+ 3. Load Components (/Load/)
3589
+
3590
+ Purpose: Data loading and state management with SSR/CSR support
3591
+
3592
+ Page
3593
+
3594
+ - loader: () => Promise<Return> - Data fetching function
3595
+ - render: (data: Return) => ReactNode - Content renderer
3596
+ - loading?: () => ReactNode - Loading state
3597
+
3598
+ Edit
3599
+
3600
+ - edit: ClientEdit<T, Full> | Partial<Full> - Edit data
3601
+ - type?: "modal" | "form" | "empty" - Display mode
3602
+ - sliceName: string - Store slice
3603
+ - onSubmit?: string | ((model: Full) => void) - Submit handler
3604
+
3605
+ Units
3606
+
3607
+ - init: ClientInit<T, L> - Initial data
3608
+ - filter?: (item: L, idx: number) => boolean - Item filtering
3609
+ - sort?: (a: L, b: L) => number - Sorting function
3610
+ - renderItem?: (item: L, idx: number) => ReactNode - Item renderer
3611
+ - pagination?: boolean - Pagination control
3612
+
3613
+ 4. Model Components (/Model/)
3614
+
3615
+ Purpose: CRUD operations with modal and inline editing
3616
+
3617
+ EditModal
3618
+
3619
+ - sliceName: string - Store slice identifier
3620
+ - id?: string - Model ID for editing
3621
+ - renderTitle?: ((model: Full) => ReactNode) | string - Title customization
3622
+ - onSubmit?: string | ((model: Full) => void) - Submit handler
3623
+ - onCancel?: string | ((form?: any) => any) - Cancel handler
3624
+
3625
+ ViewModal
3626
+
3627
+ - id: string - Model identifier
3628
+ - renderView: (model: any) => ReactNode - View content renderer
3629
+ - renderAction?: (model: any) => ReactNode - Action buttons
3630
+
3631
+ NewWrapper
3632
+
3633
+ - sliceName: string - Store slice
3634
+ - partial?: Partial<Full> - Default values
3635
+ - modal?: string | null - Modal type
3636
+
3637
+ 5. System Components (/System/)
3638
+
3639
+ Purpose: Application-level providers and system utilities
3640
+
3641
+ Provider (CSR)
3642
+
3643
+ - fonts: ReactFont[] - Font configurations
3644
+ - gaTrackingId?: string - Analytics tracking
3645
+ - layoutStyle?: "mobile" | "web" - Layout mode
3646
+
3647
+ Provider (SSR)
3648
+
3649
+ - fonts?: NextFont[] - Next.js font configurations
3650
+
3651
+ SelectLanguage
3652
+
3653
+ - languages?: string[] - Available languages
3654
+
3655
+ 6. Only Components (/Only/)
3656
+
3657
+ Purpose: Conditional rendering based on user state and device
3658
+
3659
+ Admin
3660
+
3661
+ - roles?: cnst.AdminRole[] - Required admin roles
3662
+
3663
+ User
3664
+
3665
+ - roles?: cnst.UserRole[] - Required user roles
3666
+
3667
+ Show
3668
+
3669
+ - show?: boolean | cnst.util.Responsive[] - Show conditions
3670
+
3671
+ Mobile/Web
3672
+
3673
+ - No props - Device-based rendering
3674
+
3675
+ 7. Editor Components (/Editor/)
3676
+
3677
+ Purpose: Rich text editing capabilities
3678
+
3679
+ Slate
3680
+
3681
+ - addFilesGql: (fileList: FileList, id?: string) => Promise<File[]> - File upload
3682
+ - addFile: (file: cnst.File | cnst.File[], options?) => void - File management
3683
+ - onChange: (value: unknown) => void - Content change handler
3684
+ - defaultValue?: unknown - Initial content
3685
+ - placeholder?: string - Placeholder text
3686
+ - disabled?: boolean - Read-only mode
3687
+
3688
+ SlateContent
3689
+
3690
+ - content: unknown - Slate content to display
3691
+
3692
+ 8. Property Component (Property.tsx)
3693
+
3694
+ Purpose: Metadata-driven property editing
3695
+
3696
+ - prop: string - Property name
3697
+ - slice: StoreOf<any, any> - Store slice
3698
+ - renderTemplate?: (form: any) => ReactNode - Custom edit renderer
3699
+ - renderView?: (model: any) => ReactNode - Custom view renderer
3700
+ - modelPath?: string - Model path in store
3701
+
3702
+ Integration Patterns
3703
+
3704
+ Store Integration
3705
+
3706
+ - Components use standardized naming conventions for store slices
3707
+ - Automatic state management through slice integration
3708
+ - Consistent error handling and loading states
3709
+
3710
+ Validation System
3711
+
3712
+ - Built-in validation for all field types
3713
+ - Custom validation function support
3714
+ - Internationalized error messages
3715
+
3716
+ File Management
3717
+
3718
+ - Integrated file upload system
3719
+ - Progress tracking and status management
3720
+ - Multiple file type support with preview
3721
+
3722
+ Responsive Behavior
3723
+
3724
+ - Mobile-first design approach
3725
+ - Adaptive layouts based on screen size
3726
+ - Touch-friendly interactions
3727
+
3728
+ This comprehensive UI library provides everything needed to build sophisticated data management interfaces with consistent user
3729
+ experience and maintainable code architecture.
3730
+ `;
3981
3731
  var frameworkAbstract = `
3982
3732
  Intro
3983
3733
  - Build an all-stack application at once.
@@ -4057,6 +3807,87 @@ Core ESLint Extensions
4057
3807
  This configuration creates a robust linting setup that enforces Next.js App Router best practices, maintains clean code
4058
3808
  organization, and ensures proper server/client code separation.
4059
3809
  `;
3810
+ var componentDefaultDescription = ({
3811
+ modelName,
3812
+ ModelName,
3813
+ exampleFiles,
3814
+ constant,
3815
+ properties
3816
+ }) => `
3817
+
3818
+
3819
+ 1. Akan.js \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uAC1C\uC694
3820
+ ${frameworkAbstract}
3821
+
3822
+ 2. Akan.js\uD504\uB808\uC784\uC6CC\uD06C \uB370\uC774\uD130 \uAE30\uBC18 \uBAA8\uB4C8\uC5D0 \uB300\uD55C \uC124\uBA85
3823
+ ${moduleDesription}
3824
+
3825
+ 3. Akan.js eslint \uC124\uC815\uC5D0 \uB300\uD55C \uC124\uBA85
3826
+ ${eslintDescription}
3827
+
3828
+ 4. util/ui \uB0B4 ui\uD0B7\uC5D0 \uB300\uD55C \uC124\uBA85
3829
+ ${utilUiDescription}
3830
+
3831
+ 5. shared/ui \uB0B4 ui\uD0B7\uC5D0 \uB300\uD55C \uC124\uBA85
3832
+ ${shardUiDescription}
3833
+
3834
+ 6. ${ModelName}.constant.ts \uD30C\uC77C\uC5D0 \uB300\uD55C \uC815\uBCF4
3835
+ ${constant}
3836
+
3837
+ 7. ${modelName}\uC5D0\uC11C \uC885\uC18D\uB418\uB294 \uB2E4\uB978 \uBAA8\uB378\uB4E4\uC758 \uD0C0\uC785 \uC815\uC758
3838
+ ${properties.map(
3839
+ (property) => `
3840
+ \`\`\`
3841
+ ${property.key}.constant.ts
3842
+
3843
+
3844
+ ${property.source}
3845
+ \`\`\`
3846
+ `
3847
+ ).join("\n\n")}
3848
+
3849
+
3850
+ 8. \uC608\uC2DC \uD30C\uC77C\uB4E4
3851
+ ${exampleFiles.map(
3852
+ (example) => `
3853
+ Example filename: ${example.filepath}
3854
+ \`\`\`
3855
+ ${example.content}
3856
+ \`\`\`
3857
+ `
3858
+ ).join("\n\n")}
3859
+
3860
+
3861
+
3862
+
3863
+
3864
+
3865
+ \uC5ED\uD560\uBD80\uC5EC
3866
+ - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
3867
+
3868
+ \uCF54\uB529 \uADDC\uCE59
3869
+ - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
3870
+ - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
3871
+ - CSS: tailwind, DaisyUI(btn, input, badge \uAC19\uC740 \uAE30\uBCF8 \uC694\uC18C\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5, card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 X) \uC0AC\uC6A9
3872
+ - Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
3873
+ - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
3874
+ \uCF54\uB4DC \uC2A4\uD0C0\uC77C
3875
+ - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
3876
+ - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
3877
+ - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
3878
+ - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
3879
+ \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
3880
+
3881
+ \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
3882
+ - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
3883
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
3884
+ - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
3885
+ - dayjs \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB294 @akanjs/base\uC5D0\uC11C \uB798\uD551\uD558\uC5EC \uC81C\uACF5\uD558\uACE0 \uC788\uC74C.
3886
+
3887
+
3888
+
3889
+
3890
+ `;
4060
3891
  var scalarConstantDescription = `
4061
3892
  Purpose and Structure
4062
3893
 
@@ -4440,7 +4271,7 @@ Target filename: ${modelName}.constant.ts
4440
4271
  ${boilerplate}
4441
4272
  \`\`\`
4442
4273
  `;
4443
- var requestView = ({
4274
+ var requestTemplate = ({
4444
4275
  sysName,
4445
4276
  modelName,
4446
4277
  ModelName,
@@ -4449,78 +4280,56 @@ var requestView = ({
4449
4280
  properties,
4450
4281
  exampleFiles
4451
4282
  }) => `
4452
-
4453
-
4454
- 1. Akan.js \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uAC1C\uC694
4455
- ${frameworkAbstract}
4456
-
4457
- 2. Akan.js\uD504\uB808\uC784\uC6CC\uD06C \uB370\uC774\uD130 \uAE30\uBC18 \uBAA8\uB4C8\uC5D0 \uB300\uD55C \uC124\uBA85
4458
- ${moduleDesription}
4459
-
4460
- 3. Akan.js eslint \uC124\uC815\uC5D0 \uB300\uD55C \uC124\uBA85
4461
- ${eslintDescription}
4462
-
4463
- 4. util/ui \uB0B4 ui\uD0B7\uC5D0 \uB300\uD55C \uC124\uBA85
4464
- ${utilUiDescription}
4465
-
4466
-
4467
-
4468
- 5. ${ModelName}.constant.ts \uD30C\uC77C\uC5D0 \uB300\uD55C \uC815\uBCF4
4469
- ${constant}
4283
+ ${componentDefaultDescription({
4284
+ sysName,
4285
+ modelName,
4286
+ ModelName,
4287
+ exampleFiles,
4288
+ constant,
4289
+ properties
4290
+ })}
4291
+ \uC694\uCCAD\uC0AC\uD56D
4292
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
4293
+ - ${ModelName}.Template.tsx \uCF54\uB4DC \uC791\uC131
4294
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC740 \uBAA8\uB378 \uC774\uB984\uC740 \uC2A4\uD0A4\uB9C8\uC5D0 \uAE30\uBC18\uD55C \uAE30\uB2A5\uC5D0 \uCD08\uC810\uC744 \uB450\uACE0 \uC791\uC131
4295
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uC788\uB294 General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
4296
+ - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
4297
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
4298
+ -
4470
4299
 
4471
- 6. ${modelName}\uC5D0\uC11C \uC885\uC18D\uB418\uB294 \uB2E4\uB978 \uBAA8\uB378\uB4E4\uC758 \uD0C0\uC785 \uC815\uC758
4472
- ${properties.map(
4473
- (property) => `
4300
+ Application name: ${sysName}
4301
+ Model name: ${modelName}
4302
+ Target filename: ${ModelName}.Template.tsx
4474
4303
  \`\`\`
4475
- ${property.key}.constant.ts
4476
4304
 
4477
-
4478
- ${property.source}
4479
- \`\`\`
4480
- `
4481
- ).join("\n\n")}
4482
-
4483
-
4484
- 7. \uC608\uC2DC \uD30C\uC77C\uB4E4
4485
- ${exampleFiles.map(
4486
- (example) => `
4487
- Example filename: ${example.filepath}
4488
- \`\`\`
4489
- ${example.content}
4490
4305
  \`\`\`
4491
- `
4492
- ).join("\n\n")}
4493
-
4494
-
4495
-
4496
-
4497
-
4498
-
4499
- \uC5ED\uD560\uBD80\uC5EC
4500
- - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C
4501
4306
 
4502
- \uCF54\uB529 \uADDC\uCE59
4503
- - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4504
- - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4505
- - CSS: DaisyUI (btn, input, badge \uAC19\uC740 \uAE30\uBCF8 \uC694\uC18C\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5, card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uBD88\uAC00)
4506
- - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4507
- \uCF54\uB4DC \uC2A4\uD0C0\uC77C
4508
- - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
4509
- - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
4510
- - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
4511
- - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
4512
- \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
4513
-
4514
- \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
4515
- - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
4516
- - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
4517
- - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
4518
-
4307
+
4308
+ `;
4309
+ var requestView = ({
4310
+ sysName,
4311
+ modelName,
4312
+ ModelName,
4313
+ boilerplate,
4314
+ constant,
4315
+ properties,
4316
+ exampleFiles
4317
+ }) => `
4318
+ ${componentDefaultDescription({
4319
+ sysName,
4320
+ modelName,
4321
+ ModelName,
4322
+ exampleFiles,
4323
+ constant,
4324
+ properties
4325
+ })}
4519
4326
  \uC694\uCCAD\uC0AC\uD56D
4520
- ${ModelName}.View.tsx \uCF54\uB4DC \uC791\uC131
4521
- \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC740 \uBAA8\uB378 \uC774\uB984\uC740 \uC81C\uC678\uD55C \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uB514\uC790\uC778\uC5D0 \uCD08\uC810\uC744 \uB450\uACE0 \uC791\uC131
4522
- \uC77C\uBC18\uC801\uC73C\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
4523
- \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
4327
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
4328
+ - ${ModelName}.View.tsx \uCF54\uB4DC \uC791\uC131
4329
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ${ModelName}\uC740 \uC0DD\uB7B5\uD558\uBA70, \uB514\uC790\uC778 \uC911\uC810\uC758 \uC774\uB984\uC73C\uB85C \uC791\uC131
4330
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uC788\uB294 General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
4331
+ - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
4332
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
4524
4333
 
4525
4334
 
4526
4335
  Application name: ${sysName}
@@ -4528,7 +4337,7 @@ Model name: ${modelName}
4528
4337
 
4529
4338
  Target filename: ${ModelName}.View.tsx
4530
4339
  \`\`\`
4531
-
4340
+ ${boilerplate}
4532
4341
  \`\`\`
4533
4342
 
4534
4343
 
@@ -4539,78 +4348,25 @@ var requestUnit = ({
4539
4348
  ModelName,
4540
4349
  constant,
4541
4350
  properties,
4351
+ boilerplate,
4542
4352
  exampleFiles
4543
4353
  }) => `
4544
-
4545
-
4546
- 1. Akan.js \uD504\uB808\uC784\uC6CC\uD06C\uC5D0 \uB300\uD55C \uAC1C\uC694
4547
- ${frameworkAbstract}
4548
-
4549
- 2. Akan.js\uD504\uB808\uC784\uC6CC\uD06C \uB370\uC774\uD130 \uAE30\uBC18 \uBAA8\uB4C8\uC5D0 \uB300\uD55C \uC124\uBA85
4550
- ${moduleDesription}
4551
-
4552
- 3. Akan.js eslint \uC124\uC815\uC5D0 \uB300\uD55C \uC124\uBA85
4553
- ${eslintDescription}
4554
-
4555
- 4. util/ui \uB0B4 ui\uD0B7\uC5D0 \uB300\uD55C \uC124\uBA85
4556
- ${utilUiDescription}
4557
-
4558
- 5. ${ModelName}.constant.ts \uD30C\uC77C\uC5D0 \uB300\uD55C \uC815\uBCF4
4559
- ${constant}
4560
-
4561
- 6. ${modelName}\uC5D0\uC11C \uC885\uC18D\uB418\uB294 \uB2E4\uB978 \uBAA8\uB378\uB4E4\uC758 \uD0C0\uC785 \uC815\uC758
4562
- ${properties.map(
4563
- (property) => `
4564
- \`\`\`
4565
- ${property.key}.constant.ts
4566
-
4567
-
4568
- ${property.source}
4569
- \`\`\`
4570
- `
4571
- ).join("\n\n")}
4572
-
4573
-
4574
- 7. \uC608\uC2DC \uD30C\uC77C\uB4E4
4575
- ${exampleFiles.map(
4576
- (example) => `
4577
- Example filename: ${example.filepath}
4578
- \`\`\`
4579
- ${example.content}
4580
- \`\`\`
4581
- `
4582
- ).join("\n\n")}
4583
-
4584
-
4585
-
4586
-
4587
-
4588
-
4589
- \uC5ED\uD560\uBD80\uC5EC
4590
- - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
4354
+ ${componentDefaultDescription({
4355
+ sysName,
4356
+ modelName,
4357
+ ModelName,
4358
+ exampleFiles,
4359
+ constant,
4360
+ properties
4361
+ })}
4591
4362
 
4592
- \uCF54\uB529 \uADDC\uCE59
4593
- - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4594
- - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4595
- - CSS: DaisyUI (btn, input, badge \uAC19\uC740 \uAE30\uBCF8 \uC694\uC18C\uB9CC \uC0AC\uC6A9 \uAC00\uB2A5, card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uBD88\uAC00)
4596
- - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
4597
- \uCF54\uB4DC \uC2A4\uD0C0\uC77C
4598
- - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
4599
- - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
4600
- - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
4601
- - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
4602
- \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
4603
-
4604
- \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
4605
- - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
4606
- - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
4607
- - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
4608
-
4609
4363
  \uC694\uCCAD\uC0AC\uD56D
4364
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
4610
4365
  - ${ModelName}.Unit.tsx \uCF54\uB4DC \uC791\uC131
4611
- - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ModelName\uC740 \uC0DD\uB7B5\uD558\uBA70, \uC608\uC2DC\uD30C\uC77C\uB4E4\uC758 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCC38\uACE0\uD558\uC5EC \uC791\uBA85
4366
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ${ModelName}\uC740 \uC0DD\uB7B5\uD558\uBA70, \uB514\uC790\uC778 \uC911\uC810\uC758 \uC774\uB984\uC73C\uB85C \uC791\uC131
4612
4367
  - \uC608\uC2DC\uD30C\uC77C \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAE30\uBC18\uD558\uC5EC \uC77C\uBC18\uC801\uC73C\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uC640 \uB514\uC790\uC778\uC801 \uC694\uC18C\uAC00 \uD3EC\uD568\uB41C \uCEF4\uD3EC\uB10C\uD2B8 3\uAC1C \uAC1C\uBC1C
4613
4368
  - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
4369
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
4614
4370
 
4615
4371
 
4616
4372
  Application name: ${sysName}
@@ -4618,7 +4374,7 @@ Model name: ${modelName}
4618
4374
 
4619
4375
  Target filename: ${ModelName}.Unit.tsx
4620
4376
  \`\`\`
4621
-
4377
+ ${boilerplate}
4622
4378
  \`\`\`
4623
4379
 
4624
4380
 
@@ -5110,7 +4866,7 @@ var ApplicationRunner = class {
5110
4866
  const chatModel = new import_openai3.ChatOpenAI({ modelName: "gpt-4o", openAIApiKey });
5111
4867
  const projectName = await (0, import_prompts5.input)({ message: "please enter project name." });
5112
4868
  const projectDesc = await (0, import_prompts5.input)({ message: "please enter project description. (40 ~ 60 characters)" });
5113
- const spinner = (0, import_ora2.default)("Gerating project files...");
4869
+ const spinner = (0, import_ora3.default)("Gerating project files...");
5114
4870
  const mainPrompt = import_prompts6.PromptTemplate.fromTemplate(requestApplication());
5115
4871
  const chain = import_runnables2.RunnableSequence.from([mainPrompt, chatModel, new import_output_parsers.StringOutputParser()]);
5116
4872
  const resultOne = await chain.invoke({ projectName, projectDesc });
@@ -5286,7 +5042,7 @@ var ApplicationScript = class {
5286
5042
  var ApplicationCommand = class {
5287
5043
  applicationScript = new ApplicationScript();
5288
5044
  async createApplication(name, start, workspace) {
5289
- await this.applicationScript.createApplication(name, workspace, { start });
5045
+ await this.applicationScript.createApplication(name.toLowerCase().replace(/ /g, "-"), workspace, { start });
5290
5046
  }
5291
5047
  async removeApplication(app) {
5292
5048
  await this.applicationScript.removeApplication(app);
@@ -5785,7 +5541,7 @@ CloudCommand = __decorateClass([
5785
5541
  var LibraryCommand = class {
5786
5542
  libraryScript = new LibraryScript();
5787
5543
  async createLibrary(name, workspace) {
5788
- await this.libraryScript.createLibrary(name, workspace);
5544
+ await this.libraryScript.createLibrary(name.toLowerCase().replace(/ /g, "-"), workspace);
5789
5545
  }
5790
5546
  async removeLibrary(lib) {
5791
5547
  await this.libraryScript.removeLibrary(lib);
@@ -5854,19 +5610,11 @@ var ModuleRunner = class {
5854
5610
  async createScalarTemplate(sys2, name) {
5855
5611
  const akanConfig = await sys2.getConfig();
5856
5612
  const scanResult = await sys2.scan({ akanConfig });
5857
- const names = (0, import_pluralize.default)(name);
5858
5613
  await sys2.applyTemplate({
5859
5614
  basePath: "./lib/__scalar",
5860
5615
  template: "__scalar",
5861
5616
  scanResult,
5862
- dict: {
5863
- model: name,
5864
- Model: capitalize(name),
5865
- models: names,
5866
- Models: capitalize(names),
5867
- sysName: sys2.name,
5868
- SysName: capitalize(sys2.name)
5869
- }
5617
+ dict: { model: name, Model: capitalize(name), sysName: sys2.name, SysName: capitalize(sys2.name) }
5870
5618
  });
5871
5619
  await sys2.scan({ akanConfig });
5872
5620
  return {
@@ -5877,22 +5625,39 @@ var ModuleRunner = class {
5877
5625
  }
5878
5626
  };
5879
5627
  }
5628
+ async createComponentTemplate(sys2, name, type) {
5629
+ const akanConfig = await sys2.getConfig();
5630
+ const scanResult = await sys2.scan({ akanConfig });
5631
+ await sys2.applyTemplate({
5632
+ basePath: `./lib/${name}__`,
5633
+ template: `module/__Model__.${capitalize(type)}.ts`,
5634
+ scanResult,
5635
+ dict: { model: name, Model: capitalize(name), appName: sys2.name }
5636
+ });
5637
+ await sys2.scan({ akanConfig });
5638
+ return {
5639
+ component: {
5640
+ filename: `${name}.${capitalize(type)}.tsx`,
5641
+ content: sys2.readFile(`lib/${name}__/${capitalize(name)}.${capitalize(type)}.tsx`)
5642
+ }
5643
+ // constant: {
5644
+ // filename: `${name}.constant.ts`,
5645
+ // content: sys.readFile(`lib/__scalar/${name}/${name}.constant.ts`),
5646
+ // },
5647
+ // dictionary: {
5648
+ // filename: `${name}.dictionary.ts`,
5649
+ // content: sys.readFile(`lib/__scalar/${name}/${name}.dictionary.ts`),
5650
+ // },
5651
+ };
5652
+ }
5880
5653
  async createModuleTemplate(sys2, name) {
5881
5654
  const akanConfig = await sys2.getConfig();
5882
5655
  const scanResult = await sys2.scan({ akanConfig });
5883
- const names = (0, import_pluralize.default)(name);
5884
5656
  await sys2.applyTemplate({
5885
5657
  basePath: `./lib/${name}`,
5886
5658
  template: "module",
5887
5659
  scanResult,
5888
- dict: {
5889
- model: name,
5890
- Model: capitalize(name),
5891
- models: names,
5892
- Models: capitalize(names),
5893
- sysName: sys2.name,
5894
- SysName: capitalize(sys2.name)
5895
- }
5660
+ dict: { model: name, Model: capitalize(name), sysName: sys2.name, SysName: capitalize(sys2.name) }
5896
5661
  });
5897
5662
  await sys2.scan({ akanConfig });
5898
5663
  return {
@@ -5939,64 +5704,6 @@ var ModuleRunner = class {
5939
5704
  }
5940
5705
  };
5941
5706
  }
5942
- // async createUnit(sys: App | Lib, modelName: string) {
5943
- // const modelFileData = getModelFileData(sys.cwdPath, modelName);
5944
- // const { paths } = getRelatedCnsts(modelFileData.constantFilePath);
5945
- // const names = { model: modelName, Model: capitalize(modelName), LightModel: `Light${capitalize(modelName)}` };
5946
- // const prompt = `
5947
- // 너는 React, Typescript, TailwindCSS를 기반으로 코딩하는 프론트엔드 개발자야.
5948
- // ${names.Model}이라는 도메인에 대해서 ${names.LightModel} 스키마를 대상으로 react component를 디자인하려고 해. 아래는 현재 보일러플레이트 형태의 컴포넌트야.
5949
- // \`\`\`
5950
- // ${modelFileData.unitFileStr}
5951
- // \`\`\`
5952
- // ${names.LightModel}은 다음과 같이 ${names.Model} 스키마에서 일부 필드를 추출해서 lightweight fetch된 형태로 설계되어있어.
5953
- // \`\`\`
5954
- // ${modelFileData.constantFileStr}
5955
- // \`\`\`
5956
- // 아래는 현재 스키마와 연관된 스키마 파일들이야. 스키마 파일들의 코드들을 보고 서로 어떻게 연계 되어있는지 이해하도록 해.
5957
- // ${
5958
- // paths
5959
- // ? new Array(paths.size).fill(0).map((_, index) => {
5960
- // //순서대로 paths key 추출
5961
- // const key = Array.from(paths.keys())[index];
5962
- // const filePath = paths.get(key)?.filePath;
5963
- // if (!filePath) throw new Error("filePath is undefined");
5964
- // return `${key}\n\`\`\`${fs.readFileSync(filePath, "utf8")}\`\`\`\`\n\n`;
5965
- // })
5966
- // : ""
5967
- // }
5968
- // 추가로, 만약에 아이콘 사용이 필요하면 react-icons/bi 라이브러리에서 사용해줘.
5969
- // 또, 색상을 사용하려고 하면 하드코딩된 색상(bg-red)이 아닌 테마 색상(bg-primary)을 사용해서 작성해줘.
5970
- // 그리고 optional한 필드는 field && <div>... 가 아닌, field ? <div>... : null 형태로 작성해줘.
5971
- // css라이브러리는 DaisyUI를 기반으로 작성해주는데, btn, input, badge와 같은 단순한 기본 css는 사용해도 괜찮아. 그런데 card, hero같이 복잡한 컴포넌트는 사용하면 안돼.
5972
- // 조건부 className이 필요한 경우에는 clsx 라이브러리를 사용해서 작성해야해.
5973
- // 모델에 대해서 object destructuring은 하지말고 ${modelName}.field 형태로 접근하게 코드를 작성해야해.
5974
- // ${names.Model}.Unit.Card의 리액트 컴포넌트를 디자인해서 작성해줘.
5975
- // `;
5976
- // try {
5977
- // fs.writeFileSync("./local/prompt.txt", prompt);
5978
- // const { content } = await streamAi(prompt, (chunk) => {
5979
- // process.stdout.write(chunk);
5980
- // });
5981
- // fs.writeFileSync("./local/result.txt", content);
5982
- // } catch (error) {
5983
- // // console.error("Application error:", error);
5984
- // }
5985
- // }
5986
- // async createView(sys: App | Lib, modelName: string) {
5987
- // const modelFileData = getModelFileData(sys.cwdPath, modelName);
5988
- // // const modelFileData = getModelFileData(sys.cwdPath, modelName);
5989
- // // const { paths } = getRelatedCnsts(modelFileData.constantFilePath);
5990
- // // const names = { model: modelName, Model: capitalize(modelName), LightModel: `Light${capitalize(modelName)}` };
5991
- // // const prompt = prompt.requestView({
5992
- // // sysName: sys.name,
5993
- // // modelName,
5994
- // // modelDesc: modelFileData.modelDesc,
5995
- // // modelSchemaDesign: modelFileData.modelSchemaDesign,
5996
- // // boilerplate: modelFileData.viewFileStr,
5997
- // // paths,
5998
- // // });
5999
- // }
6000
5707
  };
6001
5708
 
6002
5709
  // pkgs/@akanjs/cli/src/module/module.script.ts
@@ -6026,6 +5733,7 @@ var ModuleScript = class {
6026
5733
  async removeModule(workspace, name) {
6027
5734
  }
6028
5735
  async createScalar(sys2, name, description, schemaDescription) {
5736
+ await AiSession.init();
6029
5737
  await AiSession.init();
6030
5738
  const scalarConstantExampleFiles = await sys2.workspace.getScalarConstantFiles();
6031
5739
  const { constant, dictionary } = await this.#runner.createScalarTemplate(sys2, name);
@@ -6046,7 +5754,45 @@ var ModuleScript = class {
6046
5754
  }
6047
5755
  async createTest(workspace, name) {
6048
5756
  }
5757
+ async createTemplate(sys2) {
5758
+ await AiSession.init();
5759
+ const libs = await sys2.getModules();
5760
+ const lib = await (0, import_prompts8.select)({
5761
+ message: "Select the lib",
5762
+ choices: libs
5763
+ }).catch((e) => {
5764
+ Logger.error("canceled");
5765
+ return null;
5766
+ });
5767
+ if (!lib)
5768
+ return;
5769
+ const name = lib.split("/").pop();
5770
+ if (!name)
5771
+ return;
5772
+ const { component: template } = await this.#runner.createComponentTemplate(sys2, name, "template");
5773
+ const templateExampleFiles = (await sys2.getTemplatesSourceCode()).filter(
5774
+ (f) => !f.filepath.includes(`${name}.Template.tsx`)
5775
+ );
5776
+ const Name = capitalize(name);
5777
+ const relatedCnsts = getRelatedCnsts(`${sys2.cwdPath}/lib/${name}/${name}.constant.ts`);
5778
+ const constant = import_fs10.default.readFileSync(`${sys2.cwdPath}/lib/${name}/${name}.constant.ts`, "utf-8");
5779
+ const session = new AiSession();
5780
+ const promptRst = requestTemplate({
5781
+ sysName: sys2.name,
5782
+ modelName: name,
5783
+ ModelName: Name,
5784
+ constant,
5785
+ boilerplate: template.content,
5786
+ properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
5787
+ exampleFiles: randomPicks(templateExampleFiles, Math.min(20, templateExampleFiles.length))
5788
+ });
5789
+ const content = await session.editTypescript(promptRst);
5790
+ import_fs10.default.writeFileSync(`${sys2.cwdPath}/promptTemplate.txt`, promptRst);
5791
+ import_fs10.default.writeFileSync(`${sys2.cwdPath}/resultTemplate.txt`, content);
5792
+ sys2.writeFile(`lib/${name}__/${Name}.Template.tsx`, content);
5793
+ }
6049
5794
  async createUnit(sys2) {
5795
+ await AiSession.init();
6050
5796
  const libs = await sys2.getModules();
6051
5797
  const lib = await (0, import_prompts8.select)({
6052
5798
  message: "Select the lib",
@@ -6060,6 +5806,7 @@ var ModuleScript = class {
6060
5806
  const name = lib.split("/").pop();
6061
5807
  if (!name)
6062
5808
  return;
5809
+ const { component: unit } = await this.#runner.createComponentTemplate(sys2, name, "unit");
6063
5810
  const Name = capitalize(name);
6064
5811
  const unitExampleFiles = (await sys2.getUnitsSourceCode()).filter((f) => !f.filepath.includes(`${name}.Unit.tsx`));
6065
5812
  const relatedCnsts = getRelatedCnsts(`${sys2.cwdPath}/lib/${name}/${name}.constant.ts`);
@@ -6071,13 +5818,16 @@ var ModuleScript = class {
6071
5818
  ModelName: Name,
6072
5819
  constant,
6073
5820
  properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
6074
- exampleFiles: randomPicks(unitExampleFiles, Math.min(10, unitExampleFiles.length))
5821
+ exampleFiles: randomPicks(unitExampleFiles, Math.min(10, unitExampleFiles.length)),
5822
+ boilerplate: unit.content
6075
5823
  });
6076
5824
  const content = await session.editTypescript(promptRst);
6077
5825
  import_fs10.default.writeFileSync(`${sys2.cwdPath}/promptUnit.txt`, promptRst);
6078
5826
  import_fs10.default.writeFileSync(`${sys2.cwdPath}/resultUnit.txt`, content);
5827
+ sys2.writeFile(`lib/${name}__/${Name}.Unit.tsx`, content);
6079
5828
  }
6080
5829
  async createView(sys2) {
5830
+ await AiSession.init();
6081
5831
  const libs = await sys2.getModules();
6082
5832
  const lib = await (0, import_prompts8.select)({
6083
5833
  message: "Select the lib",
@@ -6091,6 +5841,7 @@ var ModuleScript = class {
6091
5841
  const name = lib.split("/").pop();
6092
5842
  if (!name)
6093
5843
  return;
5844
+ const { component: view } = await this.#runner.createComponentTemplate(sys2, name, "view");
6094
5845
  const viewExampleFiles = (await sys2.getViewsSourceCode()).filter((f) => !f.filepath.includes(`${name}.View.tsx`));
6095
5846
  const Name = capitalize(name);
6096
5847
  const relatedCnsts = getRelatedCnsts(`${sys2.cwdPath}/lib/${name}/${name}.constant.ts`);
@@ -6101,12 +5852,14 @@ var ModuleScript = class {
6101
5852
  modelName: name,
6102
5853
  ModelName: Name,
6103
5854
  constant,
5855
+ boilerplate: view.content,
6104
5856
  properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
6105
5857
  exampleFiles: randomPicks(viewExampleFiles, Math.min(20, viewExampleFiles.length))
6106
5858
  });
6107
5859
  const content = await session.editTypescript(promptRst);
6108
5860
  import_fs10.default.writeFileSync(`${sys2.cwdPath}/prompt.txt`, promptRst);
6109
5861
  import_fs10.default.writeFileSync(`${sys2.cwdPath}/result.txt`, content);
5862
+ sys2.writeFile(`lib/${name}__/${Name}.View.tsx`, content);
6110
5863
  }
6111
5864
  };
6112
5865
 
@@ -6114,14 +5867,14 @@ var ModuleScript = class {
6114
5867
  var ModuleCommand = class {
6115
5868
  moduleScript = new ModuleScript();
6116
5869
  async createModule(sys2, name, workspace) {
6117
- await this.moduleScript.createModuleTemplate(sys2, name);
5870
+ await this.moduleScript.createModuleTemplate(sys2, lowerlize(name));
6118
5871
  }
6119
5872
  async removeModule(name, workspace) {
6120
5873
  }
6121
5874
  async scanModule(name, workspace) {
6122
5875
  }
6123
5876
  async createScalar(sys2, name, description, schemaDescription, workspace) {
6124
- await this.moduleScript.createScalar(sys2, name, description, schemaDescription);
5877
+ await this.moduleScript.createScalar(sys2, lowerlize(name), description, schemaDescription);
6125
5878
  }
6126
5879
  async createService(name, workspace) {
6127
5880
  }
@@ -6133,6 +5886,9 @@ var ModuleCommand = class {
6133
5886
  async createUnit(sys2, workspace) {
6134
5887
  await this.moduleScript.createUnit(sys2);
6135
5888
  }
5889
+ async createTemplate(sys2, workspace) {
5890
+ await this.moduleScript.createTemplate(sys2);
5891
+ }
6136
5892
  };
6137
5893
  __decorateClass([
6138
5894
  Target.Public(),
@@ -6178,6 +5934,11 @@ __decorateClass([
6178
5934
  __decorateParam(0, Sys()),
6179
5935
  __decorateParam(1, Workspace())
6180
5936
  ], ModuleCommand.prototype, "createUnit", 1);
5937
+ __decorateClass([
5938
+ Target.Public(),
5939
+ __decorateParam(0, Sys()),
5940
+ __decorateParam(1, Workspace())
5941
+ ], ModuleCommand.prototype, "createTemplate", 1);
6181
5942
  ModuleCommand = __decorateClass([
6182
5943
  Commands()
6183
5944
  ], ModuleCommand);
@@ -6189,7 +5950,7 @@ var PackageCommand = class {
6189
5950
  await this.packageScript.version(workspace);
6190
5951
  }
6191
5952
  async createPackage(name, workspace) {
6192
- await this.packageScript.createPackage(workspace, name);
5953
+ await this.packageScript.createPackage(workspace, name.toLowerCase().replace(/ /g, "-"));
6193
5954
  }
6194
5955
  async removePackage(pkg) {
6195
5956
  await this.packageScript.removePackage(pkg);
@@ -6399,7 +6160,7 @@ var WorkspaceScript = class {
6399
6160
  var WorkspaceCommand = class {
6400
6161
  workspaceScript = new WorkspaceScript();
6401
6162
  async createWorkspace(name, app, dir) {
6402
- await this.workspaceScript.createWorkspace(name, app, dir);
6163
+ await this.workspaceScript.createWorkspace(name, app.toLowerCase().replace(/ /g, "-"), dir);
6403
6164
  }
6404
6165
  async generateMongo(workspace) {
6405
6166
  await this.workspaceScript.generateMongo(workspace);
@@ -6454,5 +6215,5 @@ void runCommands(
6454
6215
  //! 2. csr폴더를 현 위치로 복사 후 압축 후 삭제
6455
6216
  //! execSync를 가져오기 싫으니 일단 2번 방법으로 해보자
6456
6217
  //! add path in tsconfig.json
6457
- //! 파일을 {name}.Unit.tsx에 저장.
6458
6218
  //! 파일을 {name}.View.tsx에 저장.
6219
+ //! 파일을 {name}.Unit.tsx에 저장.