lyrics-structure 1.2.1 → 1.2.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.
package/.prettierrc ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "es5",
4
+ "singleQuote": true,
5
+ "printWidth": 100,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "bracketSpacing": true,
9
+ "arrowParens": "always"
10
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
4
+ "editor.codeActionsOnSave": {
5
+ "source.fixAll": true
6
+ },
7
+ "[typescript]": {
8
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
9
+ },
10
+ "[javascript]": {
11
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
12
+ },
13
+ "[json]": {
14
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
15
+ },
16
+ "[markdown]": {
17
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
18
+ }
19
+ }
package/CHANGELOG.md CHANGED
@@ -1,18 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.2] - 2024-04-06
4
+
5
+ ### Fixed
6
+
7
+ - Fixed issue where consecutive lines without part containers were incorrectly split into separate parts
8
+ - Improved handling of regular text lines to maintain proper grouping
9
+
3
10
  ## [1.2.0] - 2024-03-XX
4
11
 
5
12
  ### Changed
13
+
6
14
  - Simplified the lyrics format to use clear section markers
7
15
  - Updated documentation with clearer examples
8
16
  - Improved type definitions for better TypeScript support
9
17
 
10
18
  ### Added
19
+
11
20
  - Support for section indications in parentheses
12
21
  - Better handling of repeated sections
13
22
  - More intuitive content structure
14
23
 
15
24
  ### Removed
25
+
16
26
  - Slide-based content splitting
17
27
  - Special command tags
18
28
  - Complex formatting options
@@ -20,6 +30,7 @@
20
30
  ## [1.1.0] - 2024-03-XX
21
31
 
22
32
  ### Added
33
+
23
34
  - Initial release
24
35
  - `getParts` function for basic content extraction
25
36
  - `getSlideParts` function for slide-based content splitting
@@ -28,6 +39,7 @@
28
39
  - TypeScript types and documentation
29
40
 
30
41
  ### Features
42
+
31
43
  - Bracketed section parsing
32
44
  - Special command tag handling
33
45
  - Slide splitting based on content length
@@ -37,7 +49,8 @@
37
49
  - Unicode support in tags
38
50
 
39
51
  ### Test Coverage
52
+
40
53
  - Basic functionality tests
41
54
  - Slide splitting tests
42
55
  - Special command tests
