autoblogger 0.1.16 → 0.1.17

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 CHANGED
@@ -313,68 +313,74 @@ The `[[...path]]` syntax is a catch-all route that captures the dashboard's inte
313
313
 
314
314
  ## Configuring Tailwind
315
315
 
316
- Autoblogger's UI uses Tailwind CSS classes. Add the package's files to your Tailwind content configuration so the classes aren't purged:
316
+ Autoblogger's UI uses Tailwind CSS. Add the package's files to your Tailwind content configuration so the classes aren't purged:
317
317
 
318
318
  ```javascript
319
- // tailwind.config.js (or tailwind.config.ts)
319
+ // tailwind.config.js
320
320
  module.exports = {
321
+ presets: [require('autoblogger/styles/preset')],
321
322
  content: [
322
323
  './app/**/*.{js,ts,jsx,tsx}',
323
324
  './components/**/*.{js,ts,jsx,tsx}',
324
325
  // Add this line to include Autoblogger's components
325
326
  './node_modules/autoblogger/dist/**/*.{js,mjs}',
326
327
  ],
328
+ darkMode: 'class',
327
329
  // ... rest of your config
328
330
  }
329
331
  ```
330
332
 
331
- The dashboard uses semantic color tokens like `bg-card`, `text-muted-foreground`, `border-border`, etc. These come from shadcn/ui conventions. If you're using shadcn, they'll work automatically. Otherwise, add these CSS variables to your globals:
333
+ The `autoblogger/styles/preset` includes:
334
+ - Typography scale (`text-title`, `text-h1`, `text-h2`, `text-h3`, `text-body`, `text-table`)
335
+ - Color tokens (`bg-background`, `text-muted-foreground`, `border-border`, etc.)
336
+ - Border radius utilities
337
+
338
+ ---
339
+
340
+ ## Styling
341
+
342
+ Autoblogger includes a complete theme with CSS variables, prose styles, and editor styling. Import it in your global CSS:
343
+
344
+ ```css
345
+ /* app/globals.css */
346
+ @import 'autoblogger/styles/autoblogger.css';
347
+
348
+ @tailwind base;
349
+ @tailwind components;
350
+ @tailwind utilities;
351
+ ```
352
+
353
+ This provides:
354
+ - **Light and dark mode colors** — shadcn/ui Zinc theme tokens
355
+ - **Typography scale** — Consistent heading and body sizes
356
+ - **Prose styles** — Article content formatting (headings, paragraphs, lists, code, blockquotes)
357
+ - **Editor styles** — Tiptap focus states and placeholder styling
358
+ - **Animations** — Fade-in effects with reduced motion support
359
+
360
+ ### Customizing
361
+
362
+ Override any CSS variable after the import:
332
363
 
333
364
  ```css
334
- /* globals.css */
365
+ @import 'autoblogger/styles/autoblogger.css';
366
+
335
367
  :root {
336
- --background: 0 0% 100%;
337
- --foreground: 0 0% 3.9%;
338
- --card: 0 0% 100%;
339
- --card-foreground: 0 0% 3.9%;
340
- --popover: 0 0% 100%;
341
- --popover-foreground: 0 0% 3.9%;
342
- --primary: 0 0% 9%;
343
- --primary-foreground: 0 0% 98%;
344
- --secondary: 0 0% 96.1%;
345
- --secondary-foreground: 0 0% 9%;
346
- --muted: 0 0% 96.1%;
347
- --muted-foreground: 0 0% 45.1%;
348
- --accent: 0 0% 96.1%;
349
- --accent-foreground: 0 0% 9%;
350
- --destructive: 0 84.2% 60.2%;
351
- --destructive-foreground: 0 0% 98%;
352
- --border: 0 0% 89.8%;
353
- --input: 0 0% 89.8%;
354
- --ring: 0 0% 3.9%;
368
+ --primary: 221 83% 53%; /* Custom blue primary */
355
369
  }
370
+ ```
356
371
 
357
- .dark {
358
- --background: 0 0% 3.9%;
359
- --foreground: 0 0% 98%;
360
- --card: 0 0% 3.9%;
361
- --card-foreground: 0 0% 98%;
362
- --popover: 0 0% 3.9%;
363
- --popover-foreground: 0 0% 98%;
364
- --primary: 0 0% 98%;
365
- --primary-foreground: 0 0% 9%;
366
- --secondary: 0 0% 14.9%;
367
- --secondary-foreground: 0 0% 98%;
368
- --muted: 0 0% 14.9%;
369
- --muted-foreground: 0 0% 63.9%;
370
- --accent: 0 0% 14.9%;
371
- --accent-foreground: 0 0% 98%;
372
- --destructive: 0 62.8% 30.6%;
373
- --destructive-foreground: 0 0% 98%;
374
- --border: 0 0% 14.9%;
375
- --input: 0 0% 14.9%;
376
- --ring: 0 0% 83.1%;
377
- }
372
+ Or pass custom styles when creating the CMS:
373
+
374
+ ```typescript
375
+ // lib/cms.ts
376
+ export const cms = createAutoblogger({
377
+ // ... other config
378
+ styles: {
379
+ container: 'max-w-3xl mx-auto px-8',
380
+ title: 'text-4xl font-serif',
381
+ prose: 'prose prose-lg dark:prose-invert',
382
+ },
383
+ })
378
384
  ```
