@autumnsgrove/groveengine 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/jwt.d.ts +10 -4
- package/dist/auth/jwt.js +18 -4
- package/dist/auth/session.d.ts +22 -15
- package/dist/auth/session.js +35 -16
- package/dist/components/admin/GutterManager.svelte +81 -139
- package/dist/components/admin/GutterManager.svelte.d.ts +6 -6
- package/dist/components/admin/MarkdownEditor.svelte +80 -23
- package/dist/components/admin/MarkdownEditor.svelte.d.ts +14 -8
- package/dist/components/admin/composables/useAmbientSounds.svelte.d.ts +52 -2
- package/dist/components/admin/composables/useAmbientSounds.svelte.js +38 -4
- package/dist/components/admin/composables/useCommandPalette.svelte.d.ts +80 -10
- package/dist/components/admin/composables/useCommandPalette.svelte.js +45 -5
- package/dist/components/admin/composables/useDraftManager.svelte.d.ts +76 -14
- package/dist/components/admin/composables/useDraftManager.svelte.js +44 -10
- package/dist/components/admin/composables/useEditorTheme.svelte.d.ts +168 -2
- package/dist/components/admin/composables/useEditorTheme.svelte.js +40 -7
- package/dist/components/admin/composables/useSlashCommands.svelte.d.ts +94 -22
- package/dist/components/admin/composables/useSlashCommands.svelte.js +58 -9
- package/dist/components/admin/composables/useSnippets.svelte.d.ts +51 -2
- package/dist/components/admin/composables/useSnippets.svelte.js +35 -3
- package/dist/components/admin/composables/useWritingSession.svelte.d.ts +64 -6
- package/dist/components/admin/composables/useWritingSession.svelte.js +42 -5
- package/dist/components/custom/ContentWithGutter.svelte +53 -23
- package/dist/components/custom/ContentWithGutter.svelte.d.ts +6 -14
- package/dist/components/custom/GutterItem.svelte +1 -1
- package/dist/components/custom/LeftGutter.svelte +43 -13
- package/dist/components/custom/LeftGutter.svelte.d.ts +6 -6
- package/dist/config/ai-models.js +1 -1
- package/dist/groveauth/client.js +11 -11
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -2
- package/dist/server/logger.d.ts +74 -26
- package/dist/server/logger.js +133 -184
- package/dist/server/services/cache.js +1 -10
- package/dist/ui/components/charts/ActivityOverview.svelte +14 -3
- package/dist/ui/components/charts/ActivityOverview.svelte.d.ts +10 -7
- package/dist/ui/components/charts/RepoBreakdown.svelte +9 -3
- package/dist/ui/components/charts/RepoBreakdown.svelte.d.ts +12 -11
- package/dist/ui/components/charts/Sparkline.svelte +18 -7
- package/dist/ui/components/charts/Sparkline.svelte.d.ts +21 -2
- package/dist/ui/components/gallery/ImageGallery.svelte +12 -8
- package/dist/ui/components/gallery/ImageGallery.svelte.d.ts +2 -2
- package/dist/ui/components/gallery/Lightbox.svelte +5 -2
- package/dist/ui/components/gallery/ZoomableImage.svelte +8 -5
- package/dist/ui/components/primitives/accordion/index.d.ts +1 -1
- package/dist/ui/components/primitives/input/input.svelte.d.ts +1 -1
- package/dist/ui/components/primitives/tabs/index.d.ts +1 -1
- package/dist/ui/components/primitives/textarea/textarea.svelte.d.ts +1 -1
- package/dist/ui/components/ui/Button.svelte +5 -0
- package/dist/ui/components/ui/Button.svelte.d.ts +4 -1
- package/dist/ui/components/ui/Input.svelte +4 -0
- package/dist/ui/components/ui/Input.svelte.d.ts +3 -1
- package/dist/ui/components/ui/Logo.svelte +86 -0
- package/dist/ui/components/ui/Logo.svelte.d.ts +25 -0
- package/dist/ui/components/ui/LogoLoader.svelte +71 -0
- package/dist/ui/components/ui/LogoLoader.svelte.d.ts +9 -0
- package/dist/ui/components/ui/index.d.ts +2 -0
- package/dist/ui/components/ui/index.js +2 -0
- package/dist/ui/tailwind.preset.js +8 -8
- package/dist/utils/api.js +2 -1
- package/dist/utils/debounce.d.ts +4 -3
- package/dist/utils/debounce.js +10 -6
- package/dist/utils/gallery.d.ts +58 -32
- package/dist/utils/gallery.js +111 -129
- package/dist/utils/gutter.d.ts +47 -26
- package/dist/utils/gutter.js +116 -124
- package/dist/utils/imageProcessor.d.ts +66 -19
- package/dist/utils/imageProcessor.js +31 -10
- package/dist/utils/index.d.ts +11 -11
- package/dist/utils/index.js +4 -3
- package/dist/utils/json.js +1 -1
- package/dist/utils/markdown.d.ts +183 -103
- package/dist/utils/markdown.js +517 -678
- package/dist/utils/sanitize.d.ts +22 -12
- package/dist/utils/sanitize.js +268 -282
- package/dist/utils/validation.js +4 -3
- package/package.json +4 -3
package/dist/server/logger.js
CHANGED
|
@@ -2,251 +2,200 @@
|
|
|
2
2
|
* Server-side logging utility with in-memory circular buffers
|
|
3
3
|
* Supports real-time log streaming to admin console
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
// Circular buffer implementation
|
|
7
6
|
class CircularBuffer {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
maxSize;
|
|
8
|
+
buffer;
|
|
9
|
+
constructor(maxSize = 1000) {
|
|
10
|
+
this.maxSize = maxSize;
|
|
11
|
+
this.buffer = [];
|
|
12
|
+
}
|
|
13
|
+
push(item) {
|
|
14
|
+
this.buffer.push(item);
|
|
15
|
+
if (this.buffer.length > this.maxSize) {
|
|
16
|
+
this.buffer.shift(); // Remove oldest item
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
getAll() {
|
|
20
|
+
return [...this.buffer]; // Return copy to prevent external mutations
|
|
21
|
+
}
|
|
22
|
+
getSince(timestamp) {
|
|
23
|
+
return this.buffer.filter((log) => new Date(log.timestamp) > new Date(timestamp));
|
|
24
|
+
}
|
|
25
|
+
clear() {
|
|
26
|
+
this.buffer = [];
|
|
27
|
+
}
|
|
28
|
+
get length() {
|
|
29
|
+
return this.buffer.length;
|
|
17
30
|
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
getAll() {
|
|
21
|
-
return [...this.buffer]; // Return copy to prevent external mutations
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
getSince(timestamp) {
|
|
25
|
-
return this.buffer.filter(
|
|
26
|
-
(log) => new Date(log.timestamp) > new Date(timestamp),
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
clear() {
|
|
31
|
-
this.buffer = [];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get length() {
|
|
35
|
-
return this.buffer.length;
|
|
36
|
-
}
|
|
37
31
|
}
|
|
38
|
-
|
|
39
32
|
// Log buffers for each category (module-level, persists across requests in same worker)
|
|
40
33
|
const logBuffers = {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
api: new CircularBuffer(1000),
|
|
35
|
+
github: new CircularBuffer(500),
|
|
36
|
+
errors: new CircularBuffer(500),
|
|
37
|
+
cache: new CircularBuffer(1000),
|
|
45
38
|
};
|
|
46
|
-
|
|
47
39
|
// Subscribers for SSE streaming
|
|
48
40
|
const subscribers = new Set();
|
|
49
|
-
|
|
50
41
|
/**
|
|
51
42
|
* Create a log entry with consistent format
|
|
52
43
|
*/
|
|
53
44
|
function createLogEntry(level, category, message, metadata = {}) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
return {
|
|
46
|
+
id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
level,
|
|
49
|
+
category,
|
|
50
|
+
message,
|
|
51
|
+
metadata,
|
|
52
|
+
};
|
|
62
53
|
}
|
|
63
|
-
|
|
64
54
|
/**
|
|
65
55
|
* Broadcast log to all SSE subscribers
|
|
66
56
|
*/
|
|
67
57
|
function broadcast(log) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
58
|
+
subscribers.forEach((subscriber) => {
|
|
59
|
+
try {
|
|
60
|
+
subscriber(log);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.error("[Logger] Failed to broadcast to subscriber:", error);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
75
66
|
}
|
|
76
|
-
|
|
77
67
|
/**
|
|
78
68
|
* Log API activity (requests, responses, timing)
|
|
79
69
|
*/
|
|
80
70
|
export function logAPI(endpoint, method, status, metadata = {}) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
logBuffers.api.push(log);
|
|
102
|
-
broadcast(log);
|
|
103
|
-
|
|
104
|
-
// Also log to console in development
|
|
105
|
-
if (metadata.duration) {
|
|
106
|
-
console.log(
|
|
107
|
-
`[API] ${method} ${endpoint} ${status} (${metadata.duration}ms)`,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
71
|
+
const level = status >= 500
|
|
72
|
+
? "error"
|
|
73
|
+
: status >= 400
|
|
74
|
+
? "warn"
|
|
75
|
+
: status >= 200
|
|
76
|
+
? "success"
|
|
77
|
+
: "info";
|
|
78
|
+
const log = createLogEntry(level, "api", `${method} ${endpoint} → ${status}`, {
|
|
79
|
+
endpoint,
|
|
80
|
+
method,
|
|
81
|
+
status,
|
|
82
|
+
...metadata,
|
|
83
|
+
});
|
|
84
|
+
logBuffers.api.push(log);
|
|
85
|
+
broadcast(log);
|
|
86
|
+
// Also log to console in development
|
|
87
|
+
if (metadata.duration) {
|
|
88
|
+
console.log(`[API] ${method} ${endpoint} ${status} (${metadata.duration}ms)`);
|
|
89
|
+
}
|
|
110
90
|
}
|
|
111
|
-
|
|
112
91
|
/**
|
|
113
92
|
* Log GitHub API operations (rate limits, queries, errors)
|
|
114
93
|
*/
|
|
115
94
|
export function logGitHub(operation, metadata = {}) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
`[GitHub] ${operation}`,
|
|
124
|
-
metadata.rateLimit
|
|
125
|
-
? `(${metadata.rateLimit.remaining}/${metadata.rateLimit.limit} remaining)`
|
|
126
|
-
: "",
|
|
127
|
-
);
|
|
95
|
+
const level = metadata.error ? "error" : metadata.warning ? "warn" : "info";
|
|
96
|
+
const log = createLogEntry(level, "github", operation, metadata);
|
|
97
|
+
logBuffers.github.push(log);
|
|
98
|
+
broadcast(log);
|
|
99
|
+
console.log(`[GitHub] ${operation}`, metadata.rateLimit
|
|
100
|
+
? `(${metadata.rateLimit.remaining}/${metadata.rateLimit.limit} remaining)`
|
|
101
|
+
: "");
|
|
128
102
|
}
|
|
129
|
-
|
|
130
103
|
/**
|
|
131
104
|
* Log errors (exceptions, failed operations, validation errors)
|
|
132
105
|
*/
|
|
133
106
|
export function logError(message, error = null, metadata = {}) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
console.error(`[Error] ${message}`, error || "");
|
|
107
|
+
const log = createLogEntry("error", "errors", message, {
|
|
108
|
+
error: error
|
|
109
|
+
? {
|
|
110
|
+
message: error.message,
|
|
111
|
+
stack: error.stack,
|
|
112
|
+
name: error.name,
|
|
113
|
+
}
|
|
114
|
+
: null,
|
|
115
|
+
...metadata,
|
|
116
|
+
});
|
|
117
|
+
logBuffers.errors.push(log);
|
|
118
|
+
broadcast(log);
|
|
119
|
+
console.error(`[Error] ${message}`, error || "");
|
|
149
120
|
}
|
|
150
|
-
|
|
151
121
|
/**
|
|
152
122
|
* Log cache operations (KV get/set, hits/misses)
|
|
153
123
|
*/
|
|
154
124
|
export function logCache(operation, key, metadata = {}) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
...metadata,
|
|
164
|
-
},
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
logBuffers.cache.push(log);
|
|
168
|
-
broadcast(log);
|
|
125
|
+
const level = metadata.error ? "error" : "info";
|
|
126
|
+
const log = createLogEntry(level, "cache", `${operation.toUpperCase()} ${key}`, {
|
|
127
|
+
operation,
|
|
128
|
+
key,
|
|
129
|
+
...metadata,
|
|
130
|
+
});
|
|
131
|
+
logBuffers.cache.push(log);
|
|
132
|
+
broadcast(log);
|
|
169
133
|
}
|
|
170
|
-
|
|
171
134
|
/**
|
|
172
135
|
* Get logs from a specific category
|
|
173
136
|
*/
|
|
174
137
|
export function getLogs(category, since = null) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return logBuffers[category].getAll();
|
|
138
|
+
if (!logBuffers[category]) {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
if (since) {
|
|
142
|
+
return logBuffers[category].getSince(since);
|
|
143
|
+
}
|
|
144
|
+
return logBuffers[category].getAll();
|
|
184
145
|
}
|
|
185
|
-
|
|
186
146
|
/**
|
|
187
147
|
* Get all logs across all categories
|
|
188
148
|
*/
|
|
189
149
|
export function getAllLogs(since = null) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// Sort by timestamp (newest first)
|
|
200
|
-
return allLogs.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
|
|
150
|
+
const allLogs = [];
|
|
151
|
+
for (const category of Object.keys(logBuffers)) {
|
|
152
|
+
const logs = since
|
|
153
|
+
? logBuffers[category].getSince(since)
|
|
154
|
+
: logBuffers[category].getAll();
|
|
155
|
+
allLogs.push(...logs);
|
|
156
|
+
}
|
|
157
|
+
// Sort by timestamp (newest first)
|
|
158
|
+
return allLogs.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
201
159
|
}
|
|
202
|
-
|
|
203
160
|
/**
|
|
204
161
|
* Get log statistics
|
|
205
162
|
*/
|
|
206
163
|
export function getLogStats() {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
cache: {
|
|
227
|
-
total: logBuffers.cache.length,
|
|
228
|
-
recent: logBuffers.cache.getSince(
|
|
229
|
-
new Date(Date.now() - 60000).toISOString(),
|
|
230
|
-
).length,
|
|
231
|
-
},
|
|
232
|
-
};
|
|
164
|
+
const oneMinuteAgo = new Date(Date.now() - 60000).toISOString();
|
|
165
|
+
return {
|
|
166
|
+
api: {
|
|
167
|
+
total: logBuffers.api.length,
|
|
168
|
+
recent: logBuffers.api.getSince(oneMinuteAgo).length,
|
|
169
|
+
},
|
|
170
|
+
github: {
|
|
171
|
+
total: logBuffers.github.length,
|
|
172
|
+
recent: logBuffers.github.getSince(oneMinuteAgo).length,
|
|
173
|
+
},
|
|
174
|
+
errors: {
|
|
175
|
+
total: logBuffers.errors.length,
|
|
176
|
+
recent: logBuffers.errors.getSince(oneMinuteAgo).length,
|
|
177
|
+
},
|
|
178
|
+
cache: {
|
|
179
|
+
total: logBuffers.cache.length,
|
|
180
|
+
recent: logBuffers.cache.getSince(oneMinuteAgo).length,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
233
183
|
}
|
|
234
|
-
|
|
235
184
|
/**
|
|
236
185
|
* Subscribe to log events (for SSE streaming)
|
|
237
186
|
*/
|
|
238
187
|
export function subscribe(callback) {
|
|
239
|
-
|
|
240
|
-
|
|
188
|
+
subscribers.add(callback);
|
|
189
|
+
return () => subscribers.delete(callback);
|
|
241
190
|
}
|
|
242
|
-
|
|
243
191
|
/**
|
|
244
192
|
* Clear logs for a specific category or all categories
|
|
245
193
|
*/
|
|
246
194
|
export function clearLogs(category = null) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
195
|
+
if (category && logBuffers[category]) {
|
|
196
|
+
logBuffers[category].clear();
|
|
197
|
+
}
|
|
198
|
+
else if (!category) {
|
|
199
|
+
Object.values(logBuffers).forEach((buffer) => buffer.clear());
|
|
200
|
+
}
|
|
252
201
|
}
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Cache Service - KV Key-Value Store Abstraction
|
|
3
|
-
*
|
|
4
|
-
* Provides typed caching operations with:
|
|
5
|
-
* - Automatic JSON serialization/deserialization
|
|
6
|
-
* - TTL management with sensible defaults
|
|
7
|
-
* - Namespace prefixing to avoid key collisions
|
|
8
|
-
* - Compute-if-missing pattern (getOrSet)
|
|
9
|
-
* - Specific error types for debugging
|
|
10
|
-
*/
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
11
2
|
// ============================================================================
|
|
12
3
|
// Errors
|
|
13
4
|
// ============================================================================
|
|
@@ -9,16 +9,26 @@
|
|
|
9
9
|
import Sparkline from './Sparkline.svelte';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @
|
|
12
|
+
* @typedef {Object} ActivityData
|
|
13
|
+
* @property {string} activity_date
|
|
14
|
+
* @property {number} commit_count
|
|
13
15
|
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} LocData
|
|
19
|
+
* @property {number} additions
|
|
20
|
+
* @property {number} deletions
|
|
21
|
+
*/
|
|
22
|
+
|
|
14
23
|
let {
|
|
15
|
-
data = [],
|
|
16
|
-
locData = { additions: 0, deletions: 0 },
|
|
24
|
+
data = /** @type {ActivityData[]} */ ([]),
|
|
25
|
+
locData = /** @type {LocData} */ ({ additions: 0, deletions: 0 }),
|
|
17
26
|
days = 14
|
|
18
27
|
} = $props();
|
|
19
28
|
|
|
20
29
|
// Format date as YYYY-MM-DD in local timezone (not UTC!)
|
|
21
30
|
// This matches how GitHub attributes contributions to user's timezone
|
|
31
|
+
/** @param {Date} date */
|
|
22
32
|
function formatDateKey(date) {
|
|
23
33
|
const year = date.getFullYear();
|
|
24
34
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
@@ -61,6 +71,7 @@
|
|
|
61
71
|
const peakCommits = $derived(Math.max(...filledData().map(d => d.commits), 0));
|
|
62
72
|
|
|
63
73
|
// Get intensity level for heatmap (0-4)
|
|
74
|
+
/** @param {number} commits */
|
|
64
75
|
function getIntensity(commits) {
|
|
65
76
|
if (commits === 0) return 0;
|
|
66
77
|
if (commits <= 2) return 1;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export default ActivityOverview;
|
|
2
2
|
type ActivityOverview = {
|
|
3
3
|
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
-
$set?(props:
|
|
5
|
-
activity_date: string;
|
|
6
|
-
commit_count: number;
|
|
7
|
-
} | undefined)[]): void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
8
5
|
};
|
|
9
6
|
declare const ActivityOverview: import("svelte").Component<{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
data?: any;
|
|
8
|
+
locData?: any;
|
|
9
|
+
days?: number;
|
|
10
|
+
}, {}, "">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
data?: any;
|
|
13
|
+
locData?: any;
|
|
14
|
+
days?: number;
|
|
15
|
+
};
|
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @
|
|
8
|
+
* @typedef {Object} RepoData
|
|
9
|
+
* @property {string} name
|
|
10
|
+
* @property {number} [commits]
|
|
11
|
+
* @property {number} [additions]
|
|
12
|
+
* @property {number} [deletions]
|
|
9
13
|
*/
|
|
14
|
+
|
|
10
15
|
let {
|
|
11
|
-
repos = [],
|
|
12
|
-
mode = 'commits'
|
|
16
|
+
repos = /** @type {RepoData[]} */ ([]),
|
|
17
|
+
mode = /** @type {'commits' | 'loc'} */ ('commits'),
|
|
13
18
|
maxWidth = 150,
|
|
14
19
|
showLegend = true
|
|
15
20
|
} = $props();
|
|
@@ -33,6 +38,7 @@
|
|
|
33
38
|
return repos.reduce((sum, r) => sum + (r.additions || 0) + (r.deletions || 0), 0) || 1;
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
/** @param {RepoData} repo */
|
|
36
42
|
function getValue(repo) {
|
|
37
43
|
if (mode === 'commits') {
|
|
38
44
|
return repo.commits || 1;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
export default RepoBreakdown;
|
|
2
2
|
type RepoBreakdown = {
|
|
3
3
|
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
-
$set?(props:
|
|
5
|
-
name: string;
|
|
6
|
-
commits?: number | undefined;
|
|
7
|
-
additions?: number | undefined;
|
|
8
|
-
deletions?: number | undefined;
|
|
9
|
-
} | undefined)[]): void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
10
5
|
};
|
|
11
6
|
declare const RepoBreakdown: import("svelte").Component<{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
7
|
+
repos?: any;
|
|
8
|
+
mode?: any;
|
|
9
|
+
maxWidth?: number;
|
|
10
|
+
showLegend?: boolean;
|
|
11
|
+
}, {}, "">;
|
|
12
|
+
type $$ComponentProps = {
|
|
13
|
+
repos?: any;
|
|
14
|
+
mode?: any;
|
|
15
|
+
maxWidth?: number;
|
|
16
|
+
showLegend?: boolean;
|
|
17
|
+
};
|
|
@@ -4,9 +4,20 @@
|
|
|
4
4
|
* Shows activity trend over time (commits, LOC, etc.)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} SparklineProps
|
|
9
|
+
* @property {number[]} [data] - Data points for the sparkline
|
|
10
|
+
* @property {number} [width] - Width of the chart
|
|
11
|
+
* @property {number} [height] - Height of the chart
|
|
12
|
+
* @property {string} [strokeColor] - Line color
|
|
13
|
+
* @property {string} [fillColor] - Fill color for area
|
|
14
|
+
* @property {number} [strokeWidth] - Line thickness
|
|
15
|
+
* @property {boolean} [showDots] - Show data point dots
|
|
16
|
+
* @property {boolean} [showArea] - Show filled area
|
|
17
|
+
*/
|
|
18
|
+
|
|
8
19
|
let {
|
|
9
|
-
data = [],
|
|
20
|
+
data = /** @type {number[]} */ ([]),
|
|
10
21
|
width = 120,
|
|
11
22
|
height = 24,
|
|
12
23
|
strokeColor = '#5cb85f',
|
|
@@ -24,14 +35,14 @@
|
|
|
24
35
|
const min = Math.min(...data, 0);
|
|
25
36
|
const range = max - min || 1;
|
|
26
37
|
|
|
27
|
-
const points = data.map((value, i) => {
|
|
38
|
+
const points = data.map((/** @type {number} */ value, /** @type {number} */ i) => {
|
|
28
39
|
const x = (i / (data.length - 1)) * width;
|
|
29
40
|
const y = height - ((value - min) / range) * (height - 4) - 2; // 2px padding
|
|
30
41
|
return { x, y };
|
|
31
42
|
});
|
|
32
43
|
|
|
33
44
|
// Create line path
|
|
34
|
-
const linePath = points.map((p, i) =>
|
|
45
|
+
const linePath = points.map((/** @type {{ x: number, y: number }} */ p, /** @type {number} */ i) =>
|
|
35
46
|
`${i === 0 ? 'M' : 'L'} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`
|
|
36
47
|
).join(' ');
|
|
37
48
|
|
|
@@ -45,14 +56,14 @@
|
|
|
45
56
|
const min = Math.min(...data, 0);
|
|
46
57
|
const range = max - min || 1;
|
|
47
58
|
|
|
48
|
-
const points = data.map((value, i) => {
|
|
59
|
+
const points = data.map((/** @type {number} */ value, /** @type {number} */ i) => {
|
|
49
60
|
const x = (i / (data.length - 1)) * width;
|
|
50
61
|
const y = height - ((value - min) / range) * (height - 4) - 2;
|
|
51
62
|
return { x, y };
|
|
52
63
|
});
|
|
53
64
|
|
|
54
65
|
// Create closed area path
|
|
55
|
-
const linePath = points.map((p, i) =>
|
|
66
|
+
const linePath = points.map((/** @type {{ x: number, y: number }} */ p, /** @type {number} */ i) =>
|
|
56
67
|
`${i === 0 ? 'M' : 'L'} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`
|
|
57
68
|
).join(' ');
|
|
58
69
|
|
|
@@ -66,7 +77,7 @@
|
|
|
66
77
|
const min = Math.min(...data, 0);
|
|
67
78
|
const range = max - min || 1;
|
|
68
79
|
|
|
69
|
-
return data.map((value, i) => ({
|
|
80
|
+
return data.map((/** @type {number} */ value, /** @type {number} */ i) => ({
|
|
70
81
|
x: (i / (data.length - 1)) * width,
|
|
71
82
|
y: height - ((value - min) / range) * (height - 4) - 2,
|
|
72
83
|
value
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
export default Sparkline;
|
|
2
2
|
type Sparkline = {
|
|
3
3
|
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
-
$set?(props:
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Sparkline: import("svelte").Component<{
|
|
7
|
+
data?: any;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
strokeColor?: string;
|
|
11
|
+
fillColor?: string;
|
|
12
|
+
strokeWidth?: number;
|
|
13
|
+
showDots?: boolean;
|
|
14
|
+
showArea?: boolean;
|
|
15
|
+
}, {}, "">;
|
|
16
|
+
type $$ComponentProps = {
|
|
17
|
+
data?: any;
|
|
18
|
+
width?: number;
|
|
19
|
+
height?: number;
|
|
20
|
+
strokeColor?: string;
|
|
21
|
+
fillColor?: string;
|
|
22
|
+
strokeWidth?: number;
|
|
23
|
+
showDots?: boolean;
|
|
24
|
+
showArea?: boolean;
|
|
5
25
|
};
|
|
6
|
-
declare const Sparkline: import("svelte").Component<number[], {}, "">;
|