43
- - Edge case handling
56
+ - Edge case handling
package/debug.ts ADDED
@@ -0,0 +1,47 @@
1
+ import { getSlideParts } from './slide';
2
+
3
+ const comprehensiveInput = `
4
+ [verse]
5
+ First line of verse
6
+ Second line of verse
7
+ Third line of verse
8
+ Fourth line of verse
9
+ Fifth line of verse
10
+ [/verse]
11
+ [verse]
12
+ (inside parentheses)
13
+ Regular text line 1
14
+ Regular text line 2
15
+
16
+ [chorus]
17
+ First chorus line
18
+ Second chorus line
19
+ [/chorus]
20
+
21
+ [verse 1]
22
+ This is a very long line that should be considered too long for the slide
23
+ This is another very long line that should also be considered too long
24
+ Short line 1
25
+ Short line 2
26
+ [/verse 1]
27
+
28
+ Regular line 1
29
+ Regular line 2
30
+
31
+ More content
32
+
33
+ This is a very long line that exceeds forty characters for testing purposes
34
+ This is another very long line that also exceeds the forty character limit
35
+ Another very long line that should be considered too long for the slide
36
+ And yet another very long line that should be split into a new section
37
+ Regular line
38
+
39
+ [verse 2]
40
+ First line with spaces
41
+ Second line with spaces
42
+ [/verse 2]
43
+
44
+ [chorus]`;
45
+
46
+ console.log('Output from getSlideParts:');
47
+ console.log(JSON.stringify(getSlideParts(comprehensiveInput), null, 2));
@@ -0,0 +1 @@
1
+ export {};
package/dist/debug.js ADDED
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const slide_1 = require("./slide");
4
+ const comprehensiveInput = `
5
+ [verse]
6
+ First line of verse
7
+ Second line of verse
8
+ Third line of verse
9
+ Fourth line of verse
10
+ Fifth line of verse
11
+ [/verse]
12
+ [verse]
13
+ (inside parentheses)
14
+ Regular text line 1
15
+ Regular text line 2
16
+
17
+ [chorus]
18
+ First chorus line
19
+ Second chorus line
20
+ [/chorus]
21
+
22
+ [verse 1]
23
+ This is a very long line that should be considered too long for the slide
24
+ This is another very long line that should also be considered too long
25
+ Short line 1
26
+ Short line 2
27
+ [/verse 1]
28
+
29
+ Regular line 1
30
+ Regular line 2
31
+
32
+ More content
33
+
34
+ This is a very long line that exceeds forty characters for testing purposes
35
+ This is another very long line that also exceeds the forty character limit
36
+ Another very long line that should be considered too long for the slide
37
+ And yet another very long line that should be split into a new section
38
+ Regular line
39
+
40
+ [verse 2]
41
+ First line with spaces
42
+ Second line with spaces
43
+ [/verse 2]
44
+
45
+ [chorus]`;
46
+ console.log('Output from getSlideParts:');
47
+ console.log(JSON.stringify((0, slide_1.getSlideParts)(comprehensiveInput), null, 2));
package/dist/lyrics.js CHANGED
@@ -33,11 +33,22 @@ function getLyricsParts(lyrics) {
33
33
  // Split the lyrics into lines and process them
34
34
  const lines = cleanedText.split('\n');
35
35
  let currentPart = null;
36
+ let currentUnnamedContent = [];
36
37
  for (let i = 0; i < lines.length; i++) {
37
38
  const line = lines[i].trim();
38
39
  // Check for part start
39
40
  const partStartMatch = line.match(/^\[([^\]]+)\](?:\s*\(([^)]+)\))?$/);
40
41
  if (partStartMatch) {
42
+ // If we have accumulated unnamed content, add it as a part
43
+ if (currentUnnamedContent.length > 0) {
44
+ parts.push({
45
+ name: undefined,
46
+ repetition: false,
47
+ indication: null,
48
+ content: currentUnnamedContent.join('\n'),
49
+ });
50
+ currentUnnamedContent = [];
51
+ }
41
52
  const partName = partStartMatch[1];
42
53
  const indication = partStartMatch[2] || null;
43
54
  // Check if this part has been seen before
@@ -47,20 +58,34 @@ function getLyricsParts(lyrics) {
47
58
  name: partName,
48
59
  repetition: partCount > 1,
49
60
  indication,
50
- content: partContentMap.get(partName)
61
+ content: partContentMap.get(partName),
51
62
  };
52
63
  parts.push(currentPart);
53
64
  continue;
54
65
  }
55
66
  // Handle content without a part container
56
67
  if (line && !line.startsWith('[') && !line.startsWith('[/')) {
68
+ currentUnnamedContent.push(line);
69
+ }
70
+ else if (currentUnnamedContent.length > 0) {
71
+ // If we hit a part marker or empty line, add the accumulated content
57
72
  parts.push({
58
73
  name: undefined,
59
74
  repetition: false,
60
75
  indication: null,
61
- content: line
76
+ content: currentUnnamedContent.join('\n'),
62
77
  });
78
+ currentUnnamedContent = [];
63
79
  }
64
80
  }
81
+ // Add any remaining unnamed content
82
+ if (currentUnnamedContent.length > 0) {
83
+ parts.push({
84
+ name: undefined,
85
+ repetition: false,
86
+ indication: null,
87
+ content: currentUnnamedContent.join('\n'),
88
+ });
89
+ }
65
90
  return parts;
66
91
  }
@@ -19,7 +19,11 @@ content 2
19
19
 
20
20
  [partname 3]
21
21
 