379
385
 
380
386
  ---
package/dist/cli/index.js CHANGED
@@ -24,8 +24,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli/init.ts
27
- var fs6 = __toESM(require("fs"));
28
- var path5 = __toESM(require("path"));
27
+ var fs7 = __toESM(require("fs"));
28
+ var path6 = __toESM(require("path"));
29
29
  var import_child_process2 = require("child_process");
30
30
  var import_picocolors3 = __toESM(require("picocolors"));
31
31
 
@@ -468,6 +468,40 @@ function writeTailwindConfig(configPath, content) {
468
468
  fs4.writeFileSync(configPath, content, "utf-8");
469
469
  }
470
470
 
471
+ // src/cli/utils/css-patch.ts
472
+ var fs5 = __toESM(require("fs"));
473
+ var path4 = __toESM(require("path"));
474
+ var AUTOBLOGGER_CSS_IMPORT = "@import 'autoblogger/styles/autoblogger.css';";
475
+ function findGlobalsCss(projectRoot) {
476
+ const candidates = [
477
+ "app/globals.css",
478
+ "src/app/globals.css",
479
+ "styles/globals.css",
480
+ "src/styles/globals.css",
481
+ "app/global.css",
482
+ "src/app/global.css"
483
+ ];
484
+ for (const candidate of candidates) {
485
+ const fullPath = path4.join(projectRoot, candidate);
486
+ if (fs5.existsSync(fullPath)) {
487
+ return fullPath;
488
+ }
489
+ }
490
+ return null;
491
+ }
492
+ function patchGlobalsCss(cssPath) {
493
+ if (!fs5.existsSync(cssPath)) {
494
+ return { success: false, alreadyPatched: false };
495
+ }
496
+ let content = fs5.readFileSync(cssPath, "utf-8");
497
+ if (content.includes("autoblogger")) {
498
+ return { success: true, alreadyPatched: true, filePath: cssPath };
499
+ }
500
+ content = AUTOBLOGGER_CSS_IMPORT + "\n\n" + content;
501
+ fs5.writeFileSync(cssPath, content, "utf-8");
502
+ return { success: true, alreadyPatched: false, filePath: cssPath };
503
+ }
504
+
471
505
  // src/cli/utils/prompts.ts
472
506
  var import_prompts = __toESM(require("prompts"));
473
507
  var import_picocolors = __toESM(require("picocolors"));
