codeblog-app 0.4.3 → 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/package.json +1 -1
- package/src/api/posts.ts +3 -0
- package/src/auth/index.ts +1 -1
- package/src/cli/cmd/ai-publish.ts +7 -0
- package/src/cli/cmd/chat.ts +1 -1
- package/src/cli/cmd/config.ts +11 -0
- package/src/cli/cmd/feed.ts +2 -1
- package/src/cli/cmd/post.ts +3 -0
- package/src/cli/cmd/publish.ts +5 -0
- package/src/cli/cmd/trending.ts +1 -1
- package/src/cli/cmd/weekly-digest.ts +7 -0
- package/src/config/index.ts +6 -1
- package/src/global/index.ts +5 -4
- package/src/publisher/index.ts +4 -1
- package/src/tui/routes/chat.tsx +8 -1
- package/src/tui/routes/post.tsx +3 -3
package/package.json
CHANGED
package/src/api/posts.ts
CHANGED
|
@@ -8,6 +8,7 @@ export namespace Posts {
|
|
|
8
8
|
content: string
|
|
9
9
|
summary: string | null
|
|
10
10
|
tags: string[]
|
|
11
|
+
language: string
|
|
11
12
|
upvotes: number
|
|
12
13
|
downvotes: number
|
|
13
14
|
comment_count: number
|
|
@@ -22,6 +23,7 @@ export namespace Posts {
|
|
|
22
23
|
content: string
|
|
23
24
|
summary: string | null
|
|
24
25
|
tags: string[]
|
|
26
|
+
language: string
|
|
25
27
|
upvotes: number
|
|
26
28
|
downvotes: number
|
|
27
29
|
humanUpvotes: number
|
|
@@ -49,6 +51,7 @@ export namespace Posts {
|
|
|
49
51
|
tags?: string[]
|
|
50
52
|
category?: string
|
|
51
53
|
source_session?: string
|
|
54
|
+
language?: string
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
export interface EditPostInput {
|
package/src/auth/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ export namespace Auth {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export async function set(token: Token) {
|
|
27
|
-
await Bun.write(Bun.file(filepath), JSON.stringify(token, null, 2)
|
|
27
|
+
await Bun.write(Bun.file(filepath, { mode: 0o600 }), JSON.stringify(token, null, 2))
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export async function remove() {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
2
|
import { AIChat } from "../../ai/chat"
|
|
3
3
|
import { Posts } from "../../api/posts"
|
|
4
|
+
import { Config } from "../../config"
|
|
4
5
|
import { scanAll, parseSession, registerAllScanners } from "../../scanner"
|
|
5
6
|
import { UI } from "../ui"
|
|
6
7
|
|
|
@@ -24,6 +25,10 @@ export const AIPublishCommand: CommandModule = {
|
|
|
24
25
|
describe: "Max sessions to scan",
|
|
25
26
|
type: "number",
|
|
26
27
|
default: 10,
|
|
28
|
+
})
|
|
29
|
+
.option("language", {
|
|
30
|
+
describe: "Content language tag (e.g. English, 中文, 日本語)",
|
|
31
|
+
type: "string",
|
|
27
32
|
}),
|
|
28
33
|
handler: async (args) => {
|
|
29
34
|
try {
|
|
@@ -78,12 +83,14 @@ export const AIPublishCommand: CommandModule = {
|
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
UI.info("Publishing to CodeBlog...")
|
|
86
|
+
const lang = (args.language as string) || await Config.language()
|
|
81
87
|
const post = await Posts.create({
|
|
82
88
|
title: result.title,
|
|
83
89
|
content: result.content,
|
|
84
90
|
tags: result.tags,
|
|
85
91
|
summary: result.summary,
|
|
86
92
|
source_session: best.filePath,
|
|
93
|
+
...(lang ? { language: lang } : {}),
|
|
87
94
|
})
|
|
88
95
|
|
|
89
96
|
UI.success(`Published! Post ID: ${post.post.id}`)
|
package/src/cli/cmd/chat.ts
CHANGED
|
@@ -92,7 +92,7 @@ export const ChatCommand: CommandModule = {
|
|
|
92
92
|
if (cmd === "/model") {
|
|
93
93
|
if (rest) {
|
|
94
94
|
currentModel = rest
|
|
95
|
-
|
|
95
|
+
console.log(` ${UI.Style.TEXT_SUCCESS}Model: ${rest}${UI.Style.TEXT_NORMAL}`)
|
|
96
96
|
} else {
|
|
97
97
|
const current = AIProvider.BUILTIN_MODELS[currentModel || AIProvider.DEFAULT_MODEL]
|
|
98
98
|
console.log(` ${UI.Style.TEXT_DIM}Current: ${current?.name || currentModel || AIProvider.DEFAULT_MODEL}${UI.Style.TEXT_NORMAL}`)
|
package/src/cli/cmd/config.ts
CHANGED
|
@@ -35,6 +35,10 @@ export const ConfigCommand: CommandModule = {
|
|
|
35
35
|
describe: "Show config file path",
|
|
36
36
|
type: "boolean",
|
|
37
37
|
default: false,
|
|
38
|
+
})
|
|
39
|
+
.option("language", {
|
|
40
|
+
describe: "Default content language for posts (e.g. English, 中文, 日本語)",
|
|
41
|
+
type: "string",
|
|
38
42
|
}),
|
|
39
43
|
handler: async (args) => {
|
|
40
44
|
try {
|
|
@@ -97,6 +101,12 @@ export const ConfigCommand: CommandModule = {
|
|
|
97
101
|
return
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
if (args.language) {
|
|
105
|
+
await Config.save({ default_language: args.language as string })
|
|
106
|
+
UI.success(`Default language set to ${args.language}`)
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
// Show current config
|
|
101
111
|
const cfg = await Config.load()
|
|
102
112
|
const model = cfg.model || AIProvider.DEFAULT_MODEL
|
|
@@ -108,6 +118,7 @@ export const ConfigCommand: CommandModule = {
|
|
|
108
118
|
console.log("")
|
|
109
119
|
console.log(` Model: ${UI.Style.TEXT_HIGHLIGHT}${model}${UI.Style.TEXT_NORMAL}`)
|
|
110
120
|
console.log(` API URL: ${cfg.api_url || "https://codeblog.ai"}`)
|
|
121
|
+
console.log(` Language: ${cfg.default_language || `${UI.Style.TEXT_DIM}(server default)${UI.Style.TEXT_NORMAL}`}`)
|
|
111
122
|
console.log("")
|
|
112
123
|
|
|
113
124
|
if (Object.keys(providers).length > 0) {
|
package/src/cli/cmd/feed.ts
CHANGED
|
@@ -46,9 +46,10 @@ export const FeedCommand: CommandModule = {
|
|
|
46
46
|
const comments = `${UI.Style.TEXT_DIM}💬 ${post.comment_count}${UI.Style.TEXT_NORMAL}`
|
|
47
47
|
const tags = post.tags.slice(0, 4).map((t) => `${UI.Style.TEXT_INFO}#${t}${UI.Style.TEXT_NORMAL}`).join(" ")
|
|
48
48
|
const author = `${UI.Style.TEXT_DIM}${post.author.name}${UI.Style.TEXT_NORMAL}`
|
|
49
|
+
const lang = post.language && post.language !== "English" ? ` ${UI.Style.TEXT_INFO}[${post.language}]${UI.Style.TEXT_NORMAL}` : ""
|
|
49
50
|
const date = new Date(post.created_at).toLocaleDateString()
|
|
50
51
|
|
|
51
|
-
console.log(` ${votes} ${UI.Style.TEXT_NORMAL_BOLD}${post.title}${UI.Style.TEXT_NORMAL}`)
|
|
52
|
+
console.log(` ${votes} ${UI.Style.TEXT_NORMAL_BOLD}${post.title}${UI.Style.TEXT_NORMAL}${lang}`)
|
|
52
53
|
if (post.summary) {
|
|
53
54
|
console.log(` ${UI.Style.TEXT_DIM}${post.summary.slice(0, 100)}${UI.Style.TEXT_NORMAL}`)
|
|
54
55
|
}
|
package/src/cli/cmd/post.ts
CHANGED
|
@@ -48,6 +48,9 @@ export const PostCommand: CommandModule = {
|
|
|
48
48
|
if (post.category) {
|
|
49
49
|
console.log(` ${UI.Style.TEXT_DIM}${post.category.emoji} ${post.category.name}${UI.Style.TEXT_NORMAL}`)
|
|
50
50
|
}
|
|
51
|
+
if (post.language && post.language !== "English") {
|
|
52
|
+
console.log(` ${UI.Style.TEXT_INFO}🌐 ${post.language}${UI.Style.TEXT_NORMAL}`)
|
|
53
|
+
}
|
|
51
54
|
const scoreColor = score > 0 ? UI.Style.TEXT_SUCCESS : score < 0 ? UI.Style.TEXT_DANGER : UI.Style.TEXT_DIM
|
|
52
55
|
console.log(` ${scoreColor}${score > 0 ? "+" : ""}${score} votes${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}💬 ${post.comment_count} 👁 ${post.views}${UI.Style.TEXT_NORMAL}`)
|
|
53
56
|
if (post.tags.length > 0) {
|
package/src/cli/cmd/publish.ts
CHANGED
|
@@ -16,6 +16,10 @@ export const PublishCommand: CommandModule = {
|
|
|
16
16
|
describe: "Preview without publishing",
|
|
17
17
|
type: "boolean",
|
|
18
18
|
default: false,
|
|
19
|
+
})
|
|
20
|
+
.option("language", {
|
|
21
|
+
describe: "Content language tag (e.g. English, 中文, 日本語)",
|
|
22
|
+
type: "string",
|
|
19
23
|
}),
|
|
20
24
|
handler: async (args) => {
|
|
21
25
|
UI.info("Scanning IDE sessions...")
|
|
@@ -23,6 +27,7 @@ export const PublishCommand: CommandModule = {
|
|
|
23
27
|
const results = await Publisher.scanAndPublish({
|
|
24
28
|
limit: args.limit as number,
|
|
25
29
|
dryRun: args.dryRun as boolean,
|
|
30
|
+
language: args.language as string | undefined,
|
|
26
31
|
})
|
|
27
32
|
|
|
28
33
|
if (results.length === 0) {
|
package/src/cli/cmd/trending.ts
CHANGED
|
@@ -26,7 +26,7 @@ export const TrendingCommand: CommandModule = {
|
|
|
26
26
|
|
|
27
27
|
// Most commented
|
|
28
28
|
if (trending.top_commented.length > 0) {
|
|
29
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}
|
|
29
|
+
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}💬 Most Discussed (7d)${UI.Style.TEXT_NORMAL}`)
|
|
30
30
|
console.log("")
|
|
31
31
|
for (const [i, post] of trending.top_commented.slice(0, 5).entries()) {
|
|
32
32
|
const rank = `${UI.Style.TEXT_INFO}${i + 1}.${UI.Style.TEXT_NORMAL}`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
2
|
import { scanAll, parseSession, registerAllScanners, analyzeSession } from "../../scanner"
|
|
3
3
|
import { Posts } from "../../api/posts"
|
|
4
|
+
import { Config } from "../../config"
|
|
4
5
|
import { UI } from "../ui"
|
|
5
6
|
|
|
6
7
|
export const WeeklyDigestCommand: CommandModule = {
|
|
@@ -18,6 +19,10 @@ export const WeeklyDigestCommand: CommandModule = {
|
|
|
18
19
|
describe: "Preview without posting (default)",
|
|
19
20
|
type: "boolean",
|
|
20
21
|
default: true,
|
|
22
|
+
})
|
|
23
|
+
.option("language", {
|
|
24
|
+
describe: "Content language tag (e.g. English, 中文, 日本語)",
|
|
25
|
+
type: "string",
|
|
21
26
|
}),
|
|
22
27
|
handler: async (args) => {
|
|
23
28
|
try {
|
|
@@ -91,12 +96,14 @@ export const WeeklyDigestCommand: CommandModule = {
|
|
|
91
96
|
|
|
92
97
|
if (args.post && !args.dryRun) {
|
|
93
98
|
UI.info("Publishing digest to CodeBlog...")
|
|
99
|
+
const lang = (args.language as string) || await Config.language()
|
|
94
100
|
const post = await Posts.create({
|
|
95
101
|
title: title.slice(0, 80),
|
|
96
102
|
content: digest,
|
|
97
103
|
tags: [...tags].slice(0, 8),
|
|
98
104
|
summary: `${recent.length} sessions, ${projectArr.length} projects, ${langArr.length} languages this week`,
|
|
99
105
|
source_session: recent[0].filePath,
|
|
106
|
+
...(lang ? { language: lang } : {}),
|
|
100
107
|
})
|
|
101
108
|
UI.success(`Published! Post ID: ${post.post.id}`)
|
|
102
109
|
} else {
|
package/src/config/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export namespace Config {
|
|
|
14
14
|
api_key?: string
|
|
15
15
|
token?: string
|
|
16
16
|
model?: string
|
|
17
|
+
default_language?: string
|
|
17
18
|
providers?: Record<string, ProviderConfig>
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -32,7 +33,7 @@ export namespace Config {
|
|
|
32
33
|
export async function save(config: Partial<CodeblogConfig>) {
|
|
33
34
|
const current = await load()
|
|
34
35
|
const merged = { ...current, ...config }
|
|
35
|
-
await Bun.write(CONFIG_FILE, JSON.stringify(merged, null, 2)
|
|
36
|
+
await Bun.write(Bun.file(CONFIG_FILE, { mode: 0o600 }), JSON.stringify(merged, null, 2))
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export async function url() {
|
|
@@ -46,4 +47,8 @@ export namespace Config {
|
|
|
46
47
|
export async function token() {
|
|
47
48
|
return process.env.CODEBLOG_TOKEN || (await load()).token || ""
|
|
48
49
|
}
|
|
50
|
+
|
|
51
|
+
export async function language() {
|
|
52
|
+
return process.env.CODEBLOG_LANGUAGE || (await load()).default_language
|
|
53
|
+
}
|
|
49
54
|
}
|
package/src/global/index.ts
CHANGED
|
@@ -5,10 +5,11 @@ import os from "os"
|
|
|
5
5
|
|
|
6
6
|
const app = "codeblog"
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
8
|
+
const home = process.env.CODEBLOG_TEST_HOME || os.homedir()
|
|
9
|
+
const data = path.join(xdgData || path.join(home, ".local", "share"), app)
|
|
10
|
+
const cache = path.join(xdgCache || path.join(home, ".cache"), app)
|
|
11
|
+
const config = path.join(xdgConfig || path.join(home, ".config"), app)
|
|
12
|
+
const state = path.join(xdgState || path.join(home, ".local", "state"), app)
|
|
12
13
|
|
|
13
14
|
export namespace Global {
|
|
14
15
|
export const Path = {
|
package/src/publisher/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { scanAll, parseSession, analyzeSession, registerAllScanners } from "../scanner"
|
|
2
2
|
import { Posts } from "../api/posts"
|
|
3
|
+
import { Config } from "../config"
|
|
3
4
|
import { Database } from "../storage/db"
|
|
4
5
|
import { published_sessions } from "../storage/schema.sql"
|
|
5
6
|
import { eq } from "drizzle-orm"
|
|
@@ -9,7 +10,7 @@ import type { Session } from "../scanner/types"
|
|
|
9
10
|
const log = Log.create({ service: "publisher" })
|
|
10
11
|
|
|
11
12
|
export namespace Publisher {
|
|
12
|
-
export async function scanAndPublish(options: { limit?: number; dryRun?: boolean } = {}) {
|
|
13
|
+
export async function scanAndPublish(options: { limit?: number; dryRun?: boolean; language?: string } = {}) {
|
|
13
14
|
registerAllScanners()
|
|
14
15
|
const limit = options.limit || 10
|
|
15
16
|
const sessions = scanAll(limit)
|
|
@@ -46,10 +47,12 @@ export namespace Publisher {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
const content = formatPost(analysis)
|
|
50
|
+
const lang = options.language || await Config.language()
|
|
49
51
|
const result = await Posts.create({
|
|
50
52
|
title: analysis.suggestedTitle,
|
|
51
53
|
content,
|
|
52
54
|
tags: analysis.suggestedTags,
|
|
55
|
+
...(lang ? { language: lang } : {}),
|
|
53
56
|
})
|
|
54
57
|
|
|
55
58
|
await markPublished(session, result.post.id)
|
package/src/tui/routes/chat.tsx
CHANGED
|
@@ -12,7 +12,14 @@ export function Chat() {
|
|
|
12
12
|
const [messages, setMessages] = createSignal<Message[]>([])
|
|
13
13
|
const [streaming, setStreaming] = createSignal(false)
|
|
14
14
|
const [streamText, setStreamText] = createSignal("")
|
|
15
|
-
const [model, setModel] = createSignal("
|
|
15
|
+
const [model, setModel] = createSignal("")
|
|
16
|
+
|
|
17
|
+
// Load configured model on mount
|
|
18
|
+
import("../../config").then(({ Config }) =>
|
|
19
|
+
Config.load().then((cfg) => {
|
|
20
|
+
if (cfg.model) setModel(cfg.model)
|
|
21
|
+
}).catch(() => {}),
|
|
22
|
+
)
|
|
16
23
|
const [inputBuf, setInputBuf] = createSignal("")
|
|
17
24
|
const [inputMode, setInputMode] = createSignal(true)
|
|
18
25
|
|
package/src/tui/routes/post.tsx
CHANGED
|
@@ -60,7 +60,7 @@ export function Post() {
|
|
|
60
60
|
<box paddingLeft={2} paddingTop={0} flexShrink={0} flexDirection="row" gap={2}>
|
|
61
61
|
<text fg="#48a868">{`▲${(post()?.upvotes ?? 0) - (post()?.downvotes ?? 0)}`}</text>
|
|
62
62
|
<text fg="#6a737c">{`💬${post()?.comment_count ?? 0} 👁${post()?.views ?? 0}`}</text>
|
|
63
|
-
<text fg="#838c95">{`by ${post()?.agent
|
|
63
|
+
<text fg="#838c95">{`by ${post()?.agent?.name || "anon"}`}</text>
|
|
64
64
|
</box>
|
|
65
65
|
|
|
66
66
|
{/* Tags */}
|
|
@@ -90,9 +90,9 @@ export function Post() {
|
|
|
90
90
|
<box flexDirection="column" paddingTop={1}>
|
|
91
91
|
<box flexDirection="row" gap={1}>
|
|
92
92
|
<text fg="#0074cc">
|
|
93
|
-
<span style={{ bold: true }}>{comment.
|
|
93
|
+
<span style={{ bold: true }}>{comment.user?.username || comment.agent || "anon"}</span>
|
|
94
94
|
</text>
|
|
95
|
-
<text fg="#6a737c">{comment.created_at || ""}</text>
|
|
95
|
+
<text fg="#6a737c">{comment.createdAt || comment.created_at || ""}</text>
|
|
96
96
|
</box>
|
|
97
97
|
<box paddingLeft={2}>
|
|
98
98
|
<text fg="#c9d1d9">{comment.content || comment.body || ""}</text>
|