mtrl 0.3.1 → 0.3.2

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.
Files changed (159) hide show
  1. package/.env +15 -0
  2. package/CONTRIBUTING.md +8 -8
  3. package/DOCS.md +3 -3
  4. package/README.md +43 -20
  5. package/TESTING.md +128 -18
  6. package/dist/index.js +14865 -0
  7. package/git-user-stats.js +545 -0
  8. package/index.ts +9 -67
  9. package/package.json +8 -3
  10. package/src/components/badge/api.ts +15 -1
  11. package/src/components/badge/badge.ts +43 -4
  12. package/src/components/badge/config.ts +40 -8
  13. package/src/components/badge/index.ts +64 -3
  14. package/src/components/badge/types.ts +175 -33
  15. package/src/components/button/api.ts +63 -1
  16. package/src/components/button/button.ts +39 -3
  17. package/src/components/button/config.ts +21 -4
  18. package/src/components/button/index.ts +26 -1
  19. package/src/components/button/types.ts +7 -1
  20. package/src/components/card/api.ts +78 -9
  21. package/src/components/card/card.ts +58 -3
  22. package/src/components/card/config.ts +41 -11
  23. package/src/components/card/features.ts +39 -12
  24. package/src/components/card/index.ts +84 -19
  25. package/src/components/card/types.ts +218 -29
  26. package/src/components/carousel/carousel.ts +92 -28
  27. package/src/components/carousel/constants.ts +107 -21
  28. package/src/components/carousel/index.ts +31 -13
  29. package/src/components/checkbox/checkbox.ts +83 -16
  30. package/src/components/checkbox/index.ts +43 -1
  31. package/src/components/checkbox/types.ts +219 -32
  32. package/src/components/chips/api.ts +194 -0
  33. package/src/components/{chip → chips/chip}/api.ts +42 -2
  34. package/src/components/chips/chip/chip.ts +131 -0
  35. package/src/components/{chip → chips/chip}/config.ts +3 -3
  36. package/src/components/chips/chip/index.ts +3 -0
  37. package/src/components/chips/chips.md +481 -0
  38. package/src/components/chips/chips.ts +75 -0
  39. package/src/components/chips/config.ts +109 -0
  40. package/src/components/chips/constants.ts +61 -0
  41. package/src/components/chips/features/chip-items.ts +33 -0
  42. package/src/components/chips/features/container.ts +77 -0
  43. package/src/components/chips/features/controller.ts +448 -0
  44. package/src/components/chips/features/index.ts +5 -0
  45. package/src/components/chips/features/label.ts +108 -0
  46. package/src/components/chips/index.ts +11 -0
  47. package/src/components/chips/schema.ts +61 -0
  48. package/src/components/{chip → chips}/types.ts +203 -92
  49. package/src/components/dialog/dialog.ts +99 -16
  50. package/src/components/dialog/index.ts +97 -1
  51. package/src/components/dialog/types.ts +375 -69
  52. package/src/components/divider/config.ts +90 -6
  53. package/src/components/divider/divider.ts +32 -2
  54. package/src/components/divider/features.ts +26 -0
  55. package/src/components/divider/index.ts +30 -0
  56. package/src/components/divider/types.ts +86 -9
  57. package/src/components/extended-fab/api.ts +53 -1
  58. package/src/components/extended-fab/config.ts +29 -1
  59. package/src/components/extended-fab/extended-fab.ts +28 -0
  60. package/src/components/extended-fab/index.ts +36 -0
  61. package/src/components/extended-fab/types.ts +458 -13
  62. package/src/components/fab/api.ts +42 -2
  63. package/src/components/fab/config.ts +29 -1
  64. package/src/components/fab/fab.ts +16 -2
  65. package/src/components/fab/index.ts +35 -0
  66. package/src/components/fab/types.ts +374 -10
  67. package/src/components/list/api.ts +12 -2
  68. package/src/components/list/config.ts +21 -0
  69. package/src/components/list/features.ts +6 -0
  70. package/src/components/list/index.ts +56 -1
  71. package/src/components/list/list-item.ts +46 -2
  72. package/src/components/list/list.ts +73 -2
  73. package/src/components/list/types.ts +172 -0
  74. package/src/components/list/utils.ts +26 -2
  75. package/src/components/menu/api.ts +217 -20
  76. package/src/components/menu/config.ts +27 -0
  77. package/src/components/menu/features/visibility.ts +55 -6
  78. package/src/components/menu/index.ts +64 -0
  79. package/src/components/menu/menu-item.ts +46 -3
  80. package/src/components/menu/menu.ts +77 -1
  81. package/src/components/menu/types.ts +404 -39
  82. package/src/components/sheet/config.ts +1 -2
  83. package/src/components/sheet/features/gestures.ts +1 -1
  84. package/src/components/sheet/features/position.ts +1 -2
  85. package/src/components/sheet/features/state.ts +1 -1
  86. package/src/components/sheet/index.ts +10 -2
  87. package/src/components/sheet/sheet.ts +1 -2
  88. package/src/components/sheet/types.ts +29 -1
  89. package/src/components/slider/api.ts +1 -1
  90. package/src/components/slider/config.ts +1 -1
  91. package/src/components/slider/features/controller.ts +1 -1
  92. package/src/components/slider/features/handlers.ts +1 -1
  93. package/src/components/slider/features/states.ts +1 -1
  94. package/src/components/slider/index.ts +12 -5
  95. package/src/components/slider/schema.ts +1 -1
  96. package/src/components/slider/types.ts +31 -0
  97. package/src/components/tabs/tab-api.ts +1 -1
  98. package/src/components/tabs/types.ts +1 -1
  99. package/src/components/tooltip/api.ts +6 -2
  100. package/src/components/tooltip/config.ts +9 -28
  101. package/src/components/tooltip/index.ts +10 -1
  102. package/src/components/tooltip/types.ts +38 -3
  103. package/src/index.ts +129 -31
  104. package/src/styles/abstract/_mixins.scss +23 -9
  105. package/src/styles/abstract/_variables.scss +14 -4
  106. package/src/styles/components/_card.scss +1 -1
  107. package/src/styles/components/_chip.scss +323 -113
  108. package/src/styles/components/_tabs.scss +1 -1
  109. package/CLAUDE.md +0 -33
  110. package/src/components/checkbox/constants.ts +0 -37
  111. package/src/components/chip/chip-set.ts +0 -225
  112. package/src/components/chip/chip.ts +0 -118
  113. package/src/components/chip/constants.ts +0 -28
  114. package/src/components/chip/index.ts +0 -12
  115. package/src/components/list/constants.ts +0 -116
  116. package/src/components/sheet/constants.ts +0 -20
  117. package/src/components/slider/constants.ts +0 -32
  118. package/src/components/tooltip/constants.ts +0 -27
  119. package/test/components/badge.test.ts +0 -545
  120. package/test/components/bottom-app-bar.test.ts +0 -303
  121. package/test/components/button.test.ts +0 -233
  122. package/test/components/card.test.ts +0 -560
  123. package/test/components/carousel.test.ts +0 -951
  124. package/test/components/checkbox.test.ts +0 -462
  125. package/test/components/chip.test.ts +0 -692
  126. package/test/components/datepicker.test.ts +0 -1124
  127. package/test/components/dialog.test.ts +0 -990
  128. package/test/components/divider.test.ts +0 -412
  129. package/test/components/extended-fab.test.ts +0 -672
  130. package/test/components/fab.test.ts +0 -561
  131. package/test/components/list.test.ts +0 -365
  132. package/test/components/menu.test.ts +0 -718
  133. package/test/components/navigation.test.ts +0 -186
  134. package/test/components/progress.test.ts +0 -567
  135. package/test/components/radios.test.ts +0 -699
  136. package/test/components/search.test.ts +0 -1135
  137. package/test/components/segmented-button.test.ts +0 -732
  138. package/test/components/sheet.test.ts +0 -641
  139. package/test/components/slider.test.ts +0 -1220
  140. package/test/components/snackbar.test.ts +0 -461
  141. package/test/components/switch.test.ts +0 -452
  142. package/test/components/tabs.test.ts +0 -1369
  143. package/test/components/textfield.test.ts +0 -400
  144. package/test/components/timepicker.test.ts +0 -592
  145. package/test/components/tooltip.test.ts +0 -630
  146. package/test/components/top-app-bar.test.ts +0 -566
  147. package/test/core/dom.attributes.test.ts +0 -148
  148. package/test/core/dom.classes.test.ts +0 -152
  149. package/test/core/dom.events.test.ts +0 -243
  150. package/test/core/emitter.test.ts +0 -141
  151. package/test/core/ripple.test.ts +0 -99
  152. package/test/core/state.store.test.ts +0 -189
  153. package/test/core/utils.normalize.test.ts +0 -61
  154. package/test/core/utils.object.test.ts +0 -120
  155. package/test/setup.js +0 -371
  156. package/test/setup.ts +0 -451
  157. package/tsconfig.json +0 -22
  158. package/typedoc.json +0 -28
  159. package/typedoc.simple.json +0 -14