@@ -669,8 +703,8 @@ export default async function WriterPage({
669
703
  `;
670
704
 
671
705
  // src/cli/import.ts
672
- var fs5 = __toESM(require("fs"));
673
- var path4 = __toESM(require("path"));
706
+ var fs6 = __toESM(require("fs"));
707
+ var path5 = __toESM(require("path"));
674
708
  var import_child_process = require("child_process");
675
709
  var import_picocolors2 = __toESM(require("picocolors"));
676
710
  function parseFrontmatter(content) {
@@ -734,14 +768,14 @@ function slugify(text) {
734
768
  }
735
769
  function parseMarkdownFile(filePath) {
736
770
  try {
737
- const content = fs5.readFileSync(filePath, "utf-8");
771
+ const content = fs6.readFileSync(filePath, "utf-8");
738
772
  const { frontmatter, body } = parseFrontmatter(content);
739
773
  let title = frontmatter.title;
740
774
  if (!title) {
741
775
  const headingMatch = body.match(/^#\s+(.+)$/m);
742
- title = headingMatch ? headingMatch[1] : path4.basename(filePath, path4.extname(filePath));
776
+ title = headingMatch ? headingMatch[1] : path5.basename(filePath, path5.extname(filePath));
743
777
  }
744
- const slug = frontmatter.slug || slugify(path4.basename(filePath, path4.extname(filePath))) || slugify(title);
778
+ const slug = frontmatter.slug || slugify(path5.basename(filePath, path5.extname(filePath))) || slugify(title);
745
779
  let publishedAt;
746
780
  if (frontmatter.date) {
747
781
  const parsed = new Date(frontmatter.date);
@@ -772,9 +806,9 @@ function parseMarkdownFile(filePath) {
772
806
  function findMarkdownFiles(dir) {
773
807
  const files = [];
774
808
  function walk(currentDir) {
775
- const entries = fs5.readdirSync(currentDir, { withFileTypes: true });
809
+ const entries = fs6.readdirSync(currentDir, { withFileTypes: true });
776
810
  for (const entry of entries) {
777
- const fullPath = path4.join(currentDir, entry.name);
811
+ const fullPath = path5.join(currentDir, entry.name);
778
812
  if (entry.isDirectory()) {
779
813
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
780
814
  walk(fullPath);
@@ -789,8 +823,8 @@ function findMarkdownFiles(dir) {
789
823
  }
790
824
  async function importContent(dirPath, options = {}) {
791
825
  const cwd = process.cwd();
792
- const absolutePath = path4.isAbsolute(dirPath) ? dirPath : path4.join(cwd, dirPath);
793
- if (!fs5.existsSync(absolutePath)) {
826
+ const absolutePath = path5.isAbsolute(dirPath) ? dirPath : path5.join(cwd, dirPath);
827
+ if (!fs6.existsSync(absolutePath)) {
794
828
  throw new Error(`Directory not found: ${dirPath}`);
795
829
  }
796
830
  const files = findMarkdownFiles(absolutePath);
@@ -816,8 +850,8 @@ async function importContent(dirPath, options = {}) {
816
850
  }
817
851
  const status = options.status || "draft";
818
852
  const importScript = generateImportScript(posts, status, options.tag);
819
- const scriptPath = path4.join(cwd, ".autoblogger-import.mjs");
820
- fs5.writeFileSync(scriptPath, importScript);
853
+ const scriptPath = path5.join(cwd, ".autoblogger-import.mjs");
854
+ fs6.writeFileSync(scriptPath, importScript);
821
855
  try {
822
856
  (0, import_child_process.execSync)(`node ${scriptPath}`, {
823
857
  cwd,
@@ -825,8 +859,8 @@ async function importContent(dirPath, options = {}) {
825
859
  });
826
860
  log("check", `Imported ${posts.length} posts as ${status}`);
827
861
  } finally {
828
- if (fs5.existsSync(scriptPath)) {
829
- fs5.unlinkSync(scriptPath);
862
+ if (fs6.existsSync(scriptPath)) {
863
+ fs6.unlinkSync(scriptPath);
830
864
  }
831
865
  }
832
866
  }
@@ -923,7 +957,7 @@ async function init(options = {}) {
923
957
  log("check", `Found ${project.prismaSchemaPath}`);
924
958
  }
925
959
  if (project.hasTailwind) {
926
- log("check", `Found ${path5.basename(project.tailwindConfigPath)}`);
960
+ log("check", `Found ${path6.basename(project.tailwindConfigPath)}`);
927
961
  }
928
962
  if (!project.appRouterPath) {
929
963
  console.log(import_picocolors3.default.red("Error: Could not find App Router (app/ or src/app/ directory)"));
@@ -932,7 +966,7 @@ async function init(options = {}) {
932
966
  }
933
967
  const contentCounts = {};
934
968
  for (const contentPath of project.contentPaths) {
935
- contentCounts[contentPath] = countMarkdownFiles(path5.join(cwd, contentPath));
969
+ contentCounts[contentPath] = countMarkdownFiles(path6.join(cwd, contentPath));
936
970
  }
937
971
  let answers = {
938
972
  dbProvider: "postgresql",
@@ -950,7 +984,7 @@ async function init(options = {}) {
950
984
  answers.importContent = true;
951
985
  answers.importPath = project.contentPaths[0];
952
986
  }
953
- const prismaPath = project.prismaSchemaPath || path5.join(cwd, "prisma", "schema.prisma");
987
+ const prismaPath = project.prismaSchemaPath || path6.join(cwd, "prisma", "schema.prisma");
954
988
  const conflicts = checkConflicts(prismaPath);
955
989
  if (conflicts.length > 0) {
956
990
  console.log(import_picocolors3.default.red(`
@@ -976,7 +1010,7 @@ Error: Found conflicting model names in your Prisma schema:`));
976
1010
  console.log(" - npx prisma generate");
977
1011
  }
978
1012
  if (answers.importContent && answers.importPath) {
979
- const count = contentCounts[answers.importPath] || countMarkdownFiles(path5.join(cwd, answers.importPath));
1013
+ const count = contentCounts[answers.importPath] || countMarkdownFiles(path6.join(cwd, answers.importPath));
980
1014
  console.log(`
981
1015
  Would import ${count} posts from ${answers.importPath}`);
982
1016
  }
@@ -985,11 +1019,11 @@ Would import ${count} posts from ${answers.importPath}`);
985
1019
  }
986
1020
  const filesToBackup = [];
987
1021
  if (project.hasPrisma) filesToBackup.push("prisma/schema.prisma");
988
- if (project.tailwindConfigPath) filesToBackup.push(path5.relative(cwd, project.tailwindConfigPath));
989
- if (fs6.existsSync(path5.join(cwd, "lib", "cms.ts"))) filesToBackup.push("lib/cms.ts");
1022
+ if (project.tailwindConfigPath) filesToBackup.push(path6.relative(cwd, project.tailwindConfigPath));
1023
+ if (fs7.existsSync(path6.join(cwd, "lib", "cms.ts"))) filesToBackup.push("lib/cms.ts");
990
1024
  if (filesToBackup.length > 0) {
991
1025
  const backupPath = createBackup(filesToBackup, cwd);
992
- log("backup", `Created backup at ${path5.relative(cwd, backupPath)}`);
1026
+ log("backup", `Created backup at ${path6.relative(cwd, backupPath)}`);
993
1027
  }