22
- content without partname container`;
22
+ content without partname container
23
+
24
+ content standalone 1
25
+ content standalone 2
26
+ `;
23
27
  describe('getLyricsParts', () => {
24
28
  it('should correctly parse lyrics into parts', () => {
25
29
  const result = (0, lyrics_1.getLyricsParts)(testLyrics);
@@ -28,38 +32,44 @@ describe('getLyricsParts', () => {
28
32
  name: 'partname 1',
29
33
  repetition: false,
30
34
  indication: 'indication 1',
31
- content: 'content 1'
35
+ content: 'content 1',
32
36
  },
33
37
  {
34
38
  name: 'partname 1',
35
39
  repetition: true,
36
40
  indication: 'indication 2',
37
- content: 'content 1'
41
+ content: 'content 1',
38
42
  },
39
43
  {
40
44
  name: 'partname 2',
41
45
  repetition: false,
42
46
  indication: null,
43
- content: 'content 2'
47
+ content: 'content 2',
44
48
  },
45
49
  {
46
50
  name: 'interlude 1',
47
51
  repetition: false,
48
52
  indication: null,
49
- content: undefined
53
+ content: undefined,
50
54
  },
51
55
  {
52
56
  name: 'partname 3',
53
57
  repetition: false,
54
58
  indication: null,
55
- content: undefined
59
+ content: undefined,
56
60
  },
57
61
  {
58
62
  name: undefined,
59
63
  repetition: false,
60
64
  indication: null,
61
- content: 'content without partname container'
62
- }
65
+ content: 'content without partname container',
66
+ },
67
+ {
68
+ name: undefined,
69
+ repetition: false,
70
+ indication: null,
71
+ content: 'content standalone 1\ncontent standalone 2',
72
+ },
63
73
  ]);
64
74
  });
65
75
  });
package/dist/slide.js CHANGED
@@ -10,57 +10,41 @@
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.getSlideParts = void 0;
13
+ const lyrics_1 = require("./lyrics");
14
+ const isLineTooLong = (line) => line.length > 40;
15
+ const processContent = (content) => {
16
+ const slides = [];
17
+ let currentSlide = [];
18
+ content.split('\n').forEach((line) => {
19
+ const trimmedLine = line.trim();
20
+ if (!trimmedLine) {
21
+ if (currentSlide.length > 0) {
22
+ slides.push(currentSlide.join('\n'));
23
+ currentSlide = [];
24
+ }
25
+ return;
26
+ }
27
+ if (currentSlide.length >= 4 ||
28
+ (currentSlide.length >= 2 && currentSlide.filter(isLineTooLong).length >= 2)) {
29
+ slides.push(currentSlide.join('\n'));
30
+ currentSlide = [];
31
+ }
32
+ currentSlide.push(trimmedLine);
33
+ });
34
+ if (currentSlide.length > 0) {
35
+ slides.push(currentSlide.join('\n'));
36
+ }
37
+ return slides;
38
+ };
13
39
  const getSlideParts = (text) => {
14
40
  if (!text)
15
41
  return [];
16
- const isLineTooLong = (line) => line.length > 40;
17
- // Process parts in brackets and create a map
18
- const partsMap = new Map();
19
- // First pass: extract all content with closing tags
20
- const cleanedText = text.replace(/\[(.*?)\]([\s\S]*?)\[\/\1\]/g, (match, key, content) => {
21
- if (!partsMap.has(key)) {
22
- partsMap.set(key, content.trim());
23
- }
24
- return `[${key}]`;
25
- });
26
42
  // Remove text between parentheses
27
- const textWithoutParentheses = cleanedText.replace(/\([^)]*\)/g, '');
28
- const processContent = (content) => {
29
- const slides = [];
30
- let currentSlide = [];
31
- content.split("\n").forEach((line) => {
32
- const trimmedLine = line.trim();
33
- if (!trimmedLine) {
34
- if (currentSlide.length > 0) {
35
- slides.push(currentSlide.join("\n"));
36
- currentSlide = [];
37
- }
38
- return;
39
- }
40
- if (currentSlide.length >= 4 ||
41
- (currentSlide.length >= 2 &&
42
- currentSlide.filter(isLineTooLong).length >= 2)) {
43
- slides.push(currentSlide.join("\n"));
44
- currentSlide = [];
45
- }
46
- currentSlide.push(trimmedLine);
47
- });
48
- if (currentSlide.length > 0) {
49
- slides.push(currentSlide.join("\n"));
50
- }
51
- return slides;
52
- };
43
+ const textWithoutParentheses = text.replace(/\([^)]*\)/g, '');
44
+ const partsText = (0, lyrics_1.getLyricsParts)(textWithoutParentheses).map((part) => part.content);
53
45
  const result = [];