@@ -0,0 +1,545 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * Git Work Time Estimation Script
5
+ * ==============================
6
+ *
7
+ * This script analyzes git commit history to estimate the time spent working on a project.
8
+ * It combines several methodologies for estimation:
9
+ *
10
+ * 1. COCOMO II Model (Constructive Cost Model)
11
+ * - Modified for commit-level analysis
12
+ * - Uses different approaches for small vs large changes
13
+ * - Incorporates effort multipliers based on commit types
14
+ *
15
+ * 2. Git Metadata Analysis
16
+ * - Analyzes commit messages for type classification
17
+ * - Processes line addition/deletion statistics
18
+ * - Optionally analyzes across all branches
19
+ *
20
+ * 3. Time Constraints and Reality Checks
21
+ * - Enforces daily work hour limits
22
+ * - Accounts for minimum overhead time per commit
23
+ * - Caps maximum time per individual commit
24
+ *
25
+ * Theory and Methodology
26
+ * =====================
27
+ *
28
+ * COCOMO II Adaptation:
29
+ * Traditional COCOMO II formula: Effort = A × Size^B
30
+ * where:
31
+ * - A is the base effort coefficient (typically 2.94)
32
+ * - B is the scale factor (typically 0.91)
33
+ * - Size is measured in thousands of lines of code (KLOC)
34
+ *
35
+ * Our modifications:
36
+ * 1. Reduced base coefficient for commit-level granularity
37
+ * 2. Different calculation for small changes (<100 lines)
38
+ * 3. Weighted treatment of deletions vs additions
39
+ * 4. Additional effort multipliers based on commit type
40
+ *
41
+ * Usage Examples
42
+ * =============
43
+ *
44
+ * Basic usage:
45
+ * bun git-user-stats.js --authors "Author Name"
46
+ *
47
+ * Multiple authors:
48
+ * bun git-user-stats.js --authors "Author One" "author.one@email.com" "Author Two"
49
+ *
50
+ * Date range:
51
+ * bun git-user-stats.js --authors "Author" --since "2024-01-01" --until "2024-02-01"
52
+ *
53
+ * Branch analysis:
54
+ * bun git-user-stats.js --authors "Author" --all-branches
55
+ * bun git-user-stats.js --authors "Author" --branch main
56
+ *
57
+ * Combined options:
58
+ * bun git-user-stats.js --authors "Author" --all-branches --since "1 month ago" --no-merges
59
+ *
60
+ * Command Line Options
61
+ * ===================
62
+ *
63
+ * Required:
64
+ * --authors One or more author identifiers (names or emails)
65
+ *
66
+ * Optional:
67
+ * --since Start date (YYYY-MM-DD or git-compatible date)
68
+ * --until End date
69
+ * --all-branches Include commits from all branches
70
+ * --branch Analyze specific branch only
71
+ * --no-merges Exclude merge commits
72
+ *
73
+ * Date Format Options:
74
+ * - ISO dates: "2024-01-01"
75
+ * - Relative: "1 month ago", "2 weeks ago"
76
+ * - Named: "yesterday", "last monday"
77
+ */
78
+
79
+ import { exec } from 'child_process'
80
+ import { promisify } from 'util'
81
+ import progress from '../radiooooo/monorepo/packages/script/src/module/progress.js'
82
+
83
+ const execAsync = promisify(exec)
84
+
85
+ /**
86
+ * Command line argument parser
87
+ * Processes arguments and provides defaults for optional parameters
88
+ * @returns {Object} Parsed arguments with default values
89
+ */
90
+ const parseArgs = () => {
91
+ const args = {
92
+ authors: [],
93
+ since: null,
94
+ until: null,
95
+ allBranches: false,
96
+ branch: null,
97
+ noMerges: false
98
+ }
99
+
100
+ for (let i = 2; i < process.argv.length; i++) {
101
+ const arg = process.argv[i]
102
+ switch (arg) {
103
+ case '--authors':
104
+ while (i + 1 < process.argv.length && !process.argv[i + 1].startsWith('--')) {
105
+ args.authors.push(process.argv[++i])
106
+ }
107
+ break
108
+ case '--since':
109
+ args.since = process.argv[++i]
110
+ break
111
+ case '--until':
112
+ args.until = process.argv[++i]
113
+ break
114
+ case '--all-branches':
115
+ args.allBranches = true
116
+ break
117
+ case '--branch':
118
+ args.branch = process.argv[++i]
119
+ break
120
+ case '--no-merges':
121
+ args.noMerges = true
122
+ break
123
+ }
124
+ }
125
+
126
+ return args
127
+ }
128
+
129
+ const args = parseArgs()
130
+
131
+ // Input validation with detailed help
132
+ if (args.authors.length === 0) {
133
+ console.error('Usage: bun git-user-stats.js --authors "Author Name" ["Author Email"...]')
134
+ console.error('\nOptions:')
135
+ console.error(' --authors One or more author identifiers (names or emails)')
136
+ console.error(' --since Start date (YYYY-MM-DD or "1 month ago")')
137
+ console.error(' --until End date')
138
+ console.error(' --all-branches Include commits from all branches')
139
+ console.error(' --branch Analyze specific branch only')
140
+ console.error(' --no-merges Exclude merge commits')
141
+ console.error('\nExamples:')
142
+ console.error(' bun git-user-stats.js --authors "John Doe" --since "2024-01-01"')
143
+ console.error(' bun git-user-stats.js --authors "john@doe.com" --all-branches')
144
+ console.error(' bun git-user-stats.js --authors "John Doe" "john@doe.com" --branch main')
145
+ process.exit(1)
146
+ }
147
+
148
+ /**
149
+ * COCOMO Model Configuration
150
+ * -------------------------
151
+ * Modified version of COCOMO II for commit-level analysis
152
+ *
153
+ * Key differences from standard COCOMO II:
154
+ * 1. Lower base coefficient for smaller units of work
155
+ * 2. Hybrid approach for different sized changes
156
+ * 3. Additional effort multipliers for commit types
157
+ */
158
+ const COCOMO = {
159
+ // Base effort coefficient (reduced from standard 2.94)
160
+ A: 0.91,
161
+
162
+ // Scale factor (standard COCOMO II value)
163
+ B: 0.91,
164
+
165
+ // Industry standard work month
166
+ PERSON_MONTH_HOURS: 152,
167
+
168
+ // Realistic daily limit including overtime
169
+ MAX_HOURS_PER_DAY: 10,
170
+
171
+ /**
172
+ * Effort multipliers based on commit type
173
+ * Derived from COCOMO II cost drivers:
174
+ * - CPLX (Product Complexity)
175
+ * - RUSE (Required Reusability)
176
+ * - PDIF (Platform Difficulty)
177
+ */
178
+ TYPE_MULTIPLIERS: {
179
+ refactor: 1.24, // High complexity
180
+ fix: 1.15, // High attention to detail
181
+ test: 1.12, // Medium complexity
182
+ merge: 0.82, // Low complexity
183
+ update: 0.87, // Simple changes
184
+ cleanup: 0.85, // Very low complexity
185
+ default: 1.0 // Nominal case
186
+ },
187
+
188
+ /**
189
+ * Effort estimation function
190
+ * Uses different approaches based on change size:
191
+ * - COCOMO II for large changes
192
+ * - Linear estimation for small changes
193
+ *
194
+ * @param {number} kloc - Size in thousands of lines of code
195
+ * @returns {number} Estimated minutes of effort
196
+ */
197
+ estimateEffort: (kloc) => {
198
+ if (kloc >= 0.1) { // More than 100 lines
199
+ const personMonths = COCOMO.A * Math.pow(kloc, COCOMO.B)
200
+ return personMonths * COCOMO.PERSON_MONTH_HOURS * 60
201
+ } else {
202
+ // Linear estimation for small changes (3 minutes per line)
203
+ return kloc * 1000 * 3
204
+ }
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Time Formatting Helper
210
+ * Converts minutes to human-readable duration
211
+ * @param {number} minutes - Total minutes to format
212
+ * @returns {string} Formatted string like "5h30m"
213
+ */
214
+ const formatHours = minutes => {
215
+ const hours = Math.floor(minutes / 60)
216
+ const remainingMinutes = Math.round(minutes % 60)
217
+ return `${hours}h${remainingMinutes}m`
218
+ }
219
+
220
+ /**
221
+ * Git Line Statistics Analyzer
222
+ * Retrieves the number of lines added and deleted for a commit
223
+ * Uses git show --numstat for detailed change information
224
+ *
225
+ * @param {string} hash - Git commit hash
226
+ * @returns {Promise<Object>} Object containing added and deleted line counts
227
+ */
228
+ const getLineStats = async hash => {
229
+ try {
230
+ const { stdout } = await execAsync(`git show --numstat ${hash}`)
231
+ const stats = stdout.split('\n')
232
+ .filter(line => /^\d+\t\d+\t/.test(line))
233
+ .reduce((acc, line) => {
234
+ const [added, deleted] = line.split('\t').map(Number)
235
+ return {
236
+ added: acc.added + (added || 0),
237
+ deleted: acc.deleted + (deleted || 0)
238
+ }
239
+ }, { added: 0, deleted: 0 })
240
+ return stats
241
+ } catch (err) {
242
+ return { added: 0, deleted: 0 }
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Commit Type Analyzer
248
+ * Determines the effort multiplier based on commit message content
249
+ * Uses keyword analysis to categorize the type of change
250
+ *
251
+ * @param {string} message - Git commit message
252
+ * @returns {number} Effort multiplier value
253
+ */
254
+ const getCommitMultiplier = message => {
255
+ const msg = message.toLowerCase()
256
+ for (const [type, multiplier] of Object.entries(COCOMO.TYPE_MULTIPLIERS)) {
257
+ if (msg.includes(type)) return multiplier
258
+ }
259
+ return COCOMO.TYPE_MULTIPLIERS.default
260
+ }
261
+
262
+ /**
263
+ * Time Estimation Function
264
+ * Calculates estimated time for a single commit
265
+ *
266
+ * Factors considered:
267
+ * - Lines added (full weight)
268
+ * - Lines deleted (partial weight)
269
+ * - Commit type multiplier
270
+ * - Minimum overhead time
271
+ * - Maximum time cap
272
+ *
273
+ * @param {number} added - Lines added
274
+ * @param {number} deleted - Lines deleted
275
+ * @param {string} message - Commit message
276
+ * @returns {number} Estimated minutes for this commit
277
+ */
278
+ const estimateCommitTime = (added, deleted, message) => {
279
+ // Weight deletions as 20% of additions
280
+ const kloc = (added + (deleted * 0.2)) / 1000
281
+
282
+ // Get base effort estimate
283
+ let minutes = COCOMO.estimateEffort(kloc)
284
+
285
+ // Apply type-based multiplier
286
+ const multiplier = getCommitMultiplier(message)
287
+ minutes *= multiplier
288
+
289
+ // Enforce minimum and maximum bounds
290
+ minutes = Math.max(minutes + 5, 10) // At least 10 minutes
291
+ return Math.min(minutes, 240) // Cap at 4 hours
292
+ }
293
+
294
+ /**
295
+ * Git Log Retriever
296
+ * Gets commit history for a specific author
297
+ * Handles branch selection and date range filtering
298
+ *
299
+ * @param {string} author - Author name or email
300
+ * @returns {Promise<string>} Git log output
301
+ */
302
+ const getGitLog = async author => {
303
+ const cmd = ['git log']
304
+
305
+ // Branch selection
306
+ if (args.allBranches) {
307
+ cmd.push('--all')
308
+ } else if (args.branch) {
309
+ cmd.push(args.branch)
310
+ }
311
+
312
+ // Additional flags
313
+ if (args.noMerges) cmd.push('--no-merges')
314
+ cmd.push(`--author="${author}"`)
315
+ cmd.push('--date=iso')
316
+ cmd.push('--format="%ad|%h|%ae|%s|%D"')
317
+
318
+ // Date range
319
+ if (args.since) cmd.push(`--since="${args.since}"`)
320
+ if (args.until) cmd.push(`--until="${args.until}"`)
321
+
322
+ const { stdout } = await execAsync(cmd.join(' '))
323
+ return stdout.trim()
324
+ }
325
+
326
+ /**
327
+ * Main Analysis Function
328
+ * Processes git history and generates time estimates
329
+ *
330
+ * Steps:
331
+ * 1. Fetches commit history
332
+ * 2. Analyzes each commit
333
+ * 3. Groups by date
334
+ * 4. Applies time constraints
335
+ * 5. Generates statistics
336
+ *
337
+ * @param {string[]} authors - List of author identifiers
338
+ */
339
+ const getCommitStats = async authors => {
340
+ try {
341
+ // Initial status message
342
+ if (args.allBranches) {
343
+ console.log('Fetching git history from all branches...')
344
+ } else if (args.branch) {
345
+ console.log(`Fetching git history from branch: ${args.branch}`)
346
+ } else {
347
+ console.log('Fetching git history from current branch...')
348
+ }
349
+
350
+ const allLogs = await Promise.all(authors.map(getGitLog))
351
+
352
+ // Parse commit information
353
+ const commits = allLogs
354
+ .join('\n')
355
+ .split('\n')
356
+ .filter(line => line.length > 0)
357
+ .map(line => {
358
+ const [datetime, hash, email, message, refs] = line.split('|')
359
+ return {
360
+ date: datetime.split(' ')[0],
361
+ timestamp: new Date(datetime),
362
+ hash,
363
+ email,
364
+ message,
365
+ branch: refs ? refs.split(',')[0].trim() : 'main'
366
+ }
367
+ })
368
+ .sort((a, b) => b.timestamp - a.timestamp)
369
+
370
+ if (commits.length === 0) {
371
+ console.log('No commits found for the specified author(s)')
372
+ return
373
+ }
374
+
375
+ console.log(`Processing ${commits.length} commits...`)
376
+
377
+ // Initialize tracking variables
378
+ let processedCount = 0
379
+ const startTime = Date.now()
380
+ const totalCommits = commits.length
381
+ const commitsByDate = {}
382
+ const totalLines = { added: 0, deleted: 0 }
383
+
384
+ // Process each commit
385
+ for (const commit of commits) {
386
+ processedCount++
387
+ progress(processedCount, totalCommits, startTime, commit.hash.substring(0, 7))
388
+
389
+ const lines = await getLineStats(commit.hash)
390
+ const estimatedMinutes = estimateCommitTime(lines.added, lines.deleted, commit.message)
391
+
392
+ // Group by date
393
+ if (!commitsByDate[commit.date]) {
394
+ commitsByDate[commit.date] = {
395
+ commits: [],
396
+ lines: { added: 0, deleted: 0 },
397
+ estimatedMinutes: 0
398
+ }
399
+ }
400
+
401
+ // Accumulate statistics
402
+ commitsByDate[commit.date].commits.push(commit)
403
+ commitsByDate[commit.date].lines.added += lines.added
404
+ commitsByDate[commit.date].lines.deleted += lines.deleted
405
+ commitsByDate[commit.date].estimatedMinutes += estimatedMinutes
406
+
407
+ totalLines.added += lines.added
408
+ totalLines.deleted += lines.deleted
409
+ }
410
+
411
+ // Apply daily work limits
412
+ Object.values(commitsByDate).forEach(day => {
413
+ day.estimatedMinutes = Math.min(day.estimatedMinutes, COCOMO.MAX_HOURS_PER_DAY * 60)
414
+ })
415
+
416
+ progress(null)
417
+
418
+ // Branch statistics (only if analyzing all branches)
419
+ if (args.allBranches) {
420
+ const branchStats = commits.reduce((acc, commit) => {
421
+ if (commit.branch && commit.branch !== '') {
422
+ acc[commit.branch] = (acc[commit.branch] || 0) + 1
423
+ }
424
+ return acc
425
+ }, {})
426
+
427
+ console.log('\nCommits by branch:')
428
+ Object.entries(branchStats)
429
+ .sort(([, a], [, b]) => b - a)
430
+ .forEach(([branch, count]) => {
431
+ console.log(`${branch}: ${count} commits`)
432
+ })
433
+ }
434
+
435
+ /**
436
+ * Display daily breakdown of work
437
+ * Shows for each day:
438
+ * - Number of commits
439
+ * - Lines changed
440
+ * - Estimated work time
441
+ */
442
+ console.log('\nWork time estimation per date:')
443
+ Object.entries(commitsByDate)
444
+ .sort(([a], [b]) => new Date(b) - new Date(a))
445
+ .forEach(([date, stats]) => {
446
+ console.log(
447
+ `${date}: ${stats.commits.length} commits, ` +
448
+ `+${stats.lines.added}/-${stats.lines.deleted} lines, ` +
449
+ `≈ ${formatHours(stats.estimatedMinutes)}`
450
+ )
451
+ })
452
+
453
+ /**
454
+ * Calculate summary statistics
455
+ * Includes:
456
+ * - Total metrics (commits, lines, days)
457
+ * - Averages (per day, per commit)
458
+ * - Time estimations
459
+ */
460
+ const totalCommitCount = Object.values(commitsByDate)
461
+ .reduce((sum, { commits }) => sum + commits.length, 0)
462
+ const totalMinutes = Object.values(commitsByDate)
463
+ .reduce((sum, { estimatedMinutes }) => sum + estimatedMinutes, 0)
464
+ const uniqueEmails = [...new Set(commits.map(c => c.email))]
465
+ const totalDays = Object.keys(commitsByDate).length
466
+ const avgHoursPerDay = (totalMinutes / 60 / totalDays)
467
+ const avgCommitsPerDay = totalCommitCount / totalDays
468
+ const avgTimePerCommit = totalMinutes / totalCommitCount
469
+
470
+ /**
471
+ * Display comprehensive summary
472
+ * Shows overall project statistics and averages
473
+ */
474
+ console.log('\nSummary:')
475
+ console.log(`Total commits: ${totalCommitCount}`)
476
+ console.log(`Total lines added: ${totalLines.added}`)
477
+ console.log(`Total lines deleted: ${totalLines.deleted}`)
478
+ console.log(`Total active days: ${totalDays}`)
479
+ console.log(`Estimated total work time: ${formatHours(totalMinutes)}`)
480
+ console.log(`Average hours per active day: ${avgHoursPerDay.toFixed(1)}h`)
481
+ console.log(`Average commits per day: ${avgCommitsPerDay.toFixed(1)}`)
482
+ console.log(`Average time per commit: ${formatHours(Math.round(avgTimePerCommit))}`)
483
+ console.log(`Identified email addresses: ${uniqueEmails.join(', ')}`)
484
+
485
+ /**
486
+ * Display analysis configuration
487
+ * Shows all parameters used for the analysis
488
+ */
489
+ console.log('\nAnalysis Configuration:')
490
+ console.log('Repository Analysis:')
491
+ if (args.allBranches) {
492
+ console.log('- Analyzing all branches')
493
+ } else if (args.branch) {
494
+ console.log(`- Analyzing branch: ${args.branch}`)
495
+ } else {
496
+ console.log('- Analyzing current branch only')
497
+ }
498
+ if (args.noMerges) console.log('- Excluding merge commits')
499
+ if (args.since) console.log(`- Since: ${args.since}`)
500
+ if (args.until) console.log(`- Until: ${args.until}`)
501
+
502
+ /**
503
+ * Display estimation model details
504
+ * Shows all factors and multipliers used in calculations
505
+ */
506
+ console.log('\nEstimation Model Details:')
507
+ console.log('Time Calculation:')
508
+ console.log('- Using modified COCOMO II for large changes (>100 lines)')
509
+ console.log('- Linear estimation for small changes (3 min/line)')
510
+ console.log(`- Maximum ${COCOMO.MAX_HOURS_PER_DAY} hours per day`)
511
+ console.log('- Maximum 4 hours per commit')
512
+ console.log('- Minimum 10 minutes per commit')
513
+ console.log('- Deletions counted as 20% of additions')
514
+
515
+ console.log('\nCOCOMO II Parameters:')
516
+ console.log(`- Base effort coefficient (A): ${COCOMO.A}`)
517
+ console.log(`- Scale factor (B): ${COCOMO.B}`)
518
+ console.log(`- Person-month: ${COCOMO.PERSON_MONTH_HOURS} hours`)
519
+
520
+ console.log('\nEffort Multipliers:')
521
+ Object.entries(COCOMO.TYPE_MULTIPLIERS)
522
+ .sort(([, a], [, b]) => b - a)
523
+ .forEach(([type, mult]) => {
524
+ console.log(` * ${type}: ${mult}x`)
525
+ })
526
+ } catch (err) {
527
+ progress(null)
528
+ console.error('Error analyzing git log:', err)
529
+ process.exit(1)
530
+ }
531
+ }
532
+
533
+ /**
534
+ * Signal Handler
535
+ * Ensures clean exit on interrupt
536
+ * Restores cursor and terminal state
537
+ */
538
+ process.on('SIGINT', () => {
539
+ console.log('\nCaught interrupt signal')
540
+ progress(null)
541
+ process.exit()
542
+ })
543
+
544
+ // Start the analysis
545
+ getCommitStats(args.authors)
package/index.ts CHANGED
@@ -1,68 +1,10 @@
1
- // index.js
2
- import {
3
- createLayout,
4
- createElement,
5
- createBadge,
6
- createBottomAppBar,
7
- createButton,
8
- createDatePicker,
9
- createFab,
10
- createExtendedFab,
11
- createSegmentedButton,
12
- createCard,
13
- createCarousel,
14
- createCheckbox,
15
- createChip,
16
- createDialog,
17
- createDivider,
18
- createList,
19
- createMenu,
20
- createNavigation,
21
- createNavigationSystem,
22
- createProgress,
23
- createRadios,
24
- createSearch,
25
- createSheet,
26
- createSlider,
27
- createSnackbar,
28
- createTabs,
29
- createTextfield,
30
- createTimePicker,
31
- createTooltip,
32
- createTopAppBar,
33
- createSwitch
34
- } from './src/index.js'
1
+ // index.ts
2
+ /**
3
+ * Main mtrl library exports
4
+ *
5
+ * This file re-exports everything from the internal implementation.
6
+ *
7
+ * @packageDocumentation
8
+ */
35
9
 
36
- export {
37
- createLayout,
38
- createElement,
39
- createBadge,
40
- createBottomAppBar,
41
- createButton,
42
- createDatePicker,
43
- createFab,
44
- createExtendedFab,
45
- createSegmentedButton,
46
- createCard,
47
- createCarousel,
48
- createCheckbox,
49
- createChip,
50
- createDialog,
51
- createDivider,
52
- createList,
53
- createMenu,
54
- createNavigation,
55
- createNavigationSystem,
56
- createProgress,
57
- createRadios,
58
- createSearch,
59
- createSheet,
60
- createSlider,
61
- createSnackbar,
62
- createTabs,
63
- createTextfield,
64
- createTimePicker,
65
- createTooltip,
66
- createTopAppBar,
67
- createSwitch
68
- }
10
+ export * from './src/index';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mtrl",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "A functional TypeScript/JavaScript component library with composable architecture based on Material Design 3",
5
5
  "author": "floor",
6
6
  "license": "MIT License",
@@ -13,7 +13,11 @@
13
13
  "functional",
14
14
  "composable",
15
15
  "material design 3",
16
- "md3"
16
+ "md3",
17
+ "testing",
18
+ "jsdom",
19
+ "tested",
20
+ "type-safe"
17
21
  ],
18
22
  "type": "module",
19
23
  "main": "index.js",
@@ -45,6 +49,7 @@
45
49
  "@types/jsdom": "^21.1.7",
46
50
  "jsdom": "^26.0.0",
47
51
  "sass": "^1.85.1",
48
- "typedoc": "^0.27.9"
52
+ "typedoc": "^0.27.9",
53
+ "typescript": "^5.8.2"
49
54
  }
50
55
  }
@@ -2,6 +2,11 @@
2
2
  import { BadgeComponent, BadgeColor, BadgeVariant, BadgePosition } from './types';
3
3
  import { formatBadgeLabel } from './config';
4
4
 
5
+ /**
6
+ * API configuration options for badge component
7
+ * @category Components
8
+ * @internal
9
+ */
5
10
  interface ApiOptions {
6
11
  visibility: {
7
12
  show: () => void;
@@ -14,6 +19,11 @@ interface ApiOptions {
14
19
  };
15
20
  }
16
21
 
22
+ /**
23
+ * Component with required elements and methods for API enhancement
24
+ * @category Components
25
+ * @internal
26
+ */
17
27
  interface ComponentWithElements {
18
28
  element: HTMLElement;
19
29
  wrapper?: HTMLElement;
@@ -44,9 +54,13 @@ const ALL_VARIANTS = ['small', 'large'];
44
54
  const ALL_POSITIONS = ['top-right', 'top-left', 'bottom-right', 'bottom-left'];
45
55
 
46
56
  /**
47
- * Enhances a badge component with API methods
57
+ * Enhances a badge component with API methods.
58
+ * This follows the higher-order function pattern to add public API methods
59
+ * to the component, making them available to the end user.
60
+ *
48
61
  * @param {ApiOptions} options - API configuration options
49
62
  * @returns {Function} Higher-order function that adds API methods to component
63
+ * @category Components
50
64
  * @internal This is an internal utility for the Badge component
51
65
  */
52
66
  export const withAPI = ({ visibility, lifecycle }: ApiOptions) =>