@thijshakkenberg/valkyrie-mom-mcp 2.5.0 → 2.5.1
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/.claude-plugin/plugin.json +1 -1
- package/dist/io/localization-io.d.ts.map +1 -1
- package/dist/io/localization-io.js +3 -1
- package/dist/io/localization-io.js.map +1 -1
- package/package.json +1 -1
- package/skills/artwork/SKILL.md +22 -10
- package/skills/narration/SKILL.md +3 -1
- package/skills/ui-and-puzzles/SKILL.md +2 -2
- package/src/io/localization-io.ts +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localization-io.d.ts","sourceRoot":"","sources":["../../src/io/localization-io.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CA4CrE;AAcD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"localization-io.d.ts","sourceRoot":"","sources":["../../src/io/localization-io.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CA4CrE;AAcD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAiBxF"}
|
|
@@ -77,7 +77,9 @@ export function writeLocalization(language, entries) {
|
|
|
77
77
|
const lines = [`.,${language}`];
|
|
78
78
|
for (const [key, rawValue] of entries) {
|
|
79
79
|
const value = rawValue.replace(/\r\n|\r|\n/g, '\\n');
|
|
80
|
-
|
|
80
|
+
// ParseEntry strips a pair of outer quotes even inside |||, so |||"dialogue"||| would lose them
|
|
81
|
+
const quotedAtBothEnds = value.length > 1 && value.startsWith('"') && value.endsWith('"');
|
|
82
|
+
if (value.includes('"') && !value.includes(TRIPLE) && !quotedAtBothEnds) {
|
|
81
83
|
lines.push(`${key},${TRIPLE}${value}${TRIPLE}`);
|
|
82
84
|
}
|
|
83
85
|
else if (value.includes('"') || value.includes(TRIPLE) || value.includes('\\n') || value.includes(',')) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localization-io.js","sourceRoot":"","sources":["../../src/io/localization-io.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"localization-io.js","sourceRoot":"","sources":["../../src/io/localization-io.ts"],"names":[],"mappings":"AAKA,uEAAuE;AACvE,MAAM,MAAM,GAAG,KAAK,CAAC;AAErB;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1F,8DAA8D;IAC9D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC;QACrC,IAAI,OAAO,IAAI,IAAI,KAAK,EAAE;YAAE,SAAS;QACrC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEnB,IAAI,GAAY,CAAC;QACjB,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3D,UAAU,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9C,GAAG,GAAG,CAAC,UAAU,CAAC;QACpB,CAAC;aAAM,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,yEAAyE;YACzE,GAAG,GAAG,OAAO,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,gEAAgE;YAChE,GAAG,GAAG,CAAC,OAAO,CAAC;QACjB,CAAC;QAED,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAClC,OAAO,GAAG,EAAE,CAAC;YACb,UAAU,GAAG,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAE1D,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,QAAQ,KAAK,CAAC,CAAC;YAAE,SAAS;QAC9B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED,qGAAqG;AACrG,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,KAAK,GAAG,GAAG,CAAC;IAChB,IAAI,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACnG,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACrE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,OAA4B;IAC9E,MAAM,KAAK,GAAa,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAE1C,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,OAAO,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QACrD,gGAAgG;QAChG,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC1F,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxE,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,MAAM,GAAG,KAAK,GAAG,MAAM,EAAE,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACzG,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACtD,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACjC,CAAC"}
|
package/package.json
CHANGED
package/skills/artwork/SKILL.md
CHANGED
|
@@ -70,20 +70,22 @@ A translated copy goes in a language folder: `English/img/X.jpg` or `img/English
|
|
|
70
70
|
|
|
71
71
|
## Pattern: intro cutscene
|
|
72
72
|
|
|
73
|
-
This is a full-screen opening
|
|
73
|
+
This is a full-screen opening laid out like the official app's: a framed picture on the left and the story on the right, under the app's own ornament. `EventStart` becomes hidden and only puts the UI on screen. The Begin button's event removes the UI and sets up the board.
|
|
74
74
|
|
|
75
75
|
```
|
|
76
|
-
set_quest_config({ image: "img/Intro.jpg" }) # same picture as the cover
|
|
76
|
+
set_quest_config({ image: "img/Intro.jpg" }) # same picture as the cover (a square cover/intro preset)
|
|
77
77
|
|
|
78
|
-
upsert_ui("UIIntroBG", { image: "ImageCutsceneBG", xposition: "
|
|
79
|
-
upsert_ui("UIIntroPicture", { image: "img/Intro.jpg", xposition: "-0.
|
|
80
|
-
upsert_ui("
|
|
81
|
-
upsert_ui("
|
|
82
|
-
upsert_ui("
|
|
78
|
+
upsert_ui("UIIntroBG", { image: "ImageCutsceneBG", xposition: "0", yposition: "0", size: "1", vunits: "True", display: "false", buttons: "0" })
|
|
79
|
+
upsert_ui("UIIntroPicture", { image: "img/Intro.jpg", xposition: "-0.28", yposition: "0", size: "0.80", vunits: "True", display: "false", buttons: "0" })
|
|
80
|
+
upsert_ui("UIIntroFrame", { image: "ImageCutsceneFrame", xposition: "-0.28", yposition: "0", size: "0.88", vunits: "True", display: "false", buttons: "0" })
|
|
81
|
+
upsert_ui("UIIntroTopper", { image: "ImageCutsceneTextTopper", xposition: "0.47", yposition: "-0.364", size: "0.031", vunits: "True", display: "false", buttons: "0" })
|
|
82
|
+
upsert_ui("UIIntroText", { xposition: "0.47", yposition: "0.015", size: "0.69", textaspect: "0.725", textsize: "0.7", textAlignment: "TOP", richText: "True", vunits: "True", display: "false", buttons: "0" })
|
|
83
|
+
upsert_ui("UIIntroButtonFrame", { image: "ImageInvestigatorSelectTitle", xposition: "0.47", yposition: "0", size: "0.078", valign: "bottom", vunits: "True", display: "false", buttons: "0" })
|
|
84
|
+
upsert_ui("UIIntroButton", { xposition: "0.47", yposition: "0", size: "0.078", textaspect: "7", textcolor: "black", textAlignment: "CENTER", richText: "True", valign: "bottom", vunits: "True", display: "false", buttons: "1", event1: "EventSetup" }) # button LAST
|
|
83
85
|
|
|
84
86
|
upsert_event("EventStart", { trigger: "EventStart", display: "false", buttons: "0",
|
|
85
|
-
add: "UIIntroBG UIIntroPicture UIIntroText UIIntroButtonFrame UIIntroButton" })
|
|
86
|
-
upsert_event("EventSetup", { ..., remove: "UIIntroBG UIIntroPicture UIIntroText UIIntroButtonFrame UIIntroButton", add: "<first tile and tokens>" })
|
|
87
|
+
add: "UIIntroBG UIIntroPicture UIIntroFrame UIIntroTopper UIIntroText UIIntroButtonFrame UIIntroButton" })
|
|
88
|
+
upsert_event("EventSetup", { ..., remove: "UIIntroBG UIIntroPicture UIIntroFrame UIIntroTopper UIIntroText UIIntroButtonFrame UIIntroButton", add: "<first tile and tokens>" })
|
|
87
89
|
|
|
88
90
|
set_localization({
|
|
89
91
|
"UIIntroText.uitext": "<align=\"left\"><b>Title</b>\n\nThe story so far...</align>",
|
|
@@ -92,6 +94,15 @@ set_localization({
|
|
|
92
94
|
})
|
|
93
95
|
```
|
|
94
96
|
|
|
97
|
+
Why these numbers (measured from the imported assets and Valkyrie's `Quest.UI` layout code):
|
|
98
|
+
|
|
99
|
+
- **One centred group, 1.44 screen heights wide** (frame 0.88, gap 0.06, text 0.50), so it fits every screen from 3:2 to 2:1 and sits in the middle of wider ones. Anchoring the picture to the left edge and the text to the right edge instead pulls them apart on wide screens. The older pattern (picture at `-0.45` size 0.9, text at `0.35` with `textaspect` 0.95) put the text over the picture's right part on every screen, cut the picture off at the left edge on 16:9 and narrower screens, and ran the text off the right edge narrower than 16:10.
|
|
100
|
+
- **`ImageCutsceneBG` stays centred.** It is 2:1, so centred it covers screens up to 2:1. It has a transparent portrait window (where the official app plays its video) at −0.61…−0.13 screen heights from the centre, and the frame and picture at `-0.28` cover it. Moving the background leaves the window showing beside the frame, or uncovers the screen edges.
|
|
101
|
+
- **`ImageCutsceneFrame`'s opening is 84% wide and 91% tall.** A square picture at 0.80 fills it, and its sides tuck under the frame. The frame's bottom edge is partly transparent between the corners, so a frame smaller than 0.88 lets the background's window show below it.
|
|
102
|
+
- **The text column is 0.50 wide and 0.69 tall** (`size` is the height, `textaspect` the width/height ratio), starting under the ornament and below Valkyrie's Menu button. Font size is `textsize` × screen height / 30: three short paragraphs fit at 0.7, longer text needs 0.6. Keep the intro short anyway, as the official scenarios do.
|
|
103
|
+
- **The button's own text box matches its frame** (`size` 0.078, `textaspect` 7). A text element with `size` 1 is a full screen-height square, all of it clickable.
|
|
104
|
+
- Valkyrie can't play video or reveal paragraphs on a timer. For more pictures or a longer story, chain cutscene screens: the button of one adds the next and removes itself.
|
|
105
|
+
|
|
95
106
|
## Pattern: a picture during play
|
|
96
107
|
|
|
97
108
|
The event that shows the dialog also adds the picture. It sits centred just below the dialog, which Valkyrie draws at the top of the screen. Whatever runs next removes it again, so remove it on **every** button: a button with no event of its own needs a small hidden event that does the removal.
|
|
@@ -109,7 +120,8 @@ upsert_event("EventHideLetter", { display: "false", buttons: "0", remove: "UILet
|
|
|
109
120
|
## UI positioning (how Valkyrie computes it)
|
|
110
121
|
|
|
111
122
|
- With `vunits=True`, the unit is **screen heights**. `size` is the element's height, and its width follows the image's aspect ratio.
|
|
112
|
-
- Without `halign`/`valign`, an element is **centred**: `xposition`/`yposition` shift
|
|
123
|
+
- Without `halign`/`valign`, an element is **centred**: `xposition`/`yposition` shift the element's centre from the screen centre. Positive values go right and down, so `0,0` is dead centre.
|
|
124
|
+
- A 16:10 screen spans ±0.8 screen heights from the centre, 16:9 ±0.89 and 3:2 ±0.75. An element at `x` with width `w` reaches from `x - w/2` to `x + w/2`: check both against the narrowest screen you support (3:2 is a safe minimum), and check that neighbours don't overlap.
|
|
113
125
|
- `halign=left|right` and `valign=top|bottom` anchor the element to that screen edge instead, and the position becomes the distance from that edge.
|
|
114
126
|
- Elements draw in the order they were added. Add clickable elements last.
|
|
115
127
|
|
|
@@ -38,6 +38,8 @@ Run `narration_status`. If the engine is missing, `narration_status install=true
|
|
|
38
38
|
| `{ffg:MONSTER_DEEP_ONE}`, `{c:TileStudy}` | "deep one", "Study" |
|
|
39
39
|
| `\n` | A short pause; blank lines separate paragraphs |
|
|
40
40
|
|
|
41
|
+
**Keep it short, like the official scenarios' intros.** `af_nicole` at speed 0.9 reads about 85 words a minute, pauses included, so a 30-second clip is 40–45 words. A three-paragraph intro cutscene (165 words) came out at 98 seconds. It sounded good, but it was like listening to an audiobook, and the clip keeps playing after the players click on. The screen can keep the full text: pass `text` with a few of its sentences (the hook, the stakes) and narrate those.
|
|
42
|
+
|
|
41
43
|
Write narrated text for the ear: short sentences, no "as shown", no numbers the players must act on. When the dialog text doesn't read well aloud, pass `text` with a spoken version. The dialog stays as it is.
|
|
42
44
|
|
|
43
45
|
## Workflow
|
|
@@ -54,7 +56,7 @@ Write narrated text for the ear: short sentences, no "as shown", no numbers the
|
|
|
54
56
|
|
|
55
57
|
## Choosing what to narrate
|
|
56
58
|
|
|
57
|
-
Good candidates: the introduction (`EventStart` or the first story event), discovering a key clue, a letter or diary page read aloud (pair it with a handout picture from `/artwork`), the monster reveal, and the endings (win, lose, `Eliminated`).
|
|
59
|
+
Good candidates: the introduction (`EventStart` or the first story event; for an intro cutscene built from UI elements, put the clip on the event that adds them), discovering a key clue, a letter or diary page read aloud (pair it with a handout picture from `/artwork`), the monster reveal, and the endings (win, lose, `Eliminated`).
|
|
58
60
|
|
|
59
61
|
Skip: placement instructions, tests and their pass/fail results, mythos events that repeat every round (the same clip gets tiresome), and anything shown while players are busy with the board.
|
|
60
62
|
|
|
@@ -23,7 +23,7 @@ Always use vertical units: sizes and positions are then fractions of the **scree
|
|
|
23
23
|
| `image` | A built-in image (`ImageCutsceneBG`, `ImageInvestigatorSelectTitle`, ...) or a file in the scenario folder (`img/Letter.jpg`) |
|
|
24
24
|
| `textsize`, `textaspect`, `textcolor`, `textAlignment`, `richText` | Text styling for elements with `<name>.uitext` |
|
|
25
25
|
|
|
26
|
-
For example, `xposition=0 yposition=0 size=1` fills the middle of the screen, and `xposition=-0.
|
|
26
|
+
For example, `xposition=0 yposition=0 size=1` fills the middle of the screen, and `xposition=-0.28 size=0.8` puts a square picture left of centre (it spans −0.68…+0.12). The visible range is ±0.5 vertically and, horizontally, ±0.75 on a 3:2 screen, ±0.8 on 16:10 and ±0.89 on 16:9. Check an element's edges (`x ± width/2`) against the narrowest screen you support, and check that neighbours don't overlap.
|
|
27
27
|
|
|
28
28
|
### Layering Order
|
|
29
29
|
|
|
@@ -45,7 +45,7 @@ While **any** UI element is on the board, Valkyrie's next phase button does noth
|
|
|
45
45
|
|
|
46
46
|
## Prologue / Intro Cutscene
|
|
47
47
|
|
|
48
|
-
`/artwork` has the complete,
|
|
48
|
+
`/artwork` has the complete pattern, laid out like the official app's: the `ImageCutsceneBG` backdrop, the scenario's picture in the app's `ImageCutsceneFrame` on the left, the story under the `ImageCutsceneTextTopper` ornament on the right, and a Begin button anchored to the bottom (`valign=bottom`) whose event removes the UI and sets up the board. It is one centred group that fits screens from 3:2 to 2:1. `/artwork` also covers showing handouts and scene pictures during play, and generating the pictures with ComfyUI.
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
## Interactive Journal
|
|
@@ -87,7 +87,9 @@ export function writeLocalization(language: string, entries: Map<string, string>
|
|
|
87
87
|
|
|
88
88
|
for (const [key, rawValue] of entries) {
|
|
89
89
|
const value = rawValue.replace(/\r\n|\r|\n/g, '\\n');
|
|
90
|
-
|
|
90
|
+
// ParseEntry strips a pair of outer quotes even inside |||, so |||"dialogue"||| would lose them
|
|
91
|
+
const quotedAtBothEnds = value.length > 1 && value.startsWith('"') && value.endsWith('"');
|
|
92
|
+
if (value.includes('"') && !value.includes(TRIPLE) && !quotedAtBothEnds) {
|
|
91
93
|
lines.push(`${key},${TRIPLE}${value}${TRIPLE}`);
|
|
92
94
|
} else if (value.includes('"') || value.includes(TRIPLE) || value.includes('\\n') || value.includes(',')) {
|
|
93
95
|
lines.push(`${key},"${value.replace(/"/g, '""')}"`);
|