54
- const parts = textWithoutParentheses
55
- .trim()
56
- .split(/\[([^\]]+)\]/)
57
- .filter(Boolean);
58
- parts.forEach((part) => {
59
- if (partsMap.has(part)) {
60
- const partContent = partsMap.get(part);
61
- result.push(...processContent(partContent));
62
- }
63
- else if (part.trim()) {
46
+ partsText.forEach((part) => {
47
+ if (part) {
64
48
  result.push(...processContent(part));
65
49
  }
66
50
  });
package/jest.config.js CHANGED
@@ -7,8 +7,11 @@ export default {
7
7
  '^(\\.{1,2}/.*)\\.js$': '$1',
8
8
  },
9
9
  transform: {
10
- '^.+\\.tsx?$': ['ts-jest', {
11
- useESM: true,
12
- }],
10
+ '^.+\\.tsx?$': [
11
+ 'ts-jest',
12
+ {
13
+ useESM: true,
14
+ },
15
+ ],
13
16
  },
14
- };
17
+ };
package/lyrics.test.ts CHANGED
@@ -18,49 +18,59 @@ content 2
18
18
 
19
19
  [partname 3]
20
20
 
21
- content without partname container`;
21
+ content without partname container
22
+
23
+ content standalone 1
24
+ content standalone 2
25
+ `;
22
26
 
23
27
  describe('getLyricsParts', () => {
24
- it('should correctly parse lyrics into parts', () => {
25
- const result = getLyricsParts(testLyrics);
26
-
27
- expect(result).toEqual([
28
- {
29
- name: 'partname 1',
30
- repetition: false,
31
- indication: 'indication 1',
32
- content: 'content 1'
33
- },
34
- {
35
- name: 'partname 1',
36
- repetition: true,
37
- indication: 'indication 2',
38
- content: 'content 1'
39
- },
40
- {
41
- name: 'partname 2',
42
- repetition: false,
43
- indication: null,
44
- content: 'content 2'
45
- },
46
- {
47
- name: 'interlude 1',
48
- repetition: false,
49
- indication: null,
50
- content: undefined
51
- },
52
- {
53
- name: 'partname 3',
54
- repetition: false,
55
- indication: null,
56
- content: undefined
57
- },
58
- {
59
- name: undefined,
60
- repetition: false,
61
- indication: null,
62
- content: 'content without partname container'
63
- }
64
- ]);
65
- });
66
- });
28
+ it('should correctly parse lyrics into parts', () => {
29
+ const result = getLyricsParts(testLyrics);
30
+
31
+ expect(result).toEqual([
32
+ {
33
+ name: 'partname 1',
34
+ repetition: false,
35
+ indication: 'indication 1',
36
+ content: 'content 1',
37
+ },
38
+ {
39
+ name: 'partname 1',
40
+ repetition: true,
41
+ indication: 'indication 2',
42
+ content: 'content 1',
43
+ },
44
+ {
45
+ name: 'partname 2',
46
+ repetition: false,
47
+ indication: null,
48
+ content: 'content 2',
49
+ },
50
+ {
51
+ name: 'interlude 1',
52
+ repetition: false,
53
+ indication: null,
54
+ content: undefined,
55
+ },
56
+ {
57
+ name: 'partname 3',
58
+ repetition: false,
59
+ indication: null,
60
+ content: undefined,
61
+ },
62
+ {
63
+ name: undefined,
64
+ repetition: false,
65
+ indication: null,
66
+ content: 'content without partname container',
67
+ },
68
+ {
69
+ name: undefined,
70
+ repetition: false,
71
+ indication: null,
72
+ content: 'content standalone 1\ncontent standalone 2',
73
+ },
74
+ ]);
75
+ });
76
+ });
package/lyrics.ts CHANGED
@@ -1,81 +1,107 @@
1
1
  export type LyricPart = {
2
- name: string | undefined;
3
- repetition: boolean;
4
- indication: string | null;
5
- content: string | undefined;
2
+ name: string | undefined;
3
+ repetition: boolean;
4
+ indication: string | null;
5
+ content: string | undefined;
6
6
  };