994
1028
  const mergeResult = mergeSchema(prismaPath, answers.dbProvider);
995
1029
  if (!mergeResult.success) {
@@ -997,38 +1031,38 @@ Would import ${count} posts from ${answers.importPath}`);
997
1031
  return;
998
1032
  }
999
1033
  writeSchema(prismaPath, mergeResult.content);
1000
- log("write", `Updated ${path5.relative(cwd, prismaPath)} (added 11 models)`);
1001
- const libDir = path5.join(cwd, "lib");
1002
- const cmsConfigPath = path5.join(libDir, "cms.ts");
1003
- if (fs6.existsSync(cmsConfigPath)) {
1034
+ log("write", `Updated ${path6.relative(cwd, prismaPath)} (added 11 models)`);
1035
+ const libDir = path6.join(cwd, "lib");
1036
+ const cmsConfigPath = path6.join(libDir, "cms.ts");
1037
+ if (fs7.existsSync(cmsConfigPath)) {
1004
1038
  log("skip", "lib/cms.ts already exists");
1005
1039
  } else {
1006
- if (!fs6.existsSync(libDir)) {
1007
- fs6.mkdirSync(libDir, { recursive: true });
1040
+ if (!fs7.existsSync(libDir)) {
1041
+ fs7.mkdirSync(libDir, { recursive: true });
1008
1042
  }
1009
- fs6.writeFileSync(cmsConfigPath, CMS_CONFIG_TEMPLATE);
1043
+ fs7.writeFileSync(cmsConfigPath, CMS_CONFIG_TEMPLATE);
1010
1044
  log("write", "Created lib/cms.ts");
1011
1045
  }
1012
- const apiRoutePath = path5.join(cwd, project.appRouterPath, "api", "cms", "[...path]", "route.ts");
1013
- if (fs6.existsSync(apiRoutePath)) {
1046
+ const apiRoutePath = path6.join(cwd, project.appRouterPath, "api", "cms", "[...path]", "route.ts");
1047
+ if (fs7.existsSync(apiRoutePath)) {
1014
1048
  log("skip", `${project.appRouterPath}/api/cms/[...path]/route.ts already exists`);
1015
1049
  } else {
1016
- const apiRouteDir = path5.dirname(apiRoutePath);
1017
- if (!fs6.existsSync(apiRouteDir)) {
1018
- fs6.mkdirSync(apiRouteDir, { recursive: true });
1050
+ const apiRouteDir = path6.dirname(apiRoutePath);
1051
+ if (!fs7.existsSync(apiRouteDir)) {
1052
+ fs7.mkdirSync(apiRouteDir, { recursive: true });
1019
1053
  }
1020
- fs6.writeFileSync(apiRoutePath, API_ROUTE_TEMPLATE);
1054
+ fs7.writeFileSync(apiRoutePath, API_ROUTE_TEMPLATE);
1021
1055
  log("write", `Created ${project.appRouterPath}/api/cms/[...path]/route.ts`);
1022
1056
  }
1023
- const dashboardPath = path5.join(cwd, project.appRouterPath, "writer", "[[...path]]", "page.tsx");
1024
- if (fs6.existsSync(dashboardPath)) {
1057
+ const dashboardPath = path6.join(cwd, project.appRouterPath, "writer", "[[...path]]", "page.tsx");
1058
+ if (fs7.existsSync(dashboardPath)) {
1025
1059
  log("skip", `${project.appRouterPath}/writer/[[...path]]/page.tsx already exists`);
1026
1060
  } else {
1027
- const dashboardDir = path5.dirname(dashboardPath);
1028
- if (!fs6.existsSync(dashboardDir)) {
1029
- fs6.mkdirSync(dashboardDir, { recursive: true });
1061
+ const dashboardDir = path6.dirname(dashboardPath);
1062
+ if (!fs7.existsSync(dashboardDir)) {
1063
+ fs7.mkdirSync(dashboardDir, { recursive: true });
1030
1064
  }
1031
- fs6.writeFileSync(dashboardPath, DASHBOARD_PAGE_TEMPLATE);
1065
+ fs7.writeFileSync(dashboardPath, DASHBOARD_PAGE_TEMPLATE);
1032
1066
  log("write", `Created ${project.appRouterPath}/writer/[[...path]]/page.tsx`);
1033
1067
  }
1034
1068
  if (project.tailwindConfigPath) {
@@ -1037,7 +1071,7 @@ Would import ${count} posts from ${answers.importPath}`);
1037
1071
  log("skip", "Tailwind config already includes autoblogger");
