autoblogger 0.2.6 → 0.2.7

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/dist/cli/index.js CHANGED
@@ -930,7 +930,7 @@ Error: Found conflicting model names in your Prisma schema:`));
930
930
  console.log(` - ${project.appRouterPath}/(writer)/writer/[[...path]]/page.tsx`);
931
931
  console.log(` - ${project.appRouterPath}/(writer)/layout.tsx`);
932
932
  console.log(` - globals.css (add CSS import)`);
933
- console.log(` - ${project.appRouterPath}/layout.tsx (add suppressHydrationWarning)`);
933
+ console.log(` - ${project.appRouterPath}/layout.tsx (add suppressHydrationWarning, GlobalShortcuts)`);
934
934
  if (answers.runMigration) {
935
935
  console.log("\nWould run:");
936
936
  console.log(" - npx prisma migrate dev --name add-autoblogger");
@@ -1014,6 +1014,7 @@ Would import ${count} posts from ${answers.importPath}`);
1014
1014
  const rootLayoutPath = path6.join(cwd, project.appRouterPath, "layout.tsx");
1015
1015
  if (fs6.existsSync(rootLayoutPath)) {
1016
1016
  let layoutContent = fs6.readFileSync(rootLayoutPath, "utf-8");
1017
+ let layoutModified = false;
1017
1018
  if (layoutContent.includes("suppressHydrationWarning")) {
1018
1019
  log("skip", "Root layout already has suppressHydrationWarning");
1019
1020
  } else {
@@ -1023,12 +1024,39 @@ Would import ${count} posts from ${answers.importPath}`);
1023
1024
  const existingAttrs = match[1];
1024
1025
  const newHtmlTag = `<html${existingAttrs} suppressHydrationWarning>`;
1025
1026
  layoutContent = layoutContent.replace(htmlTagRegex, newHtmlTag);
1026
- fs6.writeFileSync(rootLayoutPath, layoutContent);
1027
+ layoutModified = true;
1027
1028
  log("write", `Updated ${project.appRouterPath}/layout.tsx (added suppressHydrationWarning)`);
1028
1029
  } else {
1029
1030
  log("warn", "Could not find <html> tag in root layout. Please add suppressHydrationWarning manually.");
1030
1031
  }
1031
1032
  }
1033
+ if (layoutContent.includes("GlobalShortcuts")) {
1034
+ log("skip", "Root layout already has GlobalShortcuts");
1035
+ } else {
1036
+ const importStatement = "import { GlobalShortcuts } from 'autoblogger/ui'\n";
1037
+ const lastImportMatch = layoutContent.match(/^import .+$/gm);
1038
+ if (lastImportMatch) {
1039
+ const lastImport = lastImportMatch[lastImportMatch.length - 1];
1040
+ layoutContent = layoutContent.replace(lastImport, lastImport + "\n" + importStatement.trim());
1041
+ } else {
1042
+ layoutContent = importStatement + layoutContent;
1043
+ }
1044
+ const bodyTagRegex = /<body([^>]*)>/;
1045
+ const bodyMatch = layoutContent.match(bodyTagRegex);
1046
+ if (bodyMatch) {
1047
+ layoutContent = layoutContent.replace(bodyTagRegex, `<body$1>
1048
+ <GlobalShortcuts />`);
1049
+ layoutModified = true;
1050
+ log("write", `Updated ${project.appRouterPath}/layout.tsx (added GlobalShortcuts for Cmd+/ navigation)`);
1051
+ } else {
1052
+ log("warn", "Could not find <body> tag in root layout. Please add GlobalShortcuts manually.");
1053
+ console.log(import_picocolors3.default.gray(" import { GlobalShortcuts } from 'autoblogger/ui'"));
1054
+ console.log(import_picocolors3.default.gray(" // Add <GlobalShortcuts /> inside your layout body"));
1055
+ }
1056
+ }
1057
+ if (layoutModified) {
1058
+ fs6.writeFileSync(rootLayoutPath, layoutContent);
1059
+ }
1032
1060
  }
1033
1061
  if (answers.runMigration) {
1034
1062
  console.log("");
package/dist/index.js CHANGED
@@ -1998,6 +1998,23 @@ async function handlePostsAPI(req, cms, session, path, onMutate) {
1998
1998
  if (body.status === "published" && !cms.config.auth.canPublish(session)) {
1999
1999
  return jsonResponse({ error: "Not authorized to publish" }, 403);
2000
2000
  }
2001
+ const contentChanging = body.title !== void 0 || body.subtitle !== void 0 || body.markdown !== void 0;
2002
+ if (contentChanging) {
2003
+ const existingPost = await cms.posts.findById(postId);
2004
+ if (existingPost && existingPost.markdown) {
2005
+ const recentRevisions = await cms.revisions.findByPost(postId);
2006
+ const lastRevision = recentRevisions[0];
2007
+ const contentIsDifferent = !lastRevision || lastRevision.markdown !== existingPost.markdown || lastRevision.title !== existingPost.title || lastRevision.subtitle !== existingPost.subtitle;
2008
+ if (contentIsDifferent) {
2009
+ await cms.revisions.create(postId, {
2010
+ title: existingPost.title,
2011
+ subtitle: existingPost.subtitle,
2012
+ markdown: existingPost.markdown
2013
+ });
2014
+ await cms.revisions.pruneOldest(postId, 50);
2015
+ }
2016
+ }
2017
+ }
2001
2018
  const post = await cms.posts.update(postId, body);
2002
2019
  if (onMutate) await onMutate("post", post);
2003
2020
  return jsonResponse({ data: post });
@@ -2923,6 +2940,79 @@ function renderMarkdown(markdown) {
2923
2940
  function markdownToHtml(markdown) {
2924
2941
  return import_marked.marked.parse(markdown, { gfm: true, breaks: true });
2925
2942
  }
2943
+ function createStyledRenderer() {
2944
+ const renderer = new import_marked.Renderer();
2945
+ renderer.heading = function(text, level) {
2946
+ const classes = {
2947
+ 1: "text-[22px] leading-tight font-bold mb-4",
2948
+ 2: "text-lg leading-snug font-bold mt-6 mb-3",
2949
+ 3: "text-base leading-snug font-bold mt-4 mb-2",
2950
+ 4: "text-sm leading-snug font-semibold mt-3 mb-1",
2951
+ 5: "text-sm leading-snug font-semibold mt-2 mb-1",
2952
+ 6: "text-sm leading-snug font-medium mt-2 mb-1"
2953
+ };
2954
+ return `<h${level} class="${classes[level] || ""}">${text}</h${level}>
2955
+ `;
2956
+ };
2957
+ renderer.paragraph = function(text) {
2958
+ return `<p class="mb-3 leading-relaxed">${text}</p>
2959
+ `;
2960
+ };
2961
+ renderer.list = function(body, ordered) {
2962
+ const tag = ordered ? "ol" : "ul";
2963
+ const listClass = ordered ? "list-decimal" : "list-disc";
2964
+ return `<${tag} class="${listClass} pl-5 mb-3 space-y-1">${body}</${tag}>
2965
+ `;
2966
+ };
2967
+ renderer.listitem = function(text) {
2968
+ return `<li>${text}</li>
2969
+ `;
2970
+ };
2971
+ renderer.code = function(code, language) {
2972
+ const escaped = code.replace(/</g, "&lt;").replace(/>/g, "&gt;");
2973
+ return `<pre class="bg-gray-100 dark:bg-gray-800 p-3 rounded-lg overflow-x-auto mb-3 text-sm font-mono"><code class="language-${language || ""}">${escaped}</code></pre>
2974
+ `;
2975
+ };
2976
+ renderer.codespan = function(text) {
2977
+ return `<code class="bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded text-sm font-mono">${text}</code>`;
2978
+ };
2979
+ renderer.blockquote = function(quote) {
2980
+ return `<blockquote class="border-l-4 border-gray-300 dark:border-gray-600 pl-4 italic text-gray-600 dark:text-gray-400 my-3">${quote}</blockquote>
2981
+ `;
2982
+ };
2983
+ renderer.hr = function() {
2984
+ return `<hr class="my-6 border-t border-gray-200 dark:border-gray-700" />
2985
+ `;
2986
+ };
2987
+ renderer.link = function(href, _title, text) {
2988
+ return `<a href="${href}" class="text-blue-600 dark:text-blue-400 underline">${text}</a>`;
2989
+ };
2990
+ renderer.image = function(href, _title, text) {
2991
+ return `<img src="${href}" alt="${text}" class="rounded-lg max-w-full my-3" />`;
2992
+ };
2993
+ renderer.strong = function(text) {
2994
+ return `<strong class="font-semibold">${text}</strong>`;
2995
+ };
2996
+ renderer.em = function(text) {
2997
+ return `<em class="italic">${text}</em>`;
2998
+ };
2999
+ renderer.table = function(header, body) {
3000
+ return `<table class="w-full border-collapse mb-3"><thead>${header}</thead><tbody>${body}</tbody></table>
3001
+ `;
3002
+ };
3003
+ renderer.tablerow = function(content) {
3004
+ return `<tr>${content}</tr>
3005
+ `;
3006
+ };
3007
+ renderer.tablecell = function(content, flags) {
3008
+ const tag = flags.header ? "th" : "td";
3009
+ const headerClass = flags.header ? " font-semibold bg-gray-50 dark:bg-gray-800" : "";
3010
+ const alignClass = flags.align ? ` text-${flags.align}` : " text-left";
3011
+ return `<${tag} class="border border-gray-200 dark:border-gray-700 px-3 py-2${alignClass}${headerClass}">${content}</${tag}>`;
3012
+ };
3013
+ return renderer;
3014
+ }
3015
+ var styledRenderer = createStyledRenderer();
2926
3016
  function parseMarkdown(markdown) {
2927
3017
  return import_marked.marked.lexer(markdown);
2928
3018
  }
@@ -2931,6 +3021,13 @@ var turndownService = new import_turndown.default({
2931
3021
  codeBlockStyle: "fenced",
2932
3022
  bulletListMarker: "-"
2933
3023
  });
3024
+ turndownService.addRule("strikethrough", {
3025
+ filter: (node) => {
3026
+ const tagName = node.nodeName.toLowerCase();
3027
+ return tagName === "del" || tagName === "s" || tagName === "strike";
3028
+ },
3029
+ replacement: (content) => `~~${content}~~`
3030
+ });
2934
3031
  function htmlToMarkdown(html) {
2935
3032
  return turndownService.turndown(html);
2936
3033
  }