careermate 0.1.0 → 0.1.1
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 +10 -7
- package/apps/mcp/src/index.ts +3 -1
- package/apps/web/DESIGN_GUIDE.md +2 -2
- package/apps/web/public/icon-180.png +0 -0
- package/apps/web/public/index.html +4 -1
- package/apps/web/public/lib.js +1 -1
- package/apps/web/public/pages/applications.js +17 -1
- package/apps/web/public/pages/settings.js +6 -4
- package/apps/web/public/styles.css +49 -36
- package/apps/web/src/info.ts +9 -22
- package/apps/web/src/routes.ts +17 -13
- package/apps/web/src/server.ts +33 -25
- package/apps/web/src/settings.ts +35 -17
- package/bin/careermate.mjs +8 -5
- package/dist/{mcpb-stage/dist/web.mjs → doctor.mjs} +1122 -1450
- package/dist/install-page/favicon.svg +1 -0
- package/dist/install-page/icon-180.png +0 -0
- package/dist/install-page/icon-512.png +0 -0
- package/dist/install-page/index.html +309 -167
- package/dist/install-page/llms-install.txt +202 -0
- package/dist/install-page/og.png +0 -0
- package/dist/install-page/shots/applications-dark.png +0 -0
- package/dist/install-page/shots/applications-light.png +0 -0
- package/dist/install-page/shots/documents-dark.png +0 -0
- package/dist/install-page/shots/documents-light.png +0 -0
- package/dist/install-page/shots/home-dark.png +0 -0
- package/dist/install-page/shots/home-light.png +0 -0
- package/dist/install-page/shots/interview-dark.png +0 -0
- package/dist/install-page/shots/interview-light.png +0 -0
- package/dist/install-page/shots/jobs-dark.png +0 -0
- package/dist/install-page/shots/jobs-light.png +0 -0
- package/dist/install-page/shots/settings-dark.png +0 -0
- package/dist/install-page/shots/settings-light.png +0 -0
- package/dist/install-page/start.html +346 -0
- package/dist/install-page/style.css +94 -3
- package/dist/mcp.mjs +326 -68
- package/dist/migrate.mjs +4715 -0
- package/dist/public/icon-180.png +0 -0
- package/dist/public/index.html +4 -1
- package/dist/public/lib.js +1 -1
- package/dist/public/pages/applications.js +17 -1
- package/dist/public/pages/settings.js +6 -4
- package/dist/public/styles.css +49 -36
- package/dist/web.mjs +323 -129
- package/docs/DEMO.md +3 -3
- package/docs/MCP_TOOLS.md +21 -2
- package/docs/ROADMAP.md +3 -3
- package/docs/RUNTIME_DISTRIBUTION_RESEARCH.md +135 -0
- package/docs/START_WORKFLOW.md +6 -0
- package/docs/SUPPORTED_AI_APPS.md +85 -33
- package/docs/WORKFLOWS.md +1 -1
- package/package.json +3 -2
- package/packages/core/src/services.ts +42 -1
- package/packages/db/src/connection.ts +30 -0
- package/packages/db/src/index.ts +1 -0
- package/packages/db/src/repositories.ts +109 -63
- package/packages/db/src/schema.ts +118 -0
- package/packages/exporters/src/html.ts +9 -12
- package/packages/mcp-tools/src/tools.ts +81 -0
- package/packages/shared/src/enums.ts +39 -0
- package/scripts/build-dist.mjs +27 -8
- package/scripts/build-mcpb.mjs +60 -4
- package/scripts/gen-og.mjs +86 -0
- package/scripts/init.ts +32 -3
- package/scripts/seed.ts +5 -5
- package/scripts/shots-seed.ts +192 -0
- package/scripts/shots.ts +89 -0
- package/tsconfig.json +3 -1
- package/dist/careermate.mcpb +0 -0
- package/dist/mcp-smoke.err +0 -3
- package/dist/mcpb-stage/README.md +0 -219
- package/dist/mcpb-stage/dist/install-page/index.html +0 -434
- package/dist/mcpb-stage/dist/install-page/style.css +0 -407
- package/dist/mcpb-stage/dist/install-page/vercel.json +0 -20
- package/dist/mcpb-stage/dist/mcp.mjs +0 -23704
- package/dist/mcpb-stage/dist/public/app.js +0 -118
- package/dist/mcpb-stage/dist/public/index.html +0 -41
- package/dist/mcpb-stage/dist/public/lib.js +0 -282
- package/dist/mcpb-stage/dist/public/pages/applications.js +0 -98
- package/dist/mcpb-stage/dist/public/pages/documents.js +0 -446
- package/dist/mcpb-stage/dist/public/pages/home.js +0 -263
- package/dist/mcpb-stage/dist/public/pages/interview.js +0 -230
- package/dist/mcpb-stage/dist/public/pages/jobs.js +0 -494
- package/dist/mcpb-stage/dist/public/pages/profile.js +0 -576
- package/dist/mcpb-stage/dist/public/pages/settings.js +0 -233
- package/dist/mcpb-stage/dist/public/styles.css +0 -420
- package/dist/mcpb-stage/manifest.json +0 -40
- package/docs/INSTALL.md +0 -148
- package/docs/INSTALL_AND_USAGE.md +0 -99
- package/install-page/index.html +0 -474
- package/install-page/style.css +0 -391
- package/install-page/vercel.json +0 -20
- /package/dist/{mcpb-stage/dist/public → install-page}/fonts/PretendardVariable.woff2 +0 -0
|
Binary file
|
package/dist/public/index.html
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>CareerMate — 내 커리어 흐름 관리</title>
|
|
7
7
|
<meta name="description" content="ChatGPT/Claude와 함께 쓰는 로컬 커리어 관리 도구" />
|
|
8
|
-
<
|
|
8
|
+
<meta name="theme-color" content="#faf8f4" media="(prefers-color-scheme: light)" />
|
|
9
|
+
<meta name="theme-color" content="#14130f" media="(prefers-color-scheme: dark)" />
|
|
10
|
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect width='24' height='24' rx='6' fill='%230f7256'/><text x='12' y='17' font-size='14' text-anchor='middle' fill='white' font-family='sans-serif' font-weight='bold'>C</text></svg>" />
|
|
11
|
+
<link rel="apple-touch-icon" href="/icon-180.png" />
|
|
9
12
|
<link rel="preload" href="/fonts/PretendardVariable.woff2" as="font" type="font/woff2" crossorigin />
|
|
10
13
|
<link rel="stylesheet" href="/styles.css" />
|
|
11
14
|
</head>
|
package/dist/public/lib.js
CHANGED
|
@@ -49,7 +49,7 @@ export function mount(node, ...children) { clear(node); appendChildren(node, chi
|
|
|
49
49
|
const ICONS = {
|
|
50
50
|
home: '<path d="M3 10.5 12 3l9 7.5"/><path d="M5 9.5V21h14V9.5"/>',
|
|
51
51
|
user: '<circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 4-6 8-6s8 2 8 6"/>',
|
|
52
|
-
briefcase: '<rect x="3" y="
|
|
52
|
+
briefcase: '<rect x="3" y="8" width="18" height="11" rx="2.5"/><path d="M8.5 8V6.5a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2V8"/><path d="M3 13h18"/>',
|
|
53
53
|
layers: '<path d="m12 3 9 5-9 5-9-5 9-5Z"/><path d="m3 13 9 5 9-5"/>',
|
|
54
54
|
file: '<path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8Z"/><path d="M14 3v5h5"/>',
|
|
55
55
|
mic: '<rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0 0 14 0"/><path d="M12 18v3"/>',
|
|
@@ -12,6 +12,19 @@ const STATUS_COLOR = {
|
|
|
12
12
|
final_passed: 'var(--green)', on_hold: 'var(--slate)', rejected: 'var(--red)',
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
// deadline(YYYY-MM-DD) → "마감 D-7" / "마감 D-day" / "마감 지남" (null if absent/invalid)
|
|
16
|
+
function ddayLabel(deadline) {
|
|
17
|
+
if (!deadline) return null;
|
|
18
|
+
const d = new Date(`${deadline}T00:00:00`);
|
|
19
|
+
if (isNaN(d.getTime())) return null;
|
|
20
|
+
const today = new Date();
|
|
21
|
+
today.setHours(0, 0, 0, 0);
|
|
22
|
+
const days = Math.round((d.getTime() - today.getTime()) / 86400000);
|
|
23
|
+
if (days > 0) return `마감 D-${days}`;
|
|
24
|
+
if (days === 0) return '마감 D-day';
|
|
25
|
+
return '마감 지남';
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
export async function render(ctx) {
|
|
16
29
|
ctx.setActions([]);
|
|
17
30
|
const data = await get('/api/applications');
|
|
@@ -58,6 +71,9 @@ export async function render(ctx) {
|
|
|
58
71
|
|
|
59
72
|
function AppRow(app) {
|
|
60
73
|
const job = app.job || {};
|
|
74
|
+
const subBits = [job.position, job.location].filter(Boolean);
|
|
75
|
+
const dday = ddayLabel(job.deadline);
|
|
76
|
+
if (dday) subBits.push(dday);
|
|
61
77
|
const sel = Select(
|
|
62
78
|
statusOptions.map((o) => ({ ...o, selected: o.value === app.status })),
|
|
63
79
|
{
|
|
@@ -69,7 +85,7 @@ export async function render(ctx) {
|
|
|
69
85
|
);
|
|
70
86
|
return ListRow({
|
|
71
87
|
title: job.company || '—',
|
|
72
|
-
sub:
|
|
88
|
+
sub: subBits.join(' · '),
|
|
73
89
|
trailing: [
|
|
74
90
|
app.fit_score != null
|
|
75
91
|
? el('span', { class: `strong tnum ${scoreClass(app.fit_score)}` }, `${app.fit_score}점`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Settings — data location, connection, theme, backup/export, and danger zone.
|
|
2
2
|
// Everything stays local; this page surfaces where data lives and how to manage it.
|
|
3
3
|
import {
|
|
4
|
-
el, get, post, icon, navigate, Card,
|
|
4
|
+
el, get, post, icon, navigate, Card, Btn, IconBtn, Input,
|
|
5
5
|
openModal, copyText, downloadUrl, fmtRelative, toastOk, toastError, mount,
|
|
6
6
|
} from '/lib.js';
|
|
7
7
|
|
|
@@ -134,12 +134,14 @@ function Theme() {
|
|
|
134
134
|
|
|
135
135
|
/* ---------------------------------------------------------- 내 데이터 */
|
|
136
136
|
function MyData(counts = {}) {
|
|
137
|
-
const
|
|
138
|
-
|
|
137
|
+
const items = Object.entries(COUNT_LABELS).map(([key, label]) =>
|
|
138
|
+
el('div', { class: 'countstrip__item' },
|
|
139
|
+
el('div', { class: 'countstrip__num tnum' }, String(counts[key] ?? 0)),
|
|
140
|
+
el('div', { class: 'countstrip__label' }, label)));
|
|
139
141
|
return Card({
|
|
140
142
|
title: '내 데이터',
|
|
141
143
|
sub: '이 컴퓨터에 저장된 항목 수',
|
|
142
|
-
body: el('div', { class: '
|
|
144
|
+
body: el('div', { class: 'countstrip' }, ...items),
|
|
143
145
|
});
|
|
144
146
|
}
|
|
145
147
|
|
package/dist/public/styles.css
CHANGED
|
@@ -19,24 +19,24 @@
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
:root {
|
|
22
|
-
/*
|
|
23
|
-
--bg: #
|
|
24
|
-
--bg-subtle: #
|
|
25
|
-
--bg-sunken: #
|
|
22
|
+
/* Warm paper neutrals (light) — unified with the public landing page */
|
|
23
|
+
--bg: #faf8f4;
|
|
24
|
+
--bg-subtle: #f2efe8;
|
|
25
|
+
--bg-sunken: #ece8df;
|
|
26
26
|
--surface: #ffffff;
|
|
27
|
-
--surface-hover: #
|
|
28
|
-
--border: #
|
|
29
|
-
--border-strong: #
|
|
30
|
-
--text: #
|
|
31
|
-
--text-secondary: #
|
|
32
|
-
--text-tertiary: #
|
|
27
|
+
--surface-hover: #f5f2ec;
|
|
28
|
+
--border: #e6e1d6;
|
|
29
|
+
--border-strong: #d6d0c2;
|
|
30
|
+
--text: #1c1a16;
|
|
31
|
+
--text-secondary: #595449;
|
|
32
|
+
--text-tertiary: #8b867a;
|
|
33
33
|
--text-on-accent: #ffffff;
|
|
34
34
|
|
|
35
|
-
/* Accent —
|
|
36
|
-
--accent: #
|
|
37
|
-
--accent-hover: #
|
|
38
|
-
--accent-soft: #
|
|
39
|
-
--accent-border: #
|
|
35
|
+
/* Accent — deep emerald (single flat accent, no gradient) */
|
|
36
|
+
--accent: #0f7256;
|
|
37
|
+
--accent-hover: #0b5b44;
|
|
38
|
+
--accent-soft: #e7f1ec;
|
|
39
|
+
--accent-border: #bcdccd;
|
|
40
40
|
|
|
41
41
|
/* Semantic */
|
|
42
42
|
--green: #15803d; --green-soft: #ecfdf3; --green-border: #bbf7d0;
|
|
@@ -67,31 +67,38 @@
|
|
|
67
67
|
|
|
68
68
|
@media (prefers-color-scheme: dark) {
|
|
69
69
|
:root:not([data-theme='light']) {
|
|
70
|
-
--bg: #
|
|
71
|
-
--bg-subtle: #
|
|
72
|
-
--bg-sunken: #
|
|
73
|
-
--surface: #
|
|
74
|
-
--surface-hover: #
|
|
75
|
-
--border: #
|
|
76
|
-
--border-strong: #
|
|
77
|
-
--text: #
|
|
78
|
-
--text-secondary: #
|
|
79
|
-
--text-tertiary: #
|
|
80
|
-
--accent: #
|
|
81
|
-
--accent-hover: #
|
|
82
|
-
--accent-soft: #
|
|
83
|
-
--accent-border: #
|
|
70
|
+
--bg: #14130f;
|
|
71
|
+
--bg-subtle: #201d17;
|
|
72
|
+
--bg-sunken: #100f0b;
|
|
73
|
+
--surface: #1b1915;
|
|
74
|
+
--surface-hover: #262219;
|
|
75
|
+
--border: #2e2a22;
|
|
76
|
+
--border-strong: #3e3930;
|
|
77
|
+
--text: #f2eee5;
|
|
78
|
+
--text-secondary: #aaa599;
|
|
79
|
+
--text-tertiary: #79746a;
|
|
80
|
+
--accent: #34d399;
|
|
81
|
+
--accent-hover: #6ee7b7;
|
|
82
|
+
--accent-soft: #11281d;
|
|
83
|
+
--accent-border: #1f4f3c;
|
|
84
|
+
--text-on-accent: #05241a;
|
|
84
85
|
--green-soft:#0e2a1a; --teal-soft:#0c2826; --blue-soft:#11203f; --violet-soft:#1e1638;
|
|
85
86
|
--amber-soft:#2a2008; --red-soft:#2c1414; --slate-soft:#1f242b;
|
|
87
|
+
/* lighter semantic foregrounds so status badges stay legible on dark soft bg */
|
|
88
|
+
--green:#4ade80; --teal:#2dd4bf; --blue:#60a5fa; --violet:#a78bfa; --amber:#fbbf24; --red:#ef4444; --slate:#94a3b8;
|
|
89
|
+
--green-border:#1f4f3c; --teal-border:#155e57; --blue-border:#1e3a6b; --violet-border:#3b2f6b; --amber-border:#5c4410; --red-border:#5c2020; --slate-border:#3a4250;
|
|
86
90
|
--shadow-lg: 0 12px 40px rgba(0,0,0,.5);
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
93
|
:root[data-theme='dark'] {
|
|
90
|
-
--bg:#
|
|
91
|
-
--border:#
|
|
92
|
-
--accent:#
|
|
94
|
+
--bg:#14130f; --bg-subtle:#201d17; --bg-sunken:#100f0b; --surface:#1b1915; --surface-hover:#262219;
|
|
95
|
+
--border:#2e2a22; --border-strong:#3e3930; --text:#f2eee5; --text-secondary:#aaa599; --text-tertiary:#79746a;
|
|
96
|
+
--accent:#34d399; --accent-hover:#6ee7b7; --accent-soft:#11281d; --accent-border:#1f4f3c; --text-on-accent:#05241a;
|
|
93
97
|
--green-soft:#0e2a1a; --teal-soft:#0c2826; --blue-soft:#11203f; --violet-soft:#1e1638;
|
|
94
|
-
--amber-soft:#2a2008; --red-soft:#2c1414; --slate-soft:#1f242b;
|
|
98
|
+
--amber-soft:#2a2008; --red-soft:#2c1414; --slate-soft:#1f242b;
|
|
99
|
+
--green:#4ade80; --teal:#2dd4bf; --blue:#60a5fa; --violet:#a78bfa; --amber:#fbbf24; --red:#ef4444; --slate:#94a3b8;
|
|
100
|
+
--green-border:#1f4f3c; --teal-border:#155e57; --blue-border:#1e3a6b; --violet-border:#3b2f6b; --amber-border:#5c4410; --red-border:#5c2020; --slate-border:#3a4250;
|
|
101
|
+
--shadow-lg:0 12px 40px rgba(0,0,0,.5);
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
* { box-sizing: border-box; }
|
|
@@ -120,7 +127,7 @@ h1,h2,h3,h4 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
|
|
|
120
127
|
.sidebar__logo {
|
|
121
128
|
width: 30px; height: 30px; border-radius: 8px; flex: none;
|
|
122
129
|
background: var(--accent);
|
|
123
|
-
display: grid; place-items: center; color:
|
|
130
|
+
display: grid; place-items: center; color: var(--text-on-accent); font-weight: 800; font-size: 15px;
|
|
124
131
|
}
|
|
125
132
|
.sidebar__title { font-weight: 700; font-size: 15px; letter-spacing: -.01em; }
|
|
126
133
|
.sidebar__subtitle { font-size: 12px; color: var(--text-tertiary); margin-top: 1px; }
|
|
@@ -135,7 +142,7 @@ h1,h2,h3,h4 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
|
|
|
135
142
|
.nav__item svg { width: 17px; height: 17px; flex: none; }
|
|
136
143
|
.nav__badge { margin-left: auto; font-size: 11.5px; background: var(--bg-sunken); color: var(--text-secondary);
|
|
137
144
|
padding: 1px 7px; border-radius: 999px; font-weight: 600; }
|
|
138
|
-
.nav__item.is-active .nav__badge { background: var(--accent); color:
|
|
145
|
+
.nav__item.is-active .nav__badge { background: var(--accent); color: var(--text-on-accent); }
|
|
139
146
|
.sidebar__foot { padding: 12px 16px; border-top: 1px solid var(--border); font-size: 12px; color: var(--text-tertiary); }
|
|
140
147
|
.sidebar__foot .dot { display:inline-block; width:7px; height:7px; border-radius:999px; background: var(--green); margin-right:6px; }
|
|
141
148
|
|
|
@@ -184,6 +191,12 @@ h1,h2,h3,h4 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
|
|
|
184
191
|
.stat__label svg { width: 15px; height: 15px; color: var(--text-tertiary); }
|
|
185
192
|
.stat__value { font-size: 26px; font-weight: 700; letter-spacing: -.02em; margin-top: 6px; }
|
|
186
193
|
.stat__hint { font-size: 12.5px; color: var(--text-tertiary); margin-top: 2px; }
|
|
194
|
+
/* borderless count strip (settings 내 데이터) — flat counts, never a card-in-card */
|
|
195
|
+
.countstrip { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px 24px; }
|
|
196
|
+
.countstrip__item { display: flex; flex-direction: column; gap: 2px; }
|
|
197
|
+
.countstrip__num { font-size: 24px; font-weight: 700; letter-spacing: -.02em; }
|
|
198
|
+
.countstrip__label { font-size: 13px; color: var(--text-secondary); }
|
|
199
|
+
@media (max-width: 560px){ .countstrip { grid-template-columns: repeat(2, 1fr); } }
|
|
187
200
|
|
|
188
201
|
/* ----------------------------------------------------------------- buttons */
|
|
189
202
|
.btn {
|
|
@@ -195,7 +208,7 @@ h1,h2,h3,h4 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
|
|
|
195
208
|
.btn:hover { background: var(--surface-hover); }
|
|
196
209
|
.btn:disabled { opacity: .5; cursor: not-allowed; }
|
|
197
210
|
.btn svg { width: 15px; height: 15px; }
|
|
198
|
-
.btn--primary { background: var(--accent); border-color: var(--accent); color:
|
|
211
|
+
.btn--primary { background: var(--accent); border-color: var(--accent); color: var(--text-on-accent); }
|
|
199
212
|
.btn--primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
|
200
213
|
.btn--ghost { border-color: transparent; background: transparent; }
|
|
201
214
|
.btn--ghost:hover { background: var(--surface-hover); }
|