Skip to content

Commit 3ddfa29

Browse files
Ray0907claude
andcommitted
restore: bring back all original cchub pages and components
Mission Control is additive — original pages should coexist. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ce12a18 commit 3ddfa29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+4062
-0
lines changed

app/app.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default defineAppConfig({
2+
ui: {
3+
colors: {
4+
primary: 'claude',
5+
neutral: 'stone'
6+
}
7+
}
8+
})

app/assets/css/main.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
@import "tailwindcss" theme(static);
2+
@import "@nuxt/ui";
3+
4+
@theme static {
5+
--font-sans: 'Public Sans', sans-serif;
6+
7+
--color-claude-50: #fcf4f2;
8+
--color-claude-100: #f9e6e1;
9+
--color-claude-200: #f2c9be;
10+
--color-claude-300: #e9a390;
11+
--color-claude-400: #df785c;
12+
--color-claude-500: #d54e29;
13+
--color-claude-600: #b34122;
14+
--color-claude-700: #91351c;
15+
--color-claude-800: #732a16;
16+
--color-claude-900: #551f10;
17+
--color-claude-950: #37140a;
18+
}

app/components/CodeViewer.vue

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<script setup lang="ts">
2+
const props = withDefaults(defineProps<{
3+
content: string
4+
language?: string
5+
can_edit?: boolean
6+
}>(), {
7+
can_edit: false
8+
})
9+
10+
const emit = defineEmits<{
11+
save: [content: string]
12+
}>()
13+
14+
const { copy, copied } = useClipboard()
15+
16+
const is_editing = ref(false)
17+
const content_draft = ref('')
18+
19+
const lines = computed(() => props.content.split('\n'))
20+
21+
function startEditing() {
22+
content_draft.value = props.content
23+
is_editing.value = true
24+
}
25+
26+
function handleSave() {
27+
emit('save', content_draft.value)
28+
is_editing.value = false
29+
}
30+
31+
function handleCancel() {
32+
is_editing.value = false
33+
}
34+
</script>
35+
36+
<template>
37+
<div class="relative overflow-auto">
38+
<template v-if="is_editing">
39+
<textarea
40+
v-model="content_draft"
41+
class="w-full p-4 text-sm font-mono bg-transparent border border-(--ui-border) rounded-md resize-y focus:outline-none focus:ring-2 focus:ring-(--ui-primary)"
42+
style="min-height: 300px"
43+
/>
44+
<div class="flex gap-2 mt-2">
45+
<UButton
46+
icon="i-lucide-save"
47+
label="Save"
48+
color="primary"
49+
variant="soft"
50+
size="xs"
51+
@click="handleSave"
52+
/>
53+
<UButton
54+
icon="i-lucide-x"
55+
label="Cancel"
56+
color="neutral"
57+
variant="ghost"
58+
size="xs"
59+
@click="handleCancel"
60+
/>
61+
</div>
62+
</template>
63+
64+
<template v-else>
65+
<div class="absolute top-2 right-2 z-10 flex gap-1">
66+
<UButton
67+
v-if="can_edit"
68+
icon="i-lucide-pencil"
69+
color="neutral"
70+
variant="ghost"
71+
size="xs"
72+
@click="startEditing"
73+
/>
74+
<UButton
75+
:icon="copied ? 'i-lucide-check' : 'i-lucide-copy'"
76+
color="neutral"
77+
variant="ghost"
78+
size="xs"
79+
@click="copy(content)"
80+
/>
81+
</div>
82+
83+
<div class="p-4 font-mono text-sm">
84+
<table class="border-collapse">
85+
<tbody>
86+
<tr v-for="(line, idx) in lines" :key="idx">
87+
<td class="pr-4 text-right text-dimmed select-none align-top w-1">
88+
{{ idx + 1 }}
89+
</td>
90+
<td class="whitespace-pre-wrap break-words">{{ line }}</td>
91+
</tr>
92+
</tbody>
93+
</table>
94+
</div>
95+
</template>
96+
</div>
97+
</template>

