chatgpt-to-markdown 1.0.0
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/README.md +106 -0
- package/cli.js +24 -0
- package/index.js +90 -0
- package/package.json +27 -0
package/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# ChatGPT to Markdown
|
2
|
+
|
3
|
+
Convert ChatGPT conversation JSON files to Markdown format with ease.
|
4
|
+
|
5
|
+
[](https://badge.fury.io/js/chatgpt-to-markdown)
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
Go to [chat.openai.com](https://chat.openai.com/) > Settings > Data controls > Export data and export your data.
|
10
|
+
|
11
|
+
Unzip the file you're mailed and run:
|
12
|
+
|
13
|
+
```bash
|
14
|
+
npx chatgpt-to-markdown path/to/your/conversations.json
|
15
|
+
```
|
16
|
+
|
17
|
+
**NO NEED TO INSTALL** - `npx` will automatically install the package if it's not already installed.
|
18
|
+
|
19
|
+
This will generate one Markdown file for each chat same directory as the conversations JSON file. The file name will be the chat title, with invalid filename characters replaced by spaces.
|
20
|
+
|
21
|
+
## Example
|
22
|
+
|
23
|
+
Here's an example of the Markdown output for a chat with the title `Medium-Style Table CSS`:
|
24
|
+
|
25
|
+
````markdown
|
26
|
+
# Medium-Style Table CSS
|
27
|
+
|
28
|
+
- Created: 26/9/2023, 10:10:54 am
|
29
|
+
- Updated: 26/9/2023, 10:11:19 am
|
30
|
+
|
31
|
+
## user
|
32
|
+
|
33
|
+
Generate CSS for a Medium-style table
|
34
|
+
|
35
|
+
## assistant
|
36
|
+
|
37
|
+
Creating a Medium-style table requires a clean and minimalist design. Here's a simple CSS snippet to help you create a table that resembles the style seen on Medium. This code assumes that you have a basic HTML table structure in place.
|
38
|
+
|
39
|
+
```css
|
40
|
+
/* Resetting some table styles */
|
41
|
+
table {
|
42
|
+
border-collapse: collapse;
|
43
|
+
width: 100%;
|
44
|
+
margin-bottom: 20px;
|
45
|
+
}
|
46
|
+
...
|
47
|
+
```
|
48
|
+
````
|
49
|
+
|
50
|
+
## Installation
|
51
|
+
|
52
|
+
```bash
|
53
|
+
npm install chatgpt-to-markdown
|
54
|
+
```
|
55
|
+
|
56
|
+
## API
|
57
|
+
|
58
|
+
You can also use `chatgpt-to-markdown` programmatically in your JavaScript or TypeScript projects.
|
59
|
+
|
60
|
+
```javascript
|
61
|
+
import chatgptToMarkdown from "chatgpt-to-markdown";
|
62
|
+
|
63
|
+
const json = [
|
64
|
+
// ... your ChatGPT conversation data
|
65
|
+
];
|
66
|
+
|
67
|
+
const sourceDir = "./output";
|
68
|
+
|
69
|
+
chatgptToMarkdown(json, sourceDir);
|
70
|
+
```
|
71
|
+
|
72
|
+
## Options
|
73
|
+
|
74
|
+
Currently, there are no additional options. Feel free to open an issue or submit a PR if there's a feature you'd like to see added!
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
- Create a new branch for your feature or bugfix.
|
79
|
+
- Make your changes.
|
80
|
+
|
81
|
+
# Contributing
|
82
|
+
|
83
|
+
- Fork the repository on GitHub and clone the fork to your machine.
|
84
|
+
- Run `npm install` to install dependencies
|
85
|
+
- Edit [`index.js`](index.js) or [`cli.js`](cli.js), documenting your changes
|
86
|
+
- Push your changes back to your fork on GitHub and submit a pull request to the main repository.
|
87
|
+
|
88
|
+
# Release
|
89
|
+
|
90
|
+
```shell
|
91
|
+
npm version minor
|
92
|
+
npm publish
|
93
|
+
git push --follow-tags
|
94
|
+
```
|
95
|
+
|
96
|
+
## Release notes
|
97
|
+
|
98
|
+
- 1.0.0: 26 Sep 2023. Initial release
|
99
|
+
|
100
|
+
## License
|
101
|
+
|
102
|
+
MIT
|
103
|
+
|
104
|
+
## Support
|
105
|
+
|
106
|
+
If you encounter any problems or have suggestions, please [open an issue](https://github.com/sanand0/chatgpt-to-markdown/issues) or submit a pull request.
|
package/cli.js
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
import path from "path";
|
3
|
+
import { promises as fs } from "fs";
|
4
|
+
import chatgptToMarkdown from "./index.js";
|
5
|
+
|
6
|
+
async function run() {
|
7
|
+
const filePath = process.argv[2];
|
8
|
+
if (!filePath) {
|
9
|
+
console.error("Please provide a file path as a command line argument.");
|
10
|
+
process.exit(1);
|
11
|
+
}
|
12
|
+
|
13
|
+
try {
|
14
|
+
const data = await fs.readFile(filePath, "utf8");
|
15
|
+
const json = JSON.parse(data);
|
16
|
+
const sourceDir = path.dirname(filePath);
|
17
|
+
await chatgptToMarkdown(json, sourceDir);
|
18
|
+
} catch (err) {
|
19
|
+
console.error("Error:", err.message);
|
20
|
+
process.exit(1);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
run();
|
package/index.js
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
import { promises as fs } from "fs";
|
2
|
+
import path from "path";
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Sanitizes a file name by replacing invalid characters with spaces.
|
6
|
+
* @param {string} title - The title to sanitize.
|
7
|
+
* @returns {string} - The sanitized title.
|
8
|
+
*/
|
9
|
+
function sanitizeFileName(title) {
|
10
|
+
return title
|
11
|
+
.replace(/[<>:"\/\\|?*\n]/g, " ")
|
12
|
+
.replace(/\s+/g, " ")
|
13
|
+
.trim();
|
14
|
+
}
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Wraps HTML tags in backticks.
|
18
|
+
* @param {string} text - The text to process.
|
19
|
+
* @returns {string} - The text with HTML tags wrapped in backticks.
|
20
|
+
*/
|
21
|
+
function wrapHtmlTagsInBackticks(text) {
|
22
|
+
return text.replace(/<[^>]+>/g, (match) => `\`${match}\``);
|
23
|
+
}
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Indents a string by 4 spaces.
|
27
|
+
* @param {string} str - The string to indent.
|
28
|
+
* @returns {string} - The indented string.
|
29
|
+
* @example
|
30
|
+
* indent("foo\nbar\nbaz");
|
31
|
+
* //=> " foo\n bar\n baz"
|
32
|
+
*/
|
33
|
+
function indent(str) {
|
34
|
+
return str
|
35
|
+
.split("\n")
|
36
|
+
.map((v) => ` ${v}`)
|
37
|
+
.join("\n");
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Converts a JSON object to markdown and saves it to a file.
|
42
|
+
* @param {Object[]} json - The JSON object to convert.
|
43
|
+
* @param {string} sourceDir - The directory to save the markdown files in.
|
44
|
+
*/
|
45
|
+
async function chatgptToMarkdown(json, sourceDir) {
|
46
|
+
if (!Array.isArray(json)) {
|
47
|
+
throw new TypeError("The first argument must be an array.");
|
48
|
+
}
|
49
|
+
if (typeof sourceDir !== "string") {
|
50
|
+
throw new TypeError("The second argument must be a string.");
|
51
|
+
}
|
52
|
+
|
53
|
+
for (const conversation of json) {
|
54
|
+
const sanitizedTitle = sanitizeFileName(conversation.title);
|
55
|
+
const fileName = `${sanitizedTitle}.md`;
|
56
|
+
const filePath = path.join(sourceDir, fileName);
|
57
|
+
const title = `# ${wrapHtmlTagsInBackticks(conversation.title)}\n`;
|
58
|
+
const metadata = [
|
59
|
+
`- Created: ${new Date(conversation.create_time * 1000).toLocaleString()}\n`,
|
60
|
+
`- Updated: ${new Date(conversation.update_time * 1000).toLocaleString()}\n`,
|
61
|
+
].join("");
|
62
|
+
const messages = Object.values(conversation.mapping)
|
63
|
+
.map((node) => {
|
64
|
+
let content = node.message?.content;
|
65
|
+
if (!content) return "";
|
66
|
+
// Format the body based on the content type
|
67
|
+
let body =
|
68
|
+
content.content_type == "text"
|
69
|
+
? content.parts.join("\n")
|
70
|
+
: content.content_type == "code"
|
71
|
+
? "```" + content.language.replace("unknown", "") + "\n" + content.text + "\n```"
|
72
|
+
: content.content_type == "execution_output"
|
73
|
+
? "```\n" + content.text + "\n```"
|
74
|
+
: content;
|
75
|
+
// Ignore empty content
|
76
|
+
if (!body.trim()) return "";
|
77
|
+
// Indent user / tool messages. The sometimes contain code and whitespaces are relevant
|
78
|
+
const author = node.message.author;
|
79
|
+
if (author.role == "user") body = indent(body);
|
80
|
+
if (author.role == "tool" && !body.startsWith("```") && !body.endsWith("```")) body = indent(body);
|
81
|
+
return `## ${author.role}${author.name ? ` (${author.name})` : ""}\n\n${body}\n\n`;
|
82
|
+
})
|
83
|
+
.join("");
|
84
|
+
const markdownContent = `${title}\n${metadata}\n${messages}`;
|
85
|
+
await fs.writeFile(filePath, markdownContent, "utf8");
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
// Export the convertToMarkdown function as the default export
|
90
|
+
export default chatgptToMarkdown;
|
package/package.json
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"name": "chatgpt-to-markdown",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "Convert ChatGPT exported conversations.json to Markdown",
|
5
|
+
"main": "index.js",
|
6
|
+
"type": "module",
|
7
|
+
"bin": {
|
8
|
+
"chatgpt-to-markdown": "cli.js"
|
9
|
+
},
|
10
|
+
"scripts": {
|
11
|
+
"prepublishOnly": "npx prettier --write *.js *.md package.json",
|
12
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
13
|
+
},
|
14
|
+
"keywords": [
|
15
|
+
"chatgpt",
|
16
|
+
"markdown",
|
17
|
+
"converter"
|
18
|
+
],
|
19
|
+
"author": "S Anand <root.node@gmail.com>",
|
20
|
+
"license": "MIT",
|
21
|
+
"prettier": {
|
22
|
+
"printWidth": 120
|
23
|
+
},
|
24
|
+
"devDependencies": {
|
25
|
+
"prettier": "^3.0.3"
|
26
|
+
}
|
27
|
+
}
|