1038
1072
  } else if (patchResult.success && patchResult.content) {
1039
1073
  writeTailwindConfig(project.tailwindConfigPath, patchResult.content);
1040
- log("write", `Updated ${path5.basename(project.tailwindConfigPath)}`);
1074
+ log("write", `Updated ${path6.basename(project.tailwindConfigPath)}`);
1041
1075
  } else {
1042
1076
  log("warn", "Could not auto-patch Tailwind config. Please add manually:");
1043
1077
  console.log(import_picocolors3.default.gray(" content: ['./node_modules/autoblogger/dist/**/*.{js,mjs}']"));
@@ -1048,12 +1082,24 @@ Would import ${count} posts from ${answers.importPath}`);
1048
1082
  log("skip", "Tailwind CSS config already includes autoblogger");
1049
1083
  } else if (patchResult.success && patchResult.content) {
1050
1084
  writeTailwindConfig(project.tailwindCssPath, patchResult.content);
1051
- log("write", `Updated ${path5.basename(project.tailwindCssPath)} (Tailwind v4)`);
1085
+ log("write", `Updated ${path6.basename(project.tailwindCssPath)} (Tailwind v4)`);
1052
1086
  } else {
1053
1087
  log("warn", "Could not auto-patch Tailwind v4 CSS config. Please add manually:");
1054
1088
  console.log(import_picocolors3.default.gray(' @source "./node_modules/autoblogger/dist/**/*.{js,mjs}";'));
1055
1089
  }
1056
1090
  }
1091
+ const globalsCssPath = findGlobalsCss(cwd);
1092
+ if (globalsCssPath) {
1093
+ const cssResult = patchGlobalsCss(globalsCssPath);
1094
+ if (cssResult.alreadyPatched) {
1095
+ log("skip", "globals.css already imports autoblogger styles");
1096
+ } else if (cssResult.success) {
1097
+ log("write", `Updated ${path6.relative(cwd, globalsCssPath)} (added autoblogger CSS import)`);
1098
+ }
1099
+ } else {
1100
+ log("warn", "Could not find globals.css. Please add manually:");
1101
+ console.log(import_picocolors3.default.gray(" @import 'autoblogger/styles/autoblogger.css';"));
1102
+ }
1057
1103
  if (answers.runMigration) {
1058
1104
  console.log("");
1059
1105
  log("run", "prisma migrate dev --name add-autoblogger");
@@ -1075,10 +1121,10 @@ Would import ${count} posts from ${answers.importPath}`);
1075
1121
  }
