kimaki 0.4.33 → 0.4.35

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.
@@ -12,8 +12,7 @@ test('basic - single item with code block', () => {
12
12
 
13
13
  \`\`\`js
14
14
  const x = 1
15
- \`\`\`
16
- "
15
+ \`\`\`"
17
16
  `)
18
17
  })
19
18
 
@@ -53,8 +52,7 @@ test('multiple code blocks in one item', () => {
53
52
  \`\`\`
54
53
  \`\`\`python
55
54
  b = 2
56
- \`\`\`
57
- "
55
+ \`\`\`"
58
56
  `)
59
57
  })
60
58
 
@@ -132,8 +130,7 @@ test('mixed - some items have code, some dont', () => {
132
130
 
133
131
  \`\`\`python
134
132
  y = 2
135
- \`\`\`
136
- "
133
+ \`\`\`"
137
134
  `)
138
135
  })
139
136
 
@@ -150,8 +147,7 @@ test('text before and after code in same item', () => {
150
147
  \`\`\`js
151
148
  const x = 1
152
149
  \`\`\`
153
- - End text
154
- "
150
+ - End text"
155
151
  `)
156
152
  })
157
153
 
@@ -178,7 +174,6 @@ More text after.`
178
174
  const x = 1
179
175
  \`\`\`
180
176
 
181
-
182
177
  More text after."
183
178
  `)
184
179
  })
@@ -206,8 +201,7 @@ test('handles code block without language', () => {
206
201
 
207
202
  \`\`\`
208
203
  plain code
209
- \`\`\`
210
- "
204
+ \`\`\`"
211
205
  `)
212
206
  })
213
207
 
@@ -219,7 +213,243 @@ test('handles empty list item with code', () => {
219
213
  expect(result).toMatchInlineSnapshot(`
220
214
  "\`\`\`js
221
215
  const x = 1
216
+ \`\`\`"
217
+ `)
218
+ })
219
+
220
+ test('numbered list with text after code block', () => {
221
+ const input = `1. First item
222
+ \`\`\`js
223
+ const a = 1
224
+ \`\`\`
225
+ Text after the code
226
+ 2. Second item`
227
+ const result = unnestCodeBlocksFromLists(input)
228
+ expect(result).toMatchInlineSnapshot(`
229
+ "1. First item
230
+
231
+ \`\`\`js
232
+ const a = 1
233
+ \`\`\`
234
+ - Text after the code
235
+ 2. Second item"
236
+ `)
237
+ })
238
+
239
+ test('numbered list with multiple code blocks and text between', () => {
240
+ const input = `1. First item
241
+ \`\`\`js
242
+ const a = 1
243
+ \`\`\`
244
+ Middle text
245
+ \`\`\`python
246
+ b = 2
247
+ \`\`\`
248
+ Final text
249
+ 2. Second item`
250
+ const result = unnestCodeBlocksFromLists(input)
251
+ expect(result).toMatchInlineSnapshot(`
252
+ "1. First item
253
+
254
+ \`\`\`js
255
+ const a = 1
256
+ \`\`\`
257
+ - Middle text
258
+
259
+ \`\`\`python
260
+ b = 2
261
+ \`\`\`
262
+ - Final text
263
+ 2. Second item"
264
+ `)
265
+ })
266
+
267
+ test('unordered list with multiple code blocks and text between', () => {
268
+ const input = `- First item
269
+ \`\`\`js
270
+ const a = 1
271
+ \`\`\`
272
+ Middle text
273
+ \`\`\`python
274
+ b = 2
275
+ \`\`\`
276
+ Final text
277
+ - Second item`
278
+ const result = unnestCodeBlocksFromLists(input)
279
+ expect(result).toMatchInlineSnapshot(`
280
+ "- First item
281
+
282
+ \`\`\`js
283
+ const a = 1
284
+ \`\`\`
285
+ - Middle text
286
+
287
+ \`\`\`python
288
+ b = 2
289
+ \`\`\`
290
+ - Final text
291
+ - Second item"
292
+ `)
293
+ })
294
+
295
+ test('numbered list starting from 5', () => {
296
+ const input = `5. Fifth item
297
+ \`\`\`js
298
+ code
299
+ \`\`\`
300
+ Text after
301
+ 6. Sixth item`
302
+ const result = unnestCodeBlocksFromLists(input)
303
+ expect(result).toMatchInlineSnapshot(`
304
+ "5. Fifth item
305
+
306
+ \`\`\`js
307
+ code
308
+ \`\`\`
309
+ - Text after
310
+ 6. Sixth item"
311
+ `)
312
+ })
313
+
314
+ test('deeply nested list with code', () => {
315
+ const input = `- Level 1
316
+ - Level 2
317
+ - Level 3
318
+ \`\`\`js
319
+ deep code
320
+ \`\`\`
321
+ Text after deep code
322
+ - Another level 3
323
+ - Back to level 2`
324
+ const result = unnestCodeBlocksFromLists(input)
325
+ expect(result).toMatchInlineSnapshot(`
326
+ "- Level 1
327
+ - Level 2
328
+ - Level 3
329
+
330
+ \`\`\`js
331
+ deep code
332
+ \`\`\`
333
+ - Text after deep code
334
+ - Another level 3
335
+ - Back to level 2"
336
+ `)
337
+ })
338
+
339
+ test('nested numbered list inside unordered with code', () => {
340
+ const input = `- Unordered item
341
+ 1. Nested numbered
342
+ \`\`\`js
343
+ code
344
+ \`\`\`
345
+ Text after
346
+ 2. Second nested
347
+ - Another unordered`
348
+ const result = unnestCodeBlocksFromLists(input)
349
+ expect(result).toMatchInlineSnapshot(`
350
+ "- Unordered item
351
+ 1. Nested numbered
352
+
353
+ \`\`\`js
354
+ code
355
+ \`\`\`
356
+ - Text after
357
+ 2. Second nested
358
+ - Another unordered"
359
+ `)
360
+ })
361
+
362
+ test('code block at end of numbered item no text after', () => {
363
+ const input = `1. First with text
364
+ \`\`\`js
365
+ code here
366
+ \`\`\`
367
+ 2. Second item
368
+ 3. Third item`
369
+ const result = unnestCodeBlocksFromLists(input)
370
+ expect(result).toMatchInlineSnapshot(`
371
+ "1. First with text
372
+
373
+ \`\`\`js
374
+ code here
375
+ \`\`\`
376
+ 2. Second item
377
+ 3. Third item"
378
+ `)
379
+ })
380
+
381
+ test('multiple items each with code and text after', () => {
382
+ const input = `1. First
383
+ \`\`\`js
384
+ code1
385
+ \`\`\`
386
+ After first
387
+ 2. Second
388
+ \`\`\`python
389
+ code2
390
+ \`\`\`
391
+ After second
392
+ 3. Third no code`
393
+ const result = unnestCodeBlocksFromLists(input)
394
+ expect(result).toMatchInlineSnapshot(`
395
+ "1. First
396
+
397
+ \`\`\`js
398
+ code1
399
+ \`\`\`
400
+ - After first
401
+ 2. Second
402
+
403
+ \`\`\`python
404
+ code2
405
+ \`\`\`
406
+ - After second
407
+ 3. Third no code"
408
+ `)
409
+ })
410
+
411
+ test('code block immediately after list marker', () => {
412
+ const input = `1. \`\`\`js
413
+ immediate code
414
+ \`\`\`
415
+ 2. Normal item`
416
+ const result = unnestCodeBlocksFromLists(input)
417
+ expect(result).toMatchInlineSnapshot(`
418
+ "\`\`\`js
419
+ immediate code
420
+ \`\`\`
421
+ 2. Normal item"
422
+ `)
423
+ })
424
+
425
+ test('code block with filename metadata', () => {
426
+ const input = `- Item with code
427
+ \`\`\`tsx filename=example.tsx
428
+ const x = 1
429
+ \`\`\``
430
+ const result = unnestCodeBlocksFromLists(input)
431
+ expect(result).toMatchInlineSnapshot(`
432
+ "- Item with code
433
+
434
+ \`\`\`tsx filename=example.tsx
435
+ const x = 1
436
+ \`\`\`"
437
+ `)
438
+ })
439
+
440
+ test('numbered list with filename metadata code block', () => {
441
+ const input = `1. First item
442
+ \`\`\`tsx filename=app.tsx
443
+ export default function App() {}
444
+ \`\`\`
445
+ 2. Second item`
446
+ const result = unnestCodeBlocksFromLists(input)
447
+ expect(result).toMatchInlineSnapshot(`
448
+ "1. First item
449
+
450
+ \`\`\`tsx filename=app.tsx
451
+ export default function App() {}
222
452
  \`\`\`
223
- "
453
+ 2. Second item"
224
454
  `)
225
455
  })
