@m1kapp/kit 0.0.31 → 0.0.32

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/README.md CHANGED
@@ -17,21 +17,25 @@ npm install @m1kapp/kit
17
17
 
18
18
  ```tsx
19
19
  // CSS 자동 주입 — 별도 import 불필요
20
- import { AppShell, AppShellHeader, AppShellContent, TabBar, Tab } from "@m1kapp/kit";
20
+ import { Watermark, AppShell, AppShellHeader, AppShellContent, Section, TabBar, Tab } from "@m1kapp/kit";
21
21
 
22
22
  export default function App() {
23
23
  return (
24
- <AppShell>
25
- <AppShellHeader>
26
- <h1>My App</h1>
27
- </AppShellHeader>
28
- <AppShellContent>
29
- {/* 콘텐츠 */}
30
- </AppShellContent>
31
- <TabBar>
32
- <Tab href="/" icon={<HomeIcon />} label="홈" />
33
- </TabBar>
34
- </AppShell>
24
+ // Watermark가 뷰포트 전체 높이/중앙정렬/하단 크레딧을 같이 줍니다.
25
+ // AppShell만 단독으로 쓰면 콘텐츠 높이만큼 쪼그라들어요.
26
+ <Watermark color="#e2603f" text="myapp">
27
+ <AppShell>
28
+ <AppShellHeader>
29
+ <h1>My App</h1>
30
+ </AppShellHeader>
31
+ <AppShellContent>
32
+ <Section className="pt-4">{/* 콘텐츠 — Divider로 섹션 구분 */}</Section>
33
+ </AppShellContent>
34
+ <TabBar>
35
+ <Tab href="/" icon={<HomeIcon />} label="홈" />
36
+ </TabBar>
37
+ </AppShell>
38
+ </Watermark>
35
39
  );
36
40
  }
37
41
  ```
package/bin/stats.mjs CHANGED
@@ -390,6 +390,7 @@ let totalBranches = 0;
390
390
  let totalFunctions = 0;
391
391
  let maxFile = { path: "", lines: 0 };
392
392
  let longFiles = 0; // 200줄 초과 파일 수
393
+ let longFileSeverity = 0; // 200→400줄 0→1, 400→600줄 1→2로 완만하게 누적 (600줄+ 파일당 최대 2)
393
394
  const allImports = new Set();
394
395
  const ts = loadTypescript();
395
396
  const allFns = []; // AST 모드: {name, cc, cog, line, file}
@@ -417,7 +418,10 @@ for (const file of files) {
417
418
  fileContents.push({ file: rel, content });
418
419
  }
419
420
  if (counts.code > maxFile.lines) maxFile = { path: path.relative(process.cwd(), file), lines: counts.code };
420
- if (counts.code > 200) longFiles++;
421
+ if (counts.code > 200) {
422
+ longFiles++;
423
+ longFileSeverity += Math.min(2, (counts.code - 200) / 200);
424
+ }
421
425
  const imports = detectKitImports(content);
422
426
  for (const imp of imports) allImports.add(imp);
423
427
  }
@@ -431,7 +435,10 @@ const avgFileLines = files.length > 0 ? Math.round(codeLines / files.length) : 0
431
435
  // - cog>15 함수 비율 ×3 (최대 25), cog>25 함수 비율 ×5 (최대 15)
432
436
  // - 최악 함수: cog 20 초과분 ×1 (최대 15)
433
437
  // - 중복 밀도: 3% 초과분 ×2.5 (최대 25) ← 새 축
434
- // - 200줄 초과 파일 비율 ×2 (최대 10) / 평균 파일 길이 80줄 초과분 /5 (최대 10)
438
+ // - 200줄 초과 파일: 초과분에 비례한 심각도(200→400줄 0→1, 400→600줄 1→2, 600줄+ 캡) 비율 ×1.5 (최대 10)
439
+ // ※ 파일 개수 기준 이진 카운트(200줄 넘으면 무조건 1) 대신 심각도 가중 — 살짝 넘는 파일과
440
+ // 터무니없이 큰 파일을 구분하고, 파일 수 적은 프로젝트가 파일 1개만으로 즉시 만점 감점 맞는 절벽 방지.
441
+ // / 평균 파일 길이 80줄 초과분 /5 (최대 10)
435
442
  let qualityScore;
436
443
  let cc = null;
437
444
  let cognitive = null;
