markitdown-cosense 0.1.0__py3-none-any.whl
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.
- markitdown_cosense/__init__.py +10 -0
- markitdown_cosense/_plugin.py +366 -0
- markitdown_cosense-0.1.0.dist-info/METADATA +126 -0
- markitdown_cosense-0.1.0.dist-info/RECORD +8 -0
- markitdown_cosense-0.1.0.dist-info/WHEEL +5 -0
- markitdown_cosense-0.1.0.dist-info/entry_points.txt +2 -0
- markitdown_cosense-0.1.0.dist-info/licenses/LICENSE +21 -0
- markitdown_cosense-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""markitdown-cosense: A markitdown plugin for converting Scrapbox to Markdown."""
|
|
2
|
+
|
|
3
|
+
from ._plugin import __plugin_interface_version__, register_converters
|
|
4
|
+
|
|
5
|
+
__version__ = "0.1.0"
|
|
6
|
+
__all__ = [
|
|
7
|
+
"register_converters",
|
|
8
|
+
"__plugin_interface_version__",
|
|
9
|
+
"__version__",
|
|
10
|
+
]
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import (
|
|
3
|
+
Any,
|
|
4
|
+
BinaryIO,
|
|
5
|
+
Dict,
|
|
6
|
+
List,
|
|
7
|
+
Tuple,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from markitdown import (
|
|
11
|
+
DocumentConverter,
|
|
12
|
+
DocumentConverterResult,
|
|
13
|
+
MarkItDown,
|
|
14
|
+
StreamInfo,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__plugin_interface_version__ = 1
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
ACCEPTED_FILE_EXTENSIONS = [".txt"]
|
|
21
|
+
IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "svg", "webp"]
|
|
22
|
+
CODE_BLOCK_PREFIX = "code:"
|
|
23
|
+
CODE_BLOCK_PLACEHOLDER = "<<<CODEBLOCK{}>>>"
|
|
24
|
+
CODE_BLOCK_PATTERN = re.compile(r"```[\s\S]*?```", re.MULTILINE)
|
|
25
|
+
|
|
26
|
+
_image_extensions_pattern = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def register_converters(markitdown: MarkItDown, **kwargs) -> None:
|
|
30
|
+
markitdown.register_converter(MarkdownConverter())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class MarkdownConverter(DocumentConverter):
|
|
34
|
+
def accepts(
|
|
35
|
+
self, _file_stream: BinaryIO, stream_info: StreamInfo, **kwargs
|
|
36
|
+
) -> bool:
|
|
37
|
+
extension = (stream_info.extension or "").lower()
|
|
38
|
+
return extension in ACCEPTED_FILE_EXTENSIONS
|
|
39
|
+
|
|
40
|
+
def convert(
|
|
41
|
+
self, file_stream: BinaryIO, stream_info: StreamInfo, **kwargs
|
|
42
|
+
) -> DocumentConverterResult:
|
|
43
|
+
file_stream.seek(0)
|
|
44
|
+
content = file_stream.read().decode("utf-8")
|
|
45
|
+
|
|
46
|
+
return DocumentConverterResult(_convert_content(content))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _convert_content(content: str) -> str:
|
|
50
|
+
content = convert_code_blocks(content)
|
|
51
|
+
content, protected_blocks = protect_code_blocks(content)
|
|
52
|
+
|
|
53
|
+
content = convert_tables(content)
|
|
54
|
+
content = convert_lists(content)
|
|
55
|
+
content = apply_conversions(content)
|
|
56
|
+
|
|
57
|
+
return restore_code_blocks(content, protected_blocks)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def convert_code_blocks(content: str) -> str:
|
|
61
|
+
lines = content.splitlines()
|
|
62
|
+
result = []
|
|
63
|
+
i = 0
|
|
64
|
+
|
|
65
|
+
while i < len(lines):
|
|
66
|
+
line = lines[i]
|
|
67
|
+
stripped = line.lstrip(" \t ")
|
|
68
|
+
|
|
69
|
+
if stripped.startswith(CODE_BLOCK_PREFIX):
|
|
70
|
+
indent = line[: len(line) - len(stripped)]
|
|
71
|
+
filename = stripped[len(CODE_BLOCK_PREFIX) :].strip()
|
|
72
|
+
|
|
73
|
+
code_lines, next_i = _collect_code_block_lines(lines, i + 1)
|
|
74
|
+
|
|
75
|
+
if filename == "tex":
|
|
76
|
+
_process_latex_block(code_lines, result, indent)
|
|
77
|
+
else:
|
|
78
|
+
if filename:
|
|
79
|
+
parts = filename.rsplit(".", 1)
|
|
80
|
+
lang = parts[1] if len(parts) == 2 else filename
|
|
81
|
+
else:
|
|
82
|
+
lang = ""
|
|
83
|
+
_add_code_block(result, lang, code_lines, bool(indent))
|
|
84
|
+
|
|
85
|
+
i = next_i
|
|
86
|
+
else:
|
|
87
|
+
result.append(line)
|
|
88
|
+
i += 1
|
|
89
|
+
|
|
90
|
+
return "\n".join(result)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _collect_code_block_lines(
|
|
94
|
+
lines: List[str], start_index: int
|
|
95
|
+
) -> Tuple[List[str], int]:
|
|
96
|
+
code_lines = []
|
|
97
|
+
base_indent = (
|
|
98
|
+
calculate_base_indentation(lines[start_index])
|
|
99
|
+
if start_index < len(lines) and lines[start_index].strip()
|
|
100
|
+
else 0
|
|
101
|
+
)
|
|
102
|
+
consecutive_empty_lines = 0
|
|
103
|
+
|
|
104
|
+
for i in range(start_index, len(lines)):
|
|
105
|
+
line = lines[i]
|
|
106
|
+
|
|
107
|
+
# Count consecutive empty lines
|
|
108
|
+
if not line.strip():
|
|
109
|
+
consecutive_empty_lines += 1
|
|
110
|
+
# End block after 2 consecutive empty lines
|
|
111
|
+
if consecutive_empty_lines >= 2:
|
|
112
|
+
return code_lines[:-1], i # Remove the last empty line
|
|
113
|
+
else:
|
|
114
|
+
consecutive_empty_lines = 0
|
|
115
|
+
|
|
116
|
+
# Check for block end conditions
|
|
117
|
+
if line.strip() and (
|
|
118
|
+
calculate_base_indentation(line) < base_indent
|
|
119
|
+
or line.lstrip(" \t ").startswith(CODE_BLOCK_PREFIX)
|
|
120
|
+
):
|
|
121
|
+
return code_lines, i
|
|
122
|
+
|
|
123
|
+
code_lines.append(line)
|
|
124
|
+
|
|
125
|
+
return code_lines, len(lines)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def protect_code_blocks(content: str) -> Tuple[str, List[str]]:
|
|
129
|
+
code_blocks = CODE_BLOCK_PATTERN.findall(content)
|
|
130
|
+
|
|
131
|
+
def replacer(match):
|
|
132
|
+
index = replacer.counter
|
|
133
|
+
replacer.counter += 1
|
|
134
|
+
return CODE_BLOCK_PLACEHOLDER.format(index)
|
|
135
|
+
|
|
136
|
+
replacer.counter = 0
|
|
137
|
+
protected_content = CODE_BLOCK_PATTERN.sub(replacer, content)
|
|
138
|
+
|
|
139
|
+
return protected_content, code_blocks
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def restore_code_blocks(content: str, code_blocks: List[str]) -> str:
|
|
143
|
+
for i, code_block in enumerate(code_blocks):
|
|
144
|
+
content = content.replace(CODE_BLOCK_PLACEHOLDER.format(i), code_block)
|
|
145
|
+
return content
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_image_extensions_pattern() -> re.Pattern[str]:
|
|
149
|
+
global _image_extensions_pattern
|
|
150
|
+
if _image_extensions_pattern is None:
|
|
151
|
+
extensions = "|".join(IMAGE_EXTENSIONS)
|
|
152
|
+
_image_extensions_pattern = re.compile(
|
|
153
|
+
rf"\[(https?://[^\s\]]+\.(?:{extensions}))\]"
|
|
154
|
+
)
|
|
155
|
+
return _image_extensions_pattern
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def calculate_base_indentation(line: str) -> int:
|
|
159
|
+
for i, char in enumerate(line):
|
|
160
|
+
if char not in (" ", "\t", " "):
|
|
161
|
+
return i
|
|
162
|
+
return len(line)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def is_indented_line(line: str) -> bool:
|
|
166
|
+
return bool(line and line[0] in (" ", "\t", " "))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
CONVERSION_PATTERNS = [
|
|
170
|
+
# Tag pattern - must come first to avoid conflicts
|
|
171
|
+
(
|
|
172
|
+
r"\[(?!\*|img\s|/\s|-\s|\$|https?://|YouTube\s|Twitter\s|\w+\s+https?://)([^\[\]/\-\*\s][^\[\]/\-\*\]]*?)(?!\s+https?://)\]",
|
|
173
|
+
r"<!-- tag: \1 -->",
|
|
174
|
+
),
|
|
175
|
+
# Formatting patterns
|
|
176
|
+
(r"\[\*/\s*(.*?)\]", r"***\1***"),
|
|
177
|
+
(r"\[\*-\s*(.*?)\]", r"**~~\1~~**"),
|
|
178
|
+
(r"\[/-\s*(.*?)\]", r"*~~\1~~*"),
|
|
179
|
+
(r"\[\*\*\*\s*(.*?)\s*\*\*\*\]", r"**\1**"),
|
|
180
|
+
(r"\[\*\*\s*(.*?)\s*\*\*\]", r"**\1**"),
|
|
181
|
+
# Heading patterns
|
|
182
|
+
(r"\[\*\*\*\*\*\s*(.*?)\]", r"##### \1"),
|
|
183
|
+
(r"\[\*\*\*\*\s*(.*?)\]", r"#### \1"),
|
|
184
|
+
(r"\[\*\*\*\s*(.*?)\]", r"### \1"),
|
|
185
|
+
(r"\[\*\*\s*(.*?)\]", r"## \1"),
|
|
186
|
+
(r"\[\*\s*(.*?)\]", r"# \1"),
|
|
187
|
+
# Basic formatting
|
|
188
|
+
(r"\[/\s*(.*?)\]", r"*\1*"),
|
|
189
|
+
(r"\[-\s*(.*?)\]", r"~~\1~~"),
|
|
190
|
+
(r"\[\$\s*(.*?)\s*\$\]", r"$\1$"),
|
|
191
|
+
# Images and media
|
|
192
|
+
(r"\[img\s+(https?://[^\s\]]+)\]", r""),
|
|
193
|
+
("_DYNAMIC_IMAGE_PATTERN_", r""), # Special marker for dynamic pattern
|
|
194
|
+
(
|
|
195
|
+
r"\[YouTube\s+(https?://(?:www\.)?youtube\.com/watch\?v=[\w-]+|https?://youtu\.be/[\w-]+)\]",
|
|
196
|
+
r"[YouTube Video](\1)",
|
|
197
|
+
),
|
|
198
|
+
(
|
|
199
|
+
r"\[Twitter\s+(https?://(?:www\.)?twitter\.com/\w+/status/\d+|https?://x\.com/\w+/status/\d+)\]",
|
|
200
|
+
r"[Twitter Post](\1)",
|
|
201
|
+
),
|
|
202
|
+
# Links
|
|
203
|
+
(r"\[([^/\-\*\]]+?)\s+(https?://[^\s\]]+)\]", r"[\1](\2)"),
|
|
204
|
+
(r"\[(https?://[^\s\]]+)\s+([^/\-\*\]]+?)\]", r"[\2](\1)"),
|
|
205
|
+
# URL auto-linking
|
|
206
|
+
(r'(?<!\()(https?://[^\s<>"\']+(?:\([^\s<>"\']*\)|[^\s<>"\']*)*)', r"<\1>"),
|
|
207
|
+
# Blockquotes
|
|
208
|
+
(r"^>\s*(.*)$", r"> \1"),
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
_compiled_patterns: List[Tuple[re.Pattern[str], str]] | None = None
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _get_compiled_patterns() -> List[Tuple[re.Pattern[str], str]]:
|
|
215
|
+
global _compiled_patterns
|
|
216
|
+
if _compiled_patterns is None:
|
|
217
|
+
compiled = []
|
|
218
|
+
for pattern_str, replacement in CONVERSION_PATTERNS:
|
|
219
|
+
if pattern_str == "_DYNAMIC_IMAGE_PATTERN_":
|
|
220
|
+
pattern = get_image_extensions_pattern()
|
|
221
|
+
else:
|
|
222
|
+
pattern = re.compile(pattern_str, re.MULTILINE)
|
|
223
|
+
compiled.append((pattern, replacement))
|
|
224
|
+
_compiled_patterns = compiled
|
|
225
|
+
return _compiled_patterns
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def apply_conversions(content: str) -> str:
|
|
229
|
+
for pattern, replacement in _get_compiled_patterns():
|
|
230
|
+
content = pattern.sub(replacement, content)
|
|
231
|
+
return content
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _process_latex_block(
|
|
235
|
+
code_lines: List[str], result: List[str], leading_indent: str
|
|
236
|
+
) -> None:
|
|
237
|
+
math_operators = set("=+-*/^")
|
|
238
|
+
math_indicators = ["E(", "V(", "Cov(", "σ", "μ", "√", "Φ", "\\", "^2", "_"]
|
|
239
|
+
excluded_prefixes = ("![", "http", "<http", "->")
|
|
240
|
+
|
|
241
|
+
for code_line in code_lines:
|
|
242
|
+
stripped_line = code_line.strip()
|
|
243
|
+
if not stripped_line:
|
|
244
|
+
result.append("")
|
|
245
|
+
continue
|
|
246
|
+
|
|
247
|
+
has_math = any(c in stripped_line for c in math_operators) or any(
|
|
248
|
+
indicator in stripped_line for indicator in math_indicators
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if (
|
|
252
|
+
not has_math
|
|
253
|
+
or stripped_line.startswith(excluded_prefixes)
|
|
254
|
+
or stripped_line == "code:tex"
|
|
255
|
+
):
|
|
256
|
+
result.append(code_line.rstrip())
|
|
257
|
+
continue
|
|
258
|
+
|
|
259
|
+
has_japanese = any(
|
|
260
|
+
"\u3040" <= c <= "\u309f"
|
|
261
|
+
or "\u30a0" <= c <= "\u30ff"
|
|
262
|
+
or "\u4e00" <= c <= "\u9faf"
|
|
263
|
+
for c in stripped_line
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if has_japanese:
|
|
267
|
+
result.append(code_line.rstrip())
|
|
268
|
+
else:
|
|
269
|
+
result.append(f"{leading_indent}${stripped_line}$")
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _add_code_block(
|
|
273
|
+
result: List[str],
|
|
274
|
+
lang: str,
|
|
275
|
+
code_lines: List[str],
|
|
276
|
+
has_leading_indent: bool = False,
|
|
277
|
+
) -> None:
|
|
278
|
+
if has_leading_indent:
|
|
279
|
+
result.append("")
|
|
280
|
+
|
|
281
|
+
result.append(f"```{lang}")
|
|
282
|
+
result.extend(line if line.strip() else "" for line in code_lines)
|
|
283
|
+
result.append("```")
|
|
284
|
+
|
|
285
|
+
if has_leading_indent:
|
|
286
|
+
result.append("")
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def convert_lists(content: str) -> str:
|
|
290
|
+
lines = content.splitlines()
|
|
291
|
+
result = []
|
|
292
|
+
|
|
293
|
+
for line in lines:
|
|
294
|
+
if is_indented_line(line):
|
|
295
|
+
indent_chars = 0
|
|
296
|
+
for char in line:
|
|
297
|
+
if char in (" ", "\t", " "):
|
|
298
|
+
indent_chars += 1
|
|
299
|
+
else:
|
|
300
|
+
break
|
|
301
|
+
|
|
302
|
+
indent_level = max(0, indent_chars - 1)
|
|
303
|
+
content = line[indent_chars:]
|
|
304
|
+
markdown_indent = " " * (2 * indent_level)
|
|
305
|
+
result.append(f"{markdown_indent}- {content}")
|
|
306
|
+
else:
|
|
307
|
+
result.append(line)
|
|
308
|
+
|
|
309
|
+
return "\n".join(result)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def convert_tables(content: str) -> str:
|
|
313
|
+
lines = content.splitlines()
|
|
314
|
+
result = []
|
|
315
|
+
i = 0
|
|
316
|
+
|
|
317
|
+
while i < len(lines):
|
|
318
|
+
line = lines[i]
|
|
319
|
+
|
|
320
|
+
if line.startswith("table:"):
|
|
321
|
+
processed_table = _process_table_block(lines, i)
|
|
322
|
+
result.extend(processed_table["content"])
|
|
323
|
+
i = processed_table["next_index"]
|
|
324
|
+
continue
|
|
325
|
+
|
|
326
|
+
result.append(line)
|
|
327
|
+
i += 1
|
|
328
|
+
|
|
329
|
+
return "\n".join(result)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _process_table_block(lines: List[str], start_index: int) -> Dict[str, Any]:
|
|
333
|
+
line = lines[start_index]
|
|
334
|
+
result_content = []
|
|
335
|
+
|
|
336
|
+
table_name = line[6:].strip()
|
|
337
|
+
if table_name:
|
|
338
|
+
result_content.append(f"## {table_name}")
|
|
339
|
+
result_content.append("")
|
|
340
|
+
|
|
341
|
+
table_rows = []
|
|
342
|
+
i = start_index + 1
|
|
343
|
+
while i < len(lines) and lines[i].startswith((" ", "\t")):
|
|
344
|
+
row_content = lines[i].strip()
|
|
345
|
+
if row_content:
|
|
346
|
+
table_rows.append(row_content)
|
|
347
|
+
i += 1
|
|
348
|
+
|
|
349
|
+
if table_rows:
|
|
350
|
+
header_row = table_rows[0]
|
|
351
|
+
columns = [col.strip() for col in header_row.split()]
|
|
352
|
+
|
|
353
|
+
if columns:
|
|
354
|
+
result_content.append("| " + " | ".join(columns) + " |")
|
|
355
|
+
result_content.append("|" + "---|" * len(columns))
|
|
356
|
+
|
|
357
|
+
for row in table_rows[1:]:
|
|
358
|
+
data_columns = [col.strip() for col in row.split()]
|
|
359
|
+
while len(data_columns) < len(columns):
|
|
360
|
+
data_columns.append("")
|
|
361
|
+
data_columns = data_columns[: len(columns)]
|
|
362
|
+
result_content.append("| " + " | ".join(data_columns) + " |")
|
|
363
|
+
|
|
364
|
+
result_content.append("")
|
|
365
|
+
|
|
366
|
+
return {"content": result_content, "next_index": i}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: markitdown-cosense
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A markitdown plugin for converting Scrapbox notation to Markdown
|
|
5
|
+
Author-email: kazu728 <kazuki.matsuo.728@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/kazu728/markitdown-cosense
|
|
8
|
+
Project-URL: Repository, https://github.com/kazu728/markitdown-cosense
|
|
9
|
+
Project-URL: Issues, https://github.com/kazu728/markitdown-cosense/issues
|
|
10
|
+
Keywords: markitdown,scrapbox,markdown,converter,plugin
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: markitdown>=0.1.1
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# markitdown-cosense
|
|
18
|
+
|
|
19
|
+
A [markitdown](https://github.com/microsoft/markitdown) plugin for converting Scrapbox notation to Markdown.
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
This plugin converts various Scrapbox notations to standard Markdown format:
|
|
24
|
+
|
|
25
|
+
- **Headings**: `[* Heading]` → `# Heading`
|
|
26
|
+
- **Text decorations**: `[/ italic]`, `[- strikethrough]`, `[** bold]`
|
|
27
|
+
- **Lists**: Indented lines with spaces, tabs, or full-width spaces
|
|
28
|
+
- **Code blocks**: `code:language` notation
|
|
29
|
+
- **Tables**: `table:name` notation
|
|
30
|
+
- **Links and images**: `[title url]`, `[img url]`
|
|
31
|
+
- **Math expressions**: `[$ formula $]` → `$formula$`
|
|
32
|
+
- **LaTeX blocks**: `code:tex` with mathematical content
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install markitdown-cosense
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
### With markitdown CLI
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# List available plugins
|
|
46
|
+
markitdown --list-plugins
|
|
47
|
+
|
|
48
|
+
# Convert a file using the plugin
|
|
49
|
+
markitdown --use-plugins your-file.txt
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Programmatic usage
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from markitdown import MarkItDown
|
|
56
|
+
from markitdown_cosense import register_converters
|
|
57
|
+
|
|
58
|
+
# Initialize markitdown
|
|
59
|
+
md = MarkItDown()
|
|
60
|
+
|
|
61
|
+
# Register the converter
|
|
62
|
+
register_converters(md)
|
|
63
|
+
|
|
64
|
+
# Convert a file
|
|
65
|
+
result = md.convert("your-scrapbox-file.txt")
|
|
66
|
+
print(result.text_content)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
- Converts Scrapbox notation to standard Markdown
|
|
72
|
+
- Handles headings, formatting, links, images, lists, tables, code blocks, and math notation
|
|
73
|
+
- Converts tags `[tag]` to HTML comments `<!-- tag: tag -->` to preserve information while maintaining valid Markdown
|
|
74
|
+
|
|
75
|
+
## Examples
|
|
76
|
+
|
|
77
|
+
### Input (Scrapbox notation)
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
[* Project Title]
|
|
81
|
+
|
|
82
|
+
[** Overview]
|
|
83
|
+
This project is about [/ converting] Scrapbox notation.
|
|
84
|
+
|
|
85
|
+
Features:
|
|
86
|
+
Main feature
|
|
87
|
+
Sub feature 1
|
|
88
|
+
Sub feature 2
|
|
89
|
+
Another feature
|
|
90
|
+
|
|
91
|
+
code:python
|
|
92
|
+
def hello():
|
|
93
|
+
print("Hello, World!")
|
|
94
|
+
|
|
95
|
+
table:Results
|
|
96
|
+
Name Score Grade
|
|
97
|
+
Alice 95 A
|
|
98
|
+
Bob 87 B
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Output (Markdown)
|
|
102
|
+
|
|
103
|
+
```markdown
|
|
104
|
+
# Project Title
|
|
105
|
+
|
|
106
|
+
## Overview
|
|
107
|
+
This project is about *converting* Scrapbox notation.
|
|
108
|
+
|
|
109
|
+
Features:
|
|
110
|
+
- Main feature
|
|
111
|
+
- Sub feature 1
|
|
112
|
+
- Sub feature 2
|
|
113
|
+
- Another feature
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
def hello():
|
|
117
|
+
print("Hello, World!")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Results
|
|
121
|
+
|
|
122
|
+
| Name | Score | Grade |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| Alice | 95 | A |
|
|
125
|
+
| Bob | 87 | B |
|
|
126
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
markitdown_cosense/__init__.py,sha256=3-ahVQiE9P8HHnSTZByeATTG-r9oERkMCwAELhGfjTQ,274
|
|
2
|
+
markitdown_cosense/_plugin.py,sha256=4Wvp6FXglHF2Xn9BlocLlaWgAykVzmezGp_A3DJRDLY,10900
|
|
3
|
+
markitdown_cosense-0.1.0.dist-info/licenses/LICENSE,sha256=D63V7wYVic1bwmq79kCxL9okn7HdlaoJ0too_vJ8LL0,1063
|
|
4
|
+
markitdown_cosense-0.1.0.dist-info/METADATA,sha256=_G2jHlQuIrDNJVd1TwOiD2DiF1mKKf4wuGFgpYI6N_A,2752
|
|
5
|
+
markitdown_cosense-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
+
markitdown_cosense-0.1.0.dist-info/entry_points.txt,sha256=BkLrFPlYeHeXuykl3pclx4xywFwCgUK-nCSu8VF121I,60
|
|
7
|
+
markitdown_cosense-0.1.0.dist-info/top_level.txt,sha256=rs3Ha_XfbY6RemjYc4Dq_93pbLFgk8wJ7Z-YH-7r-FY,19
|
|
8
|
+
markitdown_cosense-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 kazu728
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
markitdown_cosense
|