@link-assistant/web-capture 1.7.15 → 1.7.16
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/gdocs-render-markdown.js +21 -3
- package/src/gdocs.js +11 -1
- package/tests/unit/gdocs.test.js +84 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -138,9 +138,27 @@ function renderTextRunsMarkdown(nodes = []) {
|
|
|
138
138
|
italic: Boolean(node.italic),
|
|
139
139
|
strike: Boolean(node.strike),
|
|
140
140
|
};
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
const text = node.text || '';
|
|
142
|
+
let start = 0;
|
|
143
|
+
for (let idx = 0; idx < text.length; idx++) {
|
|
144
|
+
if (text[idx] !== '\n') {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (idx > start) {
|
|
148
|
+
output += markdownStyleTransition(active, next);
|
|
149
|
+
output += text.slice(start, idx);
|
|
150
|
+
active = next;
|
|
151
|
+
}
|
|
152
|
+
output += markdownStyleTransition(active, inactive);
|
|
153
|
+
output += '\n';
|
|
154
|
+
active = inactive;
|
|
155
|
+
start = idx + 1;
|
|
156
|
+
}
|
|
157
|
+
if (start < text.length) {
|
|
158
|
+
output += markdownStyleTransition(active, next);
|
|
159
|
+
output += text.slice(start);
|
|
160
|
+
active = next;
|
|
161
|
+
}
|
|
144
162
|
}
|
|
145
163
|
output += markdownStyleTransition(active, inactive);
|
|
146
164
|
return output;
|
package/src/gdocs.js
CHANGED
|
@@ -1219,7 +1219,17 @@ function renderContentHtml(content = []) {
|
|
|
1219
1219
|
}
|
|
1220
1220
|
|
|
1221
1221
|
function renderMarkedHtml(node) {
|
|
1222
|
-
|
|
1222
|
+
return String(node.text || '')
|
|
1223
|
+
.split('\n')
|
|
1224
|
+
.map((segment) => renderMarkedHtmlSegment(node, segment))
|
|
1225
|
+
.join('<br>');
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
function renderMarkedHtmlSegment(node, text) {
|
|
1229
|
+
if (!text) {
|
|
1230
|
+
return '';
|
|
1231
|
+
}
|
|
1232
|
+
let output = escapeHtml(text);
|
|
1223
1233
|
if (node.bold) {
|
|
1224
1234
|
output = `<strong>${output}</strong>`;
|
|
1225
1235
|
}
|
package/tests/unit/gdocs.test.js
CHANGED
|
@@ -26,10 +26,26 @@ import {
|
|
|
26
26
|
import fs from 'fs';
|
|
27
27
|
import os from 'os';
|
|
28
28
|
import path from 'path';
|
|
29
|
+
import { fileURLToPath } from 'url';
|
|
29
30
|
import nock from 'nock';
|
|
30
31
|
|
|
31
32
|
jest.setTimeout(30000);
|
|
32
33
|
|
|
34
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
35
|
+
const __dirname = path.dirname(__filename);
|
|
36
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
37
|
+
const ISSUE_104_FIXTURE_DIR = path.join(
|
|
38
|
+
REPO_ROOT,
|
|
39
|
+
'docs',
|
|
40
|
+
'case-studies',
|
|
41
|
+
'issue-104',
|
|
42
|
+
'fixtures'
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
function readIssue104Fixture(filename) {
|
|
46
|
+
return fs.readFileSync(path.join(ISSUE_104_FIXTURE_DIR, filename), 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
describe('gdocs', () => {
|
|
34
50
|
describe('isGoogleDocsUrl', () => {
|
|
35
51
|
it('returns true for standard Google Docs edit URL', () => {
|
|
@@ -648,6 +664,74 @@ describe('gdocs', () => {
|
|
|
648
664
|
);
|
|
649
665
|
});
|
|
650
666
|
|
|
667
|
+
it('renders soft breaks outside marks and preserves image dimensions (issue #104)', () => {
|
|
668
|
+
const fixture = JSON.parse(
|
|
669
|
+
readIssue104Fixture('multiline-marked-inline-image-model.json')
|
|
670
|
+
);
|
|
671
|
+
const capture = parseGoogleDocsModelChunks(
|
|
672
|
+
fixture.chunks,
|
|
673
|
+
fixture.cidUrlMap
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
expect(capture.blocks[0].content).toEqual([
|
|
677
|
+
expect.objectContaining({
|
|
678
|
+
type: 'text',
|
|
679
|
+
text: 'Line one of bold text.',
|
|
680
|
+
bold: true,
|
|
681
|
+
}),
|
|
682
|
+
expect.objectContaining({ type: 'text', text: '\n', bold: false }),
|
|
683
|
+
expect.objectContaining({
|
|
684
|
+
type: 'text',
|
|
685
|
+
text: 'Line two of bold text.',
|
|
686
|
+
bold: true,
|
|
687
|
+
}),
|
|
688
|
+
expect.objectContaining({
|
|
689
|
+
type: 'image',
|
|
690
|
+
alt: 'Inline diagram',
|
|
691
|
+
width: 320,
|
|
692
|
+
height: 180,
|
|
693
|
+
}),
|
|
694
|
+
expect.objectContaining({ type: 'text', text: '\n\n', bold: false }),
|
|
695
|
+
expect.objectContaining({
|
|
696
|
+
type: 'text',
|
|
697
|
+
text: 'Line three of bold text.',
|
|
698
|
+
bold: true,
|
|
699
|
+
}),
|
|
700
|
+
]);
|
|
701
|
+
expect(renderGoogleDocsCapture(capture, 'html')).toBe(
|
|
702
|
+
readIssue104Fixture(
|
|
703
|
+
'multiline-marked-inline-image.expected.html'
|
|
704
|
+
).trimEnd()
|
|
705
|
+
);
|
|
706
|
+
expect(renderGoogleDocsCapture(capture, 'markdown')).toBe(
|
|
707
|
+
readIssue104Fixture('multiline-marked-inline-image.expected.md')
|
|
708
|
+
);
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it('does not keep inline marks open across embedded newlines (issue #104)', () => {
|
|
712
|
+
const capture = {
|
|
713
|
+
blocks: [
|
|
714
|
+
{
|
|
715
|
+
type: 'paragraph',
|
|
716
|
+
content: [
|
|
717
|
+
{
|
|
718
|
+
type: 'text',
|
|
719
|
+
text: 'Alpha\nBeta',
|
|
720
|
+
bold: true,
|
|
721
|
+
},
|
|
722
|
+
],
|
|
723
|
+
},
|
|
724
|
+
],
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
expect(renderGoogleDocsCapture(capture, 'html')).toBe(
|
|
728
|
+
'<!doctype html><html><body><p><strong>Alpha</strong><br><strong>Beta</strong></p></body></html>'
|
|
729
|
+
);
|
|
730
|
+
expect(renderGoogleDocsCapture(capture, 'markdown')).toBe(
|
|
731
|
+
'**Alpha**\n**Beta**\n'
|
|
732
|
+
);
|
|
733
|
+
});
|
|
734
|
+
|
|
651
735
|
it('keeps nested bold and italic markers balanced (issue #96)', () => {
|
|
652
736
|
const text = 'Bold text with italic inside and back to bold\n';
|
|
653
737
|
const chunks = [
|