@@ -465,14 +472,14 @@ if (ts && allFns.length > 0) {
465
472
 
466
473
  const over15Pct = (cogOver15.length / allFns.length) * 100;
467
474
  const over25Pct = (cogOver25.length / allFns.length) * 100;
468
- const longFilesPct = (longFiles / files.length) * 100;
475
+ const longFileSeverityPct = (longFileSeverity / files.length) * 100;
469
476
  qualityScore = Math.max(0, Math.round(
470
477
  100
471
478
  - Math.min(25, over15Pct * 3)
472
479
  - Math.min(15, over25Pct * 5)
473
480
  - Math.min(15, Math.max(0, maxCog - 20))
474
481
  - Math.min(25, Math.max(0, duplication.percent - 3) * 2.5)
475
- - Math.min(10, longFilesPct * 2)
482
+ - Math.min(10, longFileSeverityPct * 1.5)
476
483
  - Math.min(10, Math.max(0, avgFileLines - 80) / 5)
477
484
  ));
478
485
  } else {
@@ -480,7 +487,7 @@ if (ts && allFns.length > 0) {
480
487
  qualityScore = Math.max(0, Math.round(
481
488
  100
482
489
  - Math.min(40, Math.max(0, branchDensity - 10) * 2)
483
- - Math.min(30, longFiles * 5)
490
+ - Math.min(30, longFileSeverity * 2.5)
484
491
  - Math.min(30, Math.max(0, avgFileLines - 80) / 4)
485
492
  ));
486
493
  }
package/dist/index.d.mts CHANGED
@@ -65,6 +65,19 @@ interface AppShellProps {
65
65
  /**
66
66
  * Mobile app-like container with rounded corners, shadow, and ring.
67
67
  * Centers content and constrains width for a phone-like viewport.
68
+ *
69
+ * `AppShell` fills `h-full` of its parent — it does not size itself. Wrap it
70
+ * in `<Watermark>` (gives `h-dvh` + centering + the `PoweredByKit` credit
71
+ * strip) rather than a hand-rolled height/centering div; without a sized
72
+ * parent the shell just collapses to content height.
73
+ *
74
+ * @example
75
+ * <Watermark color="#e2603f" text="myapp">
76
+ * <AppShell>
77
+ * <AppShellHeader>...</AppShellHeader>
78
+ * <AppShellContent><Section>...</Section></AppShellContent>
79
+ * </AppShell>
80
+ * </Watermark>
68
81
  */
69
82
  declare function AppShell({ children, className, maxWidth, maxHeight, accent, accentFg, style, }: AppShellProps): react_jsx_runtime.JSX.Element;
70
83
  interface AppShellHeaderProps {
@@ -80,7 +93,9 @@ interface AppShellContentProps {
80
93
  className?: string;
81
94
  }
82
95
  /**
83
- * Scrollable main content area.
96
+ * Scrollable main content area. Carries no padding of its own — build
97
+ * children from `<Section>` (px-4) blocks separated by `<Divider spacing="sm" />`,
98
+ * not a hand-rolled `<div className="p-4 flex flex-col gap-4">`.
84
99
  */
85
100
  declare function AppShellContent({ children, className }: AppShellContentProps): react_jsx_runtime.JSX.Element;
86
101
 
package/dist/index.d.ts CHANGED
@@ -65,6 +65,19 @@ interface AppShellProps {
65
65
  /**
66
66
  * Mobile app-like container with rounded corners, shadow, and ring.
67
67
  * Centers content and constrains width for a phone-like viewport.
68
+ *
69
+ * `AppShell` fills `h-full` of its parent — it does not size itself. Wrap it
70
+ * in `<Watermark>` (gives `h-dvh` + centering + the `PoweredByKit` credit
71
+ * strip) rather than a hand-rolled height/centering div; without a sized
72
+ * parent the shell just collapses to content height.
73
+ *
74
+ * @example
75
+ * <Watermark color="#e2603f" text="myapp">
76
+ * <AppShell>
77
+ * <AppShellHeader>...</AppShellHeader>
78
+ * <AppShellContent><Section>...</Section></AppShellContent>
79
+ * </AppShell>
80
+ * </Watermark>
68
81
  */
69
82
  declare function AppShell({ children, className, maxWidth, maxHeight, accent, accentFg, style, }: AppShellProps): react_jsx_runtime.JSX.Element;
70
83
  interface AppShellHeaderProps {
@@ -80,7 +93,9 @@ interface AppShellContentProps {
80
93
  className?: string;
81
94
  }
82
95
  /**
83
- * Scrollable main content area.
96
+ * Scrollable main content area. Carries no padding of its own — build
97
+ * children from `<Section>` (px-4) blocks separated by `<Divider spacing="sm" />`,
98
+ * not a hand-rolled `<div className="p-4 flex flex-col gap-4">`.
84
99
  */
85
100
  declare function AppShellContent({ children, className }: AppShellContentProps): react_jsx_runtime.JSX.Element;
86
101