app/components/ColorModeButton.vue

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script setup lang="ts">
2+
const colorMode = useColorMode()
3+
4+
const icon = computed(() => {
5+
if (colorMode.preference === 'system') return 'i-lucide-monitor'
6+
return colorMode.value === 'dark' ? 'i-lucide-moon' : 'i-lucide-sun'
7+
})
8+
9+
function cycleColorMode() {
10+
const modes = ['light', 'dark', 'system'] as const
11+
const idx = modes.indexOf(colorMode.preference as typeof modes[number])
12+
colorMode.preference = modes[(idx + 1) % modes.length]
13+
}
14+
</script>
15+
16+
<template>
17+
<UButton
18+
:icon="icon"
19+
color="neutral"
20+
variant="ghost"
21+
square
22+
@click="cycleColorMode"
23+
/>
24+
</template>

app/components/JsonEditor.vue

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<script setup lang="ts">
2+
const props = defineProps<{
3+
modelValue: object | string
4+
}>()
5+
6+
const emit = defineEmits<{
7+
'update:modelValue': [value: object | string]
8+
}>()
9+
10+
const content_text = ref('')
11+
const error_message = ref('')
12+
13+
watchEffect(() => {
14+
if (typeof props.modelValue === 'string') {
15+
content_text.value = props.modelValue
16+
}
17+
else {
18+
content_text.value = JSON.stringify(props.modelValue, null, '\t')
19+
}
20+
})
21+
22+
function handleInput(event: Event) {
23+
const target = event.target as HTMLTextAreaElement
24+
content_text.value = target.value
25+
validateAndEmit(target.value)
26+
}
27+
28+
function validateAndEmit(raw: string) {
29+
try {
30+
const parsed = JSON.parse(raw)
31+
error_message.value = ''
32+
emit('update:modelValue', parsed)
33+
}
34+
catch (err) {
35+
error_message.value = err instanceof Error ? err.message : 'Invalid JSON'
36+
}
37+
}
38+
39+
function formatJson() {
40+
try {
41+
const parsed = JSON.parse(content_text.value)
42+
content_text.value = JSON.stringify(parsed, null, '\t')
43+
error_message.value = ''
44+
emit('update:modelValue', parsed)
45+
}
46+
catch (err) {
47+
error_message.value = err instanceof Error ? err.message : 'Invalid JSON'
48+
}
49+
}
50+
</script>
51+
52+
<template>
53+
<div class="flex flex-col h-full">
54+
<div class="flex items-center justify-between gap-2 p-2 border-b border-default">
55+
<span
56+
v-if="error_message"
57+
class="text-xs text-red-500 truncate"
58+
>
59+
{{ error_message }}
60+
</span>
61+
<span v-else />
62+
<UButton
63+
icon="lucide:align-left"
64+
label="Format"
65+
color="neutral"
66+
variant="ghost"
67+
size="xs"
68+
@click="formatJson"
69+
/>
70+
</div>
71+
72+
<div class="flex-1 overflow-auto">
73+
<textarea
74+
:value="content_text"
75+
class="w-full h-full p-4 font-mono text-sm bg-transparent resize-none focus:outline-none"
76+
spellcheck="false"
77+
@input="handleInput"
78+
/>
79+
</div>
80+
</div>
81+
</template>

