Skip to content

Commit 2da9e88

Browse files
committed
feat: commit media files to github
1 parent 9ad324d commit 2da9e88

File tree

21 files changed

+277
-127
lines changed

21 files changed

+277
-127
lines changed

playground/docus/nuxt.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,13 @@ export default defineNuxtConfig({
1616
api: 'http://localhost:3000',
1717
},
1818
},
19+
contentStudio: {
20+
repository: {
21+
owner: 'nuxt-content',
22+
repo: 'studio',
23+
branch: 'edit-preview',
24+
rootDir: 'playground/docus',
25+
},
26+
},
1927
compatibilityDate: '2025-08-26',
2028
})

src/app/src/components/panel/base/PanelBaseBodyEditor.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ defineProps({
88
type: Object as PropType<DraftItem>,
99
required: true,
1010
},
11+
readOnly: {
12+
type: Boolean,
13+
required: false,
14+
default: false,
15+
},
1116
})
1217
1318
const { context } = useStudio()
@@ -17,9 +22,11 @@ const { context } = useStudio()
1722
<PanelContentEditor
1823
v-if="context.feature.value === StudioFeature.Content"
1924
:draft-item="draftItem"
25+
:read-only="readOnly"
2026
/>
2127
<PanelMediaEditor
2228
v-else
2329
:draft-item="draftItem"
30+
:read-only="readOnly"
2431
/>
2532
</template>

src/app/src/components/panel/base/PanelBaseFooter.vue

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,30 @@ const { ui, host } = useStudio()
66
77
const uiConfig = ui.config
88
const user = host.user.get()
9+
const repositoryUrl = computed(() => {
10+
switch (host.repository.provider) {
11+
case 'github':
12+
return `https://github.com/${host.repository.owner}/${host.repository.repo}/tree/${host.repository.branch}`
13+
default:
14+
return ''
15+
}
16+
})
917
1018
const userMenuItems = computed(() => [
11-
{
19+
[{
20+
label: 'Open Repository',
21+
icon: 'i-lucide-github',
22+
onClick: () => {
23+
window.open(repositoryUrl.value, '_blank')
24+
},
25+
}],
26+
[{
1227
label: 'Sign out',
1328
icon: 'i-lucide-log-out',
1429
onClick: () => {
1530
alert('TODO: delete cookie manually')
1631
},
17-
},
32+
}]
1833
])
1934
</script>
2035