@@ -41,11 +41,15 @@ function processListToken(list: Tokens.List): Segment[] {
41
41
  function processListItem(item: Tokens.ListItem, prefix: string): Segment[] {
42
42
  const segments: Segment[] = []
43
43
  let currentText: string[] = []
44
+ // Track if we've seen a code block - text after code uses continuation prefix
45
+ let seenCodeBlock = false
44
46
 
45
47
  const flushText = (): void => {
46
48
  const text = currentText.join('').trim()
47
49
  if (text) {
48
- segments.push({ type: 'list-item', prefix, content: text })
50
+ // After a code block, use '-' as continuation prefix to avoid repeating numbers
51
+ const effectivePrefix = seenCodeBlock ? '- ' : prefix
52
+ segments.push({ type: 'list-item', prefix: effectivePrefix, content: text })
49
53
  }
50
54
  currentText = []
51
55
  }
@@ -59,6 +63,7 @@ function processListItem(item: Tokens.ListItem, prefix: string): Segment[] {
59
63
  type: 'code',
60
64
  content: '```' + lang + '\n' + codeToken.text + '\n```\n',
61
65
  })
66
+ seenCodeBlock = true
62
67
  } else if (token.type === 'list') {
63
68
  flushText()
64
69
  // Recursively process nested list - segments bubble up
@@ -118,10 +123,12 @@ function renderSegments(segments: Segment[]): string {
118
123
  result.push(segment.prefix + segment.content + '\n')
119
124
  } else {
120
125
  // Raw content (no prefix means it's original raw)
121
- result.push(segment.content)
126
+ // Ensure raw ends with newline for proper separation from next segment
127
+ const raw = segment.content.trimEnd()
128
+ result.push(raw + '\n')
122
129
  }
123
130
  }
124
131
  }
125
132
 
126
- return result.join('')
133
+ return result.join('').trimEnd()
127
134
  }