app/components/MarkdownEditor.vue

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<script setup lang="ts">
2+
defineProps<{
3+
modelValue: string
4+
}>()
5+
6+
const emit = defineEmits<{
7+
'update:modelValue': [value: string]
8+
}>()
9+
10+
const is_preview = ref(false)
11+
12+
function handleInput(event: Event) {
13+
const target = event.target as HTMLTextAreaElement
14+
emit('update:modelValue', target.value)
15+
}
16+
</script>
17+
18+
<template>
19+
<div class="flex flex-col h-full">
20+
<div class="flex items-center justify-end gap-2 p-2 border-b border-default">
21+
<UButton
22+
:icon="is_preview ? 'lucide:pencil' : 'lucide:eye'"
23+
:label="is_preview ? 'Edit' : 'Preview'"
24+
color="neutral"
25+
variant="ghost"
26+
size="xs"
27+
@click="is_preview = !is_preview"
28+
/>
29+
</div>
30+
31+
<div class="flex-1 overflow-auto">
32+
<textarea
33+
v-if="!is_preview"
34+
:value="modelValue"
35+
class="w-full h-full p-4 font-mono text-sm bg-transparent resize-none focus:outline-none"
36+
spellcheck="false"
37+
@input="handleInput"
38+
/>
39+
<MDC
40+
v-else
41+
:value="modelValue"
42+
tag="article"
43+
class="prose prose-sm dark:prose-invert max-w-none p-4"
44+
/>
45+
</div>
46+
</div>
47+
</template>

app/components/MarkdownViewer.vue

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<script setup lang="ts">
2+
const props = withDefaults(defineProps<{
3+
content_raw: string
4+
can_edit?: boolean
5+
}>(), {
6+
can_edit: false
7+
})
8+
9+
const emit = defineEmits<{
10+
save: [content: string]
11+
}>()
12+
13+
const is_editing = ref(false)
14+
const content_draft = ref('')
15+
16+
const content_model = computed({
17+
get: () => is_editing.value ? content_draft.value : props.content_raw,
18+
set: (val: string) => { content_draft.value = val }
19+
})
20+
21+
function startEditing(): void {
22+
content_draft.value = props.content_raw
23+
is_editing.value = true
24+
}
25+
26+
function handleSave(): void {
27+
emit('save', content_draft.value)
28+
is_editing.value = false
29+
}
30+
31+
function handleCancel(): void {
32+
content_draft.value = props.content_raw
33+
is_editing.value = false
34+
}
35+
</script>
36+
37+
<template>
38+
<div class="relative">
39+
<!-- Action buttons -->
40+
<div v-if="can_edit && !is_editing" class="absolute top-2 right-2 z-10">
41+
<UButton
42+
icon="i-lucide-pencil"
43+
color="neutral"
44+
variant="ghost"
45+
size="xs"
46+
@click="startEditing"
47+
/>
48+
</div>
49+
50+
<!-- Editor -->
51+
<UEditor
52+
v-model="content_model"
53+
content-type="markdown"
54+
:editable="is_editing"
55+
class="min-h-[200px]"
56+
/>
57+
58+
<!-- Save / Cancel bar -->
59+
<div v-if="is_editing" class="flex gap-2 mt-2 px-2 pb-2">
60+
<UButton
61+
icon="i-lucide-save"
62+
label="Save"
63+
color="primary"
64+
variant="soft"
65+
size="xs"
66+
@click="handleSave"
67+
/>
68+
<UButton
69+
icon="i-lucide-x"
70+
label="Cancel"
71+
color="neutral"
72+
variant="ghost"
73+
size="xs"
74+
@click="handleCancel"
75+
/>
76+
</div>
77+
</div>
78+
</template>

app/components/StatCard.vue

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
defineProps<{
3+
label: string
4+
count: number | string
5+
icon: string
6+
}>()
7+
</script>
8+
9+
<template>
10+
<UCard>
11+
<div class="flex items-center gap-3">
12+
<div class="flex items-center justify-center size-10 rounded-lg bg-primary/10">
13+
<UIcon :name="icon" class="size-5 text-primary" />
14+
</div>
15+
<div>
16+
<p class="text-sm text-dimmed">{{ label }}</p>
17+
<p class="text-2xl font-semibold">{{ count }}</p>
18+
</div>
19+
</div>
20+
</UCard>
21+
</template>

0 commit comments

Comments
 (0)