1076
1122
  if (answers.importContent && answers.importPath) {
1077
1123
  console.log("");
1078
- const count = contentCounts[answers.importPath] || countMarkdownFiles(path5.join(cwd, answers.importPath));
1124
+ const count = contentCounts[answers.importPath] || countMarkdownFiles(path6.join(cwd, answers.importPath));
1079
1125
  log("info", `Found ${count} markdown files in ${answers.importPath}`);
1080
1126
  try {
1081
- await importContent(path5.join(cwd, answers.importPath), { status: "draft" });
1127
+ await importContent(path6.join(cwd, answers.importPath), { status: "draft" });
1082
1128
  } catch (error) {
1083
1129
  log("warn", `Content import failed: ${error instanceof Error ? error.message : error}`);
1084
1130
  console.log(import_picocolors3.default.gray(" You can import later with: npx autoblogger import <path>"));
@@ -1154,13 +1200,13 @@ async function main() {
1154
1200
  dryRun: flags.dryRun
1155
1201
  });
1156
1202
  } else if (command === "import") {
1157
- const path6 = args[1];
1158
- if (!path6) {
1203
+ const path7 = args[1];
1204
+ if (!path7) {
1159
1205
  console.error(import_picocolors4.default.red("Error: Please specify a path to import from"));
1160
1206
  console.log("\nUsage: npx autoblogger import <path>");
1161
1207
  process.exit(1);
1162
1208
  }
1163
- await importContent(path6, {
1209
+ await importContent(path7, {
1164
1210
  status: flags.status || "draft",
1165
1211
  tag: flags.tag,
1166
1212
  dryRun: flags.dryRun
@@ -3,11 +3,11 @@ declare const ARTICLE_LAYOUT: {
3
3
  readonly padding: 24;
4
4
  };
5
5
  declare const ARTICLE_CLASSES: {
6
- readonly container: "max-w-ab-content mx-auto px-ab-content-padding";
7
- readonly title: "text-ab-title font-bold";
8
- readonly subtitle: "text-ab-h2 text-muted-foreground";
6
+ readonly container: "max-w-[680px] mx-auto px-6";
7
+ readonly title: "text-title font-bold";
8
+ readonly subtitle: "text-h2 text-muted-foreground";
9
9
  readonly byline: "text-sm text-muted-foreground";
10
- readonly body: "text-ab-body prose dark:prose-invert";
10
+ readonly body: "text-body prose dark:prose-invert";
11
11
  readonly prose: "prose dark:prose-invert max-w-none";
12
12
  };
13
13
  type ArticleClasses = typeof ARTICLE_CLASSES;
@@ -3,11 +3,11 @@ declare const ARTICLE_LAYOUT: {
3
3
  readonly padding: 24;
4
4
  };
5
5
  declare const ARTICLE_CLASSES: {
6
- readonly container: "max-w-ab-content mx-auto px-ab-content-padding";
7
- readonly title: "text-ab-title font-bold";
8
- readonly subtitle: "text-ab-h2 text-muted-foreground";
6
+ readonly container: "max-w-[680px] mx-auto px-6";
7
+ readonly title: "text-title font-bold";
8
+ readonly subtitle: "text-h2 text-muted-foreground";
9
9
  readonly byline: "text-sm text-muted-foreground";
10
- readonly body: "text-ab-body prose dark:prose-invert";
10
+ readonly body: "text-body prose dark:prose-invert";
11
11
  readonly prose: "prose dark:prose-invert max-w-none";
12
12
  };
13
13
  type ArticleClasses = typeof ARTICLE_CLASSES;
@@ -29,11 +29,11 @@ var ARTICLE_LAYOUT = {
29
29
  padding: 24
30
30
  };
31
31
  var ARTICLE_CLASSES = {
32
- container: "max-w-ab-content mx-auto px-ab-content-padding",
33
- title: "text-ab-title font-bold",
34
- subtitle: "text-ab-h2 text-muted-foreground",
32
+ container: "max-w-[680px] mx-auto px-6",
33
+ title: "text-title font-bold",
34
+ subtitle: "text-h2 text-muted-foreground",
35
35
  byline: "text-sm text-muted-foreground",
36
- body: "text-ab-body prose dark:prose-invert",
36
+ body: "text-body prose dark:prose-invert",
37
37
  prose: "prose dark:prose-invert max-w-none"
38
38
  };
39
39
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/styles/article.ts"],"sourcesContent":["export const ARTICLE_LAYOUT = {\n maxWidth: 680,\n padding: 24,\n} as const\n\nexport const ARTICLE_CLASSES = {\n container: 'max-w-ab-content mx-auto px-ab-content-padding',\n title: 'text-ab-title font-bold',\n subtitle: 'text-ab-h2 text-muted-foreground',\n byline: 'text-sm text-muted-foreground',\n body: 'text-ab-body prose dark:prose-invert',\n prose: 'prose dark:prose-invert max-w-none',\n} as const\n\nexport type ArticleClasses = typeof ARTICLE_CLASSES\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,IAAM,iBAAiB;AAAA,EAC5B,UAAU;AAAA,EACV,SAAS;AACX;AAEO,IAAM,kBAAkB;AAAA,EAC7B,WAAW;AAAA,EACX,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/styles/article.ts"],"sourcesContent":["export const ARTICLE_LAYOUT = {\n maxWidth: 680,\n padding: 24,\n} as const\n\nexport const ARTICLE_CLASSES = {\n container: 'max-w-[680px] mx-auto px-6',\n title: 'text-title font-bold',\n subtitle: 'text-h2 text-muted-foreground',\n byline: 'text-sm text-muted-foreground',\n body: 'text-body prose dark:prose-invert',\n prose: 'prose dark:prose-invert max-w-none',\n} as const\n\nexport type ArticleClasses = typeof ARTICLE_CLASSES\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,IAAM,iBAAiB;AAAA,EAC5B,UAAU;AAAA,EACV,SAAS;AACX;AAEO,IAAM,kBAAkB;AAAA,EAC7B,WAAW;AAAA,EACX,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;","names":[]}
@@ -4,11 +4,11 @@ var ARTICLE_LAYOUT = {
4
4
  padding: 24
5
5
  };
6
6
  var ARTICLE_CLASSES = {
7
- container: "max-w-ab-content mx-auto px-ab-content-padding",
8
- title: "text-ab-title font-bold",
9
- subtitle: "text-ab-h2 text-muted-foreground",
7
+ container: "max-w-[680px] mx-auto px-6",
8
+ title: "text-title font-bold",
9
+ subtitle: "text-h2 text-muted-foreground",
10
10
  byline: "text-sm text-muted-foreground",
11
- body: "text-ab-body prose dark:prose-invert",
11
+ body: "text-body prose dark:prose-invert",
12
12
  prose: "prose dark:prose-invert max-w-none"
13
13
  };
14
14
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/styles/article.ts"],"sourcesContent":["export const ARTICLE_LAYOUT = {\n maxWidth: 680,\n padding: 24,\n} as const\n\nexport const ARTICLE_CLASSES = {\n container: 'max-w-ab-content mx-auto px-ab-content-padding',\n title: 'text-ab-title font-bold',\n subtitle: 'text-ab-h2 text-muted-foreground',\n byline: 'text-sm text-muted-foreground',\n body: 'text-ab-body prose dark:prose-invert',\n prose: 'prose dark:prose-invert max-w-none',\n} as const\n\nexport type ArticleClasses = typeof ARTICLE_CLASSES\n"],"mappings":";AAAO,IAAM,iBAAiB;AAAA,EAC5B,UAAU;AAAA,EACV,SAAS;AACX;AAEO,IAAM,kBAAkB;AAAA,EAC7B,WAAW;AAAA,EACX,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/styles/article.ts"],"sourcesContent":["export const ARTICLE_LAYOUT = {\n maxWidth: 680,\n padding: 24,\n} as const\n\nexport const ARTICLE_CLASSES = {\n container: 'max-w-[680px] mx-auto px-6',\n title: 'text-title font-bold',\n subtitle: 'text-h2 text-muted-foreground',\n byline: 'text-sm text-muted-foreground',\n body: 'text-body prose dark:prose-invert',\n prose: 'prose dark:prose-invert max-w-none',\n} as const\n\nexport type ArticleClasses = typeof ARTICLE_CLASSES\n"],"mappings":";AAAO,IAAM,iBAAiB;AAAA,EAC5B,UAAU;AAAA,EACV,SAAS;AACX;AAEO,IAAM,kBAAkB;AAAA,EAC7B,WAAW;AAAA,EACX,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;","names":[]}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Autoblogger Theme CSS (Isolated)
3
+ *
4
+ * All styles are scoped under .autoblogger container
5
+ * Variables are prefixed with --ab- to avoid conflicts
6
+ */
7
+
8
+ /* ============================================
9
+ CSS VARIABLES (namespaced with --ab-)
10
+ ============================================ */
11
+
12
+ :root {
13
+ --ab-background: 0 0% 100%;
14
+ --ab-foreground: 240 10% 3.9%;
15
+ --ab-card: 0 0% 100%;
16
+ --ab-card-foreground: 240 10% 3.9%;
17
+ --ab-popover: 0 0% 100%;
18
+ --ab-popover-foreground: 240 10% 3.9%;
19
+ --ab-primary: 240 5.9% 10%;
20
+ --ab-primary-foreground: 0 0% 98%;
21
+ --ab-secondary: 240 4.8% 95.9%;
22
+ --ab-secondary-foreground: 240 5.9% 10%;
23
+ --ab-muted: 240 4.8% 95.9%;
24
+ --ab-muted-foreground: 240 3.8% 46.1%;
25
+ --ab-accent: 240 4.8% 95.9%;
26
+ --ab-accent-foreground: 240 5.9% 10%;
27
+ --ab-destructive: 0 84.2% 60.2%;
28
+ --ab-destructive-foreground: 0 0% 98%;
29
+ --ab-border: 240 5.9% 90%;
30
+ --ab-input: 240 5.9% 90%;
31
+ --ab-ring: 240 10% 3.9%;
32
+ --ab-radius: 0.5rem;
33
+ }
34
+
35
+ .dark {
36
+ --ab-background: 240 10% 3.9%;
37
+ --ab-foreground: 0 0% 98%;
38
+ --ab-card: 240 10% 3.9%;
39
+ --ab-card-foreground: 0 0% 98%;
40
+ --ab-popover: 240 10% 3.9%;
41
+ --ab-popover-foreground: 0 0% 98%;
42
+ --ab-primary: 0 0% 98%;
43
+ --ab-primary-foreground: 240 5.9% 10%;
44
+ --ab-secondary: 240 3.7% 15.9%;
45
+ --ab-secondary-foreground: 0 0% 98%;
46
+ --ab-muted: 240 3.7% 15.9%;
47
+ --ab-muted-foreground: 240 5% 64.9%;
48
+ --ab-accent: 240 3.7% 15.9%;
49
+ --ab-accent-foreground: 0 0% 98%;
50
+ --ab-destructive: 0 62.8% 30.6%;
51
+ --ab-destructive-foreground: 0 0% 98%;
52
+ --ab-border: 240 3.7% 15.9%;
53
+ --ab-input: 240 3.7% 15.9%;
54
+ --ab-ring: 240 4.9% 83.9%;
55
+ }
56
+
57
+ /* Alias --ab-* to standard names for Tailwind compatibility */
58
+ .autoblogger {
59
+ --background: var(--ab-background);
60
+ --foreground: var(--ab-foreground);
61
+ --card: var(--ab-card);
62
+ --card-foreground: var(--ab-card-foreground);
63
+ --popover: var(--ab-popover);
64
+ --popover-foreground: var(--ab-popover-foreground);
65
+ --primary: var(--ab-primary);
66
+ --primary-foreground: var(--ab-primary-foreground);
67
+ --secondary: var(--ab-secondary);
68
+ --secondary-foreground: var(--ab-secondary-foreground);
69
+ --muted: var(--ab-muted);
70
+ --muted-foreground: var(--ab-muted-foreground);
71
+ --accent: var(--ab-accent);
72
+ --accent-foreground: var(--ab-accent-foreground);
73
+ --destructive: var(--ab-destructive);
74
+ --destructive-foreground: var(--ab-destructive-foreground);
75
+ --border: var(--ab-border);
76
+ --input: var(--ab-input);
77
+ --ring: var(--ab-ring);
78
+ --radius: var(--ab-radius);
79
+ }
80
+
81
+ /* ============================================
82
+ SCOPED BASE STYLES (only inside .autoblogger)
83
+ ============================================ */
84
+
85
+ .autoblogger {
86
+ background-color: hsl(var(--background));
87
+ color: hsl(var(--foreground));
88
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
89
+ -webkit-font-smoothing: antialiased;
90
+ -moz-osx-font-smoothing: grayscale;
91
+ }
92
+
93
+ .autoblogger *, .autoblogger *::before, .autoblogger *::after {
94
+ border-color: hsl(var(--border));
95
+ }
96
+
97
+ .autoblogger * {
98
+ transition-property: background-color, border-color;
99
+ transition-duration: 150ms;
100
+ transition-timing-function: ease-out;
101
+ }
102
+
103
+ .autoblogger a, .autoblogger button {
104
+ touch-action: manipulation;
105
+ }
106
+
107
+ /* ============================================
108
+ ANIMATIONS
109
+ ============================================ */
110
+
111
+ @keyframes ab-fadeInUp {
112
+ from { opacity: 0; transform: translateY(12px); }
113
+ to { opacity: 1; transform: translateY(0); }
114
+ }
115
+
116
+ .autoblogger .animate-fade-in-up {
117
+ opacity: 0;
118
+ animation: ab-fadeInUp 0.5s ease-out forwards;
119
+ }
120
+
121
+ @media (prefers-reduced-motion: reduce) {
122
+ .autoblogger .animate-fade-in-up {
123
+ animation: none;
124
+ opacity: 1;
125
+ }
126
+ }
127
+
128
+ /* ============================================
129
+ PROSE STYLES (scoped)
130
+ ============================================ */
131
+
132
+ .autoblogger .prose { max-width: none; }
133
+ .autoblogger .prose h1 { font-size: 22px; line-height: 1.3; font-weight: 700; margin-bottom: 1.5rem; }
134
+ .autoblogger .prose h2 { font-size: 18px; line-height: 1.4; font-weight: 700; margin-top: 2rem; margin-bottom: 1rem; }
135
+ .autoblogger .prose h3 { font-size: 16px; line-height: 1.4; font-weight: 700; margin-top: 1.5rem; margin-bottom: 0.75rem; }
136
+ .autoblogger .prose p { margin-bottom: 1rem; line-height: 1.625; }
137
+ .autoblogger .prose ul, .autoblogger .prose ol { margin-bottom: 1rem; padding-left: 1.5rem; }
138
+ .autoblogger .prose li { margin-bottom: 0.5rem; }
139
+ .autoblogger .prose ul li { list-style-type: disc; }
140
+ .autoblogger .prose ol li { list-style-type: decimal; }
141
+ .autoblogger .prose a { color: #2563eb; text-decoration: underline; }
142
+ .dark .autoblogger .prose a { color: #60a5fa; }
143
+ .autoblogger .prose code { background-color: hsl(var(--muted)); padding: 0.125rem 0.375rem; border-radius: 0.25rem; font-size: 0.875rem; }
144
+ .autoblogger .prose pre { background-color: hsl(var(--muted)); padding: 1rem; border-radius: 0.5rem; overflow-x: auto; margin-bottom: 1rem; }
145
+ .autoblogger .prose pre code { background-color: transparent; padding: 0; }
146
+ .autoblogger .prose blockquote { border-left: 4px solid hsl(var(--border)); padding-left: 1rem; font-style: italic; margin: 1rem 0; }
147
+ .autoblogger .prose img { border-radius: 0.5rem; margin: 1rem 0; }
148
+ .autoblogger .prose hr { margin: 2rem 0; border-color: hsl(var(--border)); }
149
+
150
+ /* ============================================
151
+ TIPTAP EDITOR STYLES (scoped)
152
+ ============================================ */
153
+
154
+ .autoblogger .ProseMirror:focus { outline: none; }
155
+ .autoblogger .ProseMirror p.is-editor-empty:first-child::before {
156
+ content: attr(data-placeholder);
157
+ color: #d1d5db;
158
+ pointer-events: none;
159
+ float: left;
160
+ height: 0;
161
+ }
162
+ .dark .autoblogger .ProseMirror p.is-editor-empty:first-child::before { color: #374151; }
163
+ .autoblogger .ProseMirror h1 { font-size: 22px; line-height: 1.3; }
164
+ .autoblogger .ProseMirror h2 { font-size: 18px; line-height: 1.4; }
165
+ .autoblogger .ProseMirror h3 { font-size: 16px; line-height: 1.4; }