7
7
  /**
8
8
  * Splits lyrics into structured parts, handling named sections, repetitions, and indications.
9
9
  * Works with lyrics formatted using square brackets for section names and optional parentheses for indications.
10
- *
10
+ *
11
11
  * Example input:
12
12
  * ```
13
13
  * [verse 1] (first time)
14
14
  * Lyrics content here
15
15
  * [/verse 1]
16
- *
16
+ *
17
17
  * [chorus]
18
18
  * Chorus lyrics
19
19
  * [/chorus]
20
20
  * ```
21
- *
21
+ *
22
22
  * @param lyrics - The input lyrics text to be parsed into parts
23
23
  * @returns Array of LyricPart objects containing structured lyrics data
24
24
  */
25
25
 
26
26
  export function getLyricsParts(lyrics: string): LyricPart[] {
27
- const parts: LyricPart[] = [];
28
- const seenParts = new Map<string, number>();
29
- const partContentMap = new Map<string, string>();
27
+ const parts: LyricPart[] = [];
28
+ const seenParts = new Map<string, number>();
29
+ const partContentMap = new Map<string, string>();
30
30
 
31
- // First pass: extract all content with closing tags
32
- const cleanedText = lyrics.replace(
33
- /\[([^\]]+)\](?:\s*\(([^)]+)\))?\s*([\s\S]*?)\[\/\1\]/g,
34
- (match, key: string, indication: string | undefined, content: string) => {
35
- if (!partContentMap.has(key)) {
36
- partContentMap.set(key, content.trim());
37
- }
38
- return `[${key}]${indication ? ` (${indication})` : ''}`;
39
- }
40
- );
31
+ // First pass: extract all content with closing tags
32
+ const cleanedText = lyrics.replace(
33
+ /\[([^\]]+)\](?:\s*\(([^)]+)\))?\s*([\s\S]*?)\[\/\1\]/g,
34
+ (match, key: string, indication: string | undefined, content: string) => {
35
+ if (!partContentMap.has(key)) {
36
+ partContentMap.set(key, content.trim());
37
+ }
38
+ return `[${key}]${indication ? ` (${indication})` : ''}`;
39
+ }
40
+ );
41
+
42
+ // Split the lyrics into lines and process them
43
+ const lines = cleanedText.split('\n');
44
+ let currentPart: LyricPart | null = null;
45
+ let currentUnnamedContent: string[] = [];
41
46
 