src/app/src/components/panel/base/PanelBaseHeader.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useStudio } from '../../../composables/useStudio'
33
import { StudioFeature } from '../../../types'
44
import { ROOT_ITEM } from '../../../utils/tree'
55
6-
const { ui, context, documentTree, mediaTree } = useStudio()
6+
const { ui, git, context, documentTree, mediaTree, draftDocuments, draftMedias } = useStudio()
77
88
const features = [{
99
label: 'Content',
@@ -28,6 +28,16 @@ const features = [{
2828
ui.openPanel(StudioFeature.Media)
2929
},
3030
}]
31+
32+
async function publishFiles() {
33+
const files = await Promise.all([
34+
draftDocuments.generateRawFiles(),
35+
draftMedias.generateRawFiles(),
36+
]).then(([documents, medias]) => ([...documents, ...medias]))
37+
38+
const message = window.prompt('Enter a commit message')
39+
await git.commitFiles(files, message || 'Publish files')
40+
}
3141
</script>
3242

3343
<template>
@@ -46,6 +56,7 @@ const features = [{
4656
color="primary"
4757
variant="solid"
4858
size="sm"
59+
@click="publishFiles"
4960
/>
5061
<!-- <USeparator
5162
orientation="vertical"

src/app/src/components/panel/content/editor/PanelContentEditor.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ const props = defineProps({
1010
type: Object as PropType<DraftItem>,
1111
required: true,
1212
},
13+
readOnly: {
14+
type: Boolean,
15+
required: false,
16+
default: false,
17+
},
1318
})
1419
1520
const { draftDocuments } = useStudio()
@@ -38,6 +43,10 @@ const document = computed<DatabasePageItem>({
3843
return result
3944
},
4045
set(value) {
46+
if (props.readOnly) {
47+
return
48+
}
49+
4150
draftDocuments.update(props.draftItem.id, {
4251
...toRaw(document.value as DatabasePageItem),
4352
...toRaw(value),
@@ -51,6 +60,7 @@ const document = computed<DatabasePageItem>({
5160
<PanelContentEditorCode
5261
v-model="document"
5362
:draft-item="draftItem"
63+
:read-only="readOnly"
5464
/>
5565
</div>
5666
<!-- <MDCEditorAST v-model="document" /> -->

src/app/src/components/panel/content/editor/PanelContentEditorCode.vue

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ const props = defineProps({
1010
type: Object as PropType<DraftItem>,
1111
required: true,
1212
},
13+
readOnly: {
14+
type: Boolean,
15+
required: false,
16+
default: false,
17+
},
1318
})
1419
1520
const document = defineModel<DatabasePageItem>()
@@ -37,8 +42,21 @@ onMounted(async () => {
3742
const monaco = await setupMonaco()
3843
3944
// create a Monaco editor instance
40-
editor.value = monaco.createEditor(editorRef.value)
45+
editor.value = monaco.createEditor(editorRef.value, {
46+
readOnly: props.readOnly,
47+
scrollbar: props.readOnly
48+
? {
49+
vertical: 'hidden',
50+
horizontal: 'hidden',
51+
handleMouseWheel: false,
52+
}
53+
: undefined,
54+
})
4155
editor.value.onDidChangeModelContent(() => {
56+
if (props.readOnly) {
57+
return
58+
}
59+
4260
// Do not trigger model updates if the document id has changed
4361
if (currentDocumentId.value !== document.value?.id) {
4462
return

src/app/src/composables/useDraftDocuments.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { createStorage } from 'unstorage'
22
import indexedDbDriver from 'unstorage/drivers/indexedb'
33
import { ref } from 'vue'
4-
import type { DatabaseItem, DraftItem, StudioHost, GithubFile, DatabasePageItem } from '../types'
4+
import type { DatabaseItem, DraftItem, StudioHost, GithubFile, DatabasePageItem, RawFile } from '../types'
55
import { DraftStatus } from '../types/draft'
66
import type { useGit } from './useGit'
77
import { generateContentFromDocument } from '../utils/content'
88
import { getDraftStatus, findDescendantsFromId } from '../utils/draft'
99
import { createSharedComposable } from '@vueuse/core'
1010
import { useHooks } from './useHooks'
11+
import { joinURL } from 'ufo'
1112

1213
const storage = createStorage({
1314
driver: indexedDbDriver({
14-
dbName: 'nuxt-content-studio-document',
15+
dbName: 'content-studio-document',
1516
storeName: 'drafts',
1617
}),
1718
})
@@ -40,7 +41,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
4041
}
4142

4243
const fsPath = host.document.getFileSystemPath(document.id)
43-
const githubFile = await git.fetchFile(fsPath, { cached: true }) as GithubFile
44+
const githubFile = await git.fetchFile(joinURL('content', fsPath), { cached: true }) as GithubFile
4445

4546
const item: DraftItem<DatabaseItem> = {
4647
id: document.id,
@@ -118,7 +119,7 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
118119
}
119120
else {
120121
// TODO: check if gh file has been updated
121-
const githubFile = await git.fetchFile(fsPath, { cached: true }) as GithubFile
122+
const githubFile = await git.fetchFile(joinURL('content', fsPath), { cached: true }) as GithubFile
122123

123124
deleteDraftItem = {
124125
id,
@@ -236,6 +237,21 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
236237
select(draftItem)
237238
}
238239

240+
async function generateRawFiles(): Promise<RawFile[]> {
241+
const files = [] as RawFile[]
242+
for (const draftItem of list.value) {
243+
if (draftItem.status === DraftStatus.Deleted) {
244+
files.push({ path: joinURL('content', draftItem.fsPath), content: null, status: draftItem.status, encoding: 'utf-8' })
245+
continue
246+
}
247+
248+
const content = await generateContentFromDocument(draftItem.modified!)
249+
files.push({ path: joinURL('content', draftItem.fsPath), content: content!, status: draftItem.status, encoding: 'utf-8' })
250+
}
251+
252+
return files
253+
}
254+
239255
return {
240256
get,
241257
create,
@@ -248,5 +264,6 @@ export const useDraftDocuments = createSharedComposable((host: StudioHost, git:
248264
current,
249265
select,
250266
selectById,
267+
generateRawFiles,
251268
}
252269
})

src/app/src/composables/useDraftMedias.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ref } from 'vue'
22
import { createStorage } from 'unstorage'
33
import indexedDbDriver from 'unstorage/drivers/indexedb'
44
import { joinURL, withLeadingSlash } from 'ufo'
5-
import type { DraftItem, StudioHost, GithubFile, MediaItem } from '../types'
5+
import type { DraftItem, StudioHost, GithubFile, MediaItem, RawFile } from '../types'
66
import { DraftStatus } from '../types/draft'
77
import type { useGit } from './useGit'
88
import { getDraftStatus } from '../utils/draft'
@@ -11,7 +11,7 @@ import { useHooks } from './useHooks'
1111

1212
const storage = createStorage({
1313
driver: indexedDbDriver({
14-
dbName: 'nuxt-content-studio-media',
14+
dbName: 'content-studio-media',
1515
storeName: 'drafts',
1616
}),
1717
})
@@ -34,7 +34,7 @@ export const useDraftMedias = createSharedComposable((host: StudioHost, git: Ret
3434
}
3535

3636
const fsPath = host.media.getFileSystemPath(media.id)
37-
const githubFile = await git.fetchFile(fsPath, { cached: true }) as GithubFile
37+
const githubFile = await git.fetchFile(joinURL('public', fsPath), { cached: true }) as GithubFile
3838

3939
const item: DraftItem = {
4040
id: media.id,
@@ -95,7 +95,7 @@ export const useDraftMedias = createSharedComposable((host: StudioHost, git: Ret
9595
}
9696
else {
9797
// Fetch github file before creating draft to detect non deployed changes
98-
const githubFile = await git.fetchFile(fsPath, { cached: true }) as GithubFile
98+
const githubFile = await git.fetchFile(joinURL('public', fsPath), { cached: true }) as GithubFile
9999
const original = await host.media.get(id)
100100

101101
const deleteItem: DraftItem = {
@@ -258,6 +258,21 @@ export const useDraftMedias = createSharedComposable((host: StudioHost, git: Ret
258258
})
259259
}
260260

261+
async function generateRawFiles(): Promise<RawFile[]> {
262+
const files = [] as RawFile[]
263+
for (const draftItem of list.value) {
264+
if (draftItem.status === DraftStatus.Deleted) {
265+
files.push({ path: joinURL('public', draftItem.fsPath), content: null, status: draftItem.status, encoding: 'base64' })
266+
continue
267+
}
268+
269+
const content = (await draftItem.modified?.raw as string).replace(/^data:\w+\/\w+;base64,/, '')
270+
files.push({ path: joinURL('public', draftItem.fsPath), content, status: draftItem.status, encoding: 'base64' })
271+
}
272+
273+
return files
274+
}
275+
261276
return {
262277
get,
263278
create,
@@ -271,5 +286,6 @@ export const useDraftMedias = createSharedComposable((host: StudioHost, git: Ret
271286
select,
272287
selectById,
273288
upload,
289+
generateRawFiles,
274290
}
275291
})

0 commit comments

Comments
 (0)