42
- // Split the lyrics into lines and process them
43
- const lines = cleanedText.split('\n');
44
- let currentPart: LyricPart | null = null;
47
+ for (let i = 0; i < lines.length; i++) {
48
+ const line = lines[i].trim();
45
49
 
46
- for (let i = 0; i < lines.length; i++) {
47
- const line = lines[i].trim();
50
+ // Check for part start
51
+ const partStartMatch = line.match(/^\[([^\]]+)\](?:\s*\(([^)]+)\))?$/);
52
+ if (partStartMatch) {
53
+ // If we have accumulated unnamed content, add it as a part
54
+ if (currentUnnamedContent.length > 0) {
55
+ parts.push({
56
+ name: undefined,
57
+ repetition: false,
58
+ indication: null,
59
+ content: currentUnnamedContent.join('\n'),
60
+ });
61
+ currentUnnamedContent = [];
62
+ }
48
63
 
49
- // Check for part start
50
- const partStartMatch = line.match(/^\[([^\]]+)\](?:\s*\(([^)]+)\))?$/);
51
- if (partStartMatch) {
52
- const partName = partStartMatch[1];
53
- const indication = partStartMatch[2] || null;
54
-
55
- // Check if this part has been seen before
56
- const partCount = (seenParts.get(partName) || 0) + 1;
57
- seenParts.set(partName, partCount);
64
+ const partName = partStartMatch[1];
65
+ const indication = partStartMatch[2] || null;
58
66
 
59
- currentPart = {
60
- name: partName,
61
- repetition: partCount > 1,
62
- indication,
63
- content: partContentMap.get(partName)
64
- };
65
- parts.push(currentPart);
66
- continue;
67
- }
67
+ // Check if this part has been seen before
68
+ const partCount = (seenParts.get(partName) || 0) + 1;
69
+ seenParts.set(partName, partCount);
68
70
 
69
- // Handle content without a part container
70
- if (line && !line.startsWith('[') && !line.startsWith('[/')) {
71
- parts.push({
72
- name: undefined,
73
- repetition: false,
74
- indication: null,
75
- content: line
76
- });
77
- }
71
+ currentPart = {
72
+ name: partName,
73
+ repetition: partCount > 1,
74
+ indication,
75
+ content: partContentMap.get(partName),
76
+ };
77
+ parts.push(currentPart);
78
+ continue;
78
79
  }
79
80
 
80
- return parts;
81
- }
81
+ // Handle content without a part container
82
+ if (line && !line.startsWith('[') && !line.startsWith('[/')) {
83
+ currentUnnamedContent.push(line);
84
+ } else if (currentUnnamedContent.length > 0) {
85
+ // If we hit a part marker or empty line, add the accumulated content
86
+ parts.push({
87
+ name: undefined,
88
+ repetition: false,
89
+ indication: null,
90
+ content: currentUnnamedContent.join('\n'),
91
+ });
92
+ currentUnnamedContent = [];
93
+ }
94
+ }
95
+
96
+ // Add any remaining unnamed content
97
+ if (currentUnnamedContent.length > 0) {
98
+ parts.push({
99
+ name: undefined,
100
+ repetition: false,
101
+ indication: null,
102
+ content: currentUnnamedContent.join('\n'),
103
+ });
104
+ }
105
+
106
+ return parts;
107
+ }
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "lyrics-structure",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Parser for lyrics with structured sections, names, and indications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
9
  "test": "jest",
10
- "test:watch": "jest --watch"
10
+ "test:watch": "jest --watch",
11
+ "format": "prettier --write \"**/*.{ts,js,json,md}\""
11
12
  },
12
13
  "keywords": [
13
14
  "lyrics",
@@ -20,6 +21,7 @@
20
21
  "devDependencies": {
21
22
  "@types/jest": "^29.0.0",
22
23
  "jest": "^29.0.0",
24
+ "prettier": "^3.5.3",
23
25
  "ts-jest": "^29.0.0",
24
26
  "typescript": "^5.0.0"
25
27
  }
package/slide.ts CHANGED
@@ -2,82 +2,62 @@
2
2
  * Splits text into natural sections based on text structure.
3
3
  * Works with plain text without requiring markdown or special formatting.
4
4
  * Empty lines are treated as natural separators between parts.
5
- *
5
+ *
6
6
  * @param text - The input text to be split into sections
7
7
  * @param maxLinesPerSlide - Maximum number of lines to include in a single slide (default: 6)
8
8
  * @returns An array of content sections
9
9
  */
10
10
 
11
- export const getSlideParts = (text?: string): string[] => {
12
- if (!text) return [];
13
-
14
- const isLineTooLong = (line: string) => line.length > 40;
15
-
16
- // Process parts in brackets and create a map
17
- const partsMap = new Map<string, string>();
18
-
19
- // First pass: extract all content with closing tags
20
- const cleanedText = text.replace(
21
- /\[(.*?)\]([\s\S]*?)\[\/\1\]/g,
22
- (match, key: string, content) => {
23
- if (!partsMap.has(key)) {
24
- partsMap.set(key, content.trim());
25
- }
26
- return `[${key}]`;
27
- }
28
- );
11
+ import { getLyricsParts } from './lyrics';
29
12
 
30
- // Remove text between parentheses
31
- const textWithoutParentheses = cleanedText.replace(/\([^)]*\)/g, '');
32
-
33
- const processContent = (content: string): string[] => {
34
- const slides: string[] = [];
35
- let currentSlide: string[] = [];
36
-
37
- content.split("\n").forEach((line) => {
38
- const trimmedLine = line.trim();
39
-
40
- if (!trimmedLine) {
41
- if (currentSlide.length > 0) {
42
- slides.push(currentSlide.join("\n"));
43
- currentSlide = [];
44
- }
45
- return;
46
- }
47
-
48
- if (
49
- currentSlide.length >= 4 ||
50
- (currentSlide.length >= 2 &&
51
- currentSlide.filter(isLineTooLong).length >= 2)
52
- ) {
53
- slides.push(currentSlide.join("\n"));
54
- currentSlide = [];
55
- }
56
-
57
- currentSlide.push(trimmedLine);
58
- });
59
-
60
- if (currentSlide.length > 0) {
61
- slides.push(currentSlide.join("\n"));
62
- }
63
-
64
- return slides;
65
- };
66
-
67
- const result: string[] = [];
68
- const parts = textWithoutParentheses
69
- .trim()
70
- .split(/\[([^\]]+)\]/)
71
- .filter(Boolean);
72
-
73
- parts.forEach((part) => {
74
- if (partsMap.has(part)) {
75
- const partContent = partsMap.get(part)!;
76
- result.push(...processContent(partContent));
77
- } else if (part.trim()) {
78
- result.push(...processContent(part));
79
- }
80
- });
81
-
82
- return result;
83
- };
13
+ const isLineTooLong = (line: string) => line.length > 40;
14
+ const processContent = (content: string): string[] => {
15
+ const slides: string[] = [];
16
+ let currentSlide: string[] = [];
17
+
18
+ content.split('\n').forEach((line) => {
19
+ const trimmedLine = line.trim();
20
+
21
+ if (!trimmedLine) {
22
+ if (currentSlide.length > 0) {
23
+ slides.push(currentSlide.join('\n'));
24
+ currentSlide = [];
25
+ }
26
+ return;
27
+ }
28
+
29
+ if (
30
+ currentSlide.length >= 4 ||
31
+ (currentSlide.length >= 2 && currentSlide.filter(isLineTooLong).length >= 2)
32
+ ) {
33
+ slides.push(currentSlide.join('\n'));
34
+ currentSlide = [];
35
+ }
36
+
37
+ currentSlide.push(trimmedLine);
38
+ });
39
+
40
+ if (currentSlide.length > 0) {
41
+ slides.push(currentSlide.join('\n'));
42
+ }
43
+
44
+ return slides;
45
+ };
46
+
47
+ export const getSlideParts = (text?: string): string[] => {
48
+ if (!text) return [];
49
+
50
+ // Remove text between parentheses
51
+ const textWithoutParentheses = text.replace(/\([^)]*\)/g, '');
52
+ const partsText = getLyricsParts(textWithoutParentheses).map((part) => part.content);
53
+
54
+ const result: string[] = [];
55
+
56
+ partsText.forEach((part) => {
57
+ if (part) {
58
+ result.push(...processContent(part));
59
+ }
60
+ });
61
+
62
+ return result;
63
+ };