@plutocms/core 0.47.1 → 0.48.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/CHANGELOG.md +11 -0
- package/FEATURES.md +15 -4
- package/app/components/Pluto/Admin/PlutoAdminBackendBadge.vue +84 -0
- package/app/components/Pluto/Admin/PlutoAdminNavbar.vue +7 -0
- package/app/components/Pluto/Github/PlutoGithubBackendDetails.vue +180 -0
- package/app/components/Pluto/Github/PlutoGithubBuildStatus.vue +17 -4
- package/app/components/Pluto/Github/PlutoGithubDeployPage.vue +472 -0
- package/app/components/Pluto/Github/PlutoGithubUsersPage.vue +214 -0
- package/app/components/Pluto/PlutoHtml.vue +8 -2
- package/app/composables/internal/async-component.ts +9 -0
- package/app/composables/pluto-extension.ts +4 -0
- package/app/composables/pluto-navbar.ts +25 -0
- package/app/composables/pluto-registry.ts +1 -0
- package/i18n/locales/en.json +164 -2
- package/i18n/locales/pt-BR.json +164 -2
- package/modules/pluto-github/index.ts +127 -51
- package/modules/pluto-github/options.ts +18 -5
- package/modules/pluto-github/runtime/app/composables/pluto-github-local-git.ts +50 -0
- package/modules/pluto-github/runtime/app/composables/pluto-github-session.ts +8 -1
- package/modules/pluto-github/runtime/app/plugins/pluto-github.ts +84 -1
- package/modules/pluto-github/runtime/server/adapter.ts +26 -7
- package/modules/pluto-github/runtime/server/api/deploy/pages.post.ts +5 -0
- package/modules/pluto-github/runtime/server/api/deploy/status.get.ts +5 -0
- package/modules/pluto-github/runtime/server/api/local/push.post.ts +5 -0
- package/modules/pluto-github/runtime/server/api/local/status.get.ts +5 -0
- package/modules/pluto-github/runtime/server/api/users.get.ts +5 -0
- package/modules/pluto-github/runtime/server/store/github.ts +4 -1
- package/modules/pluto-github/runtime/server/store/local-git.ts +145 -0
- package/modules/pluto-github/runtime/server/store/memory.ts +4 -1
- package/modules/pluto-github/runtime/server/utils/build-status.ts +16 -7
- package/modules/pluto-github/runtime/server/utils/deploy.ts +204 -0
- package/modules/pluto-github/runtime/server/utils/github-setup.ts +1 -3
- package/modules/pluto-github/runtime/server/utils/github-users.ts +120 -0
- package/modules/pluto-github/runtime/server/utils/local-git-routes.ts +95 -0
- package/modules/pluto-github/runtime/shared/build-status.ts +26 -0
- package/modules/pluto-github/runtime/shared/client-rules.ts +16 -0
- package/modules/pluto-github/runtime/shared/deploy.ts +43 -0
- package/modules/pluto-github/runtime/shared/pages-workflow.ts +138 -0
- package/modules/pluto-github/runtime/shared/repo-users.ts +97 -0
- package/package.json +1 -1
- package/shared/types/github.d.ts +2 -0
- package/shared/types/registry.ts +22 -0
- package/shared/utils/html-base.ts +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [0.48.0](https://github.com/plutocms/pluto/compare/v0.47.1...v0.48.0) (2026-11-02)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* a Deploy tab in Settings puts the Git CMS site on Vercel, Netlify, a Node host, Cloudflare or GitHub Pages ([#373](https://github.com/plutocms/pluto/issues/373)) ([f8a7a27](https://github.com/plutocms/pluto/commit/f8a7a276e3019f0dc5aff933cb4e703ad85c343f))
|
|
10
|
+
* a draft save of the Git CMS skips the deploy, and the build status follows the last deploy ([#371](https://github.com/plutocms/pluto/issues/371)) ([800b2a5](https://github.com/plutocms/pluto/commit/800b2a557ad7b7678133a5f04fe000c86aaa9e19))
|
|
11
|
+
* a read-only Users page for the Git CMS lists the people with write access to the repo ([#369](https://github.com/plutocms/pluto/issues/369)) ([0412158](https://github.com/plutocms/pluto/commit/0412158ff698074ccff505c3e0630bc4721e0cf4))
|
|
12
|
+
* the admin navbar shows the backend (Git or a database), and local mode counts the commits to push ([#368](https://github.com/plutocms/pluto/issues/368)) ([b71c4b5](https://github.com/plutocms/pluto/commit/b71c4b5cbbc728ddf6214fa35cbbe8190f64e164))
|
|
13
|
+
* the Git CMS builds a static site (nuxt generate, GitHub Pages) from the files of the checkout ([#372](https://github.com/plutocms/pluto/issues/372)) ([f5de17e](https://github.com/plutocms/pluto/commit/f5de17e85a5ec6e54d15eb40e8d12719caf94ee1))
|
|
14
|
+
|
|
4
15
|
## [0.47.1](https://github.com/plutocms/pluto/compare/v0.47.0...v0.47.1) (2026-11-02)
|
|
5
16
|
|
|
6
17
|
|
package/FEATURES.md
CHANGED
|
@@ -97,18 +97,29 @@ policies shared by every project here.
|
|
|
97
97
|
(`excerpt` in a list, `html` for one item, through `adapter.toPublic`), and `PlutoMarkdown` (an alias of the core `PlutoHtml`, which shows the sanitized `html` of any backend). The GitHub App is created from the site (App Manifest flow): the setup routes
|
|
98
98
|
under `/api/_pluto/github/setup/` (`status`, `github/start`, `github/callback`), a result page with the
|
|
99
99
|
env block, a `.env` write in development, a setup check on `/admin/login`, a **GitHub** tab in Settings,
|
|
100
|
-
and a private key in PEM, `\n` or base64 form.
|
|
101
|
-
|
|
100
|
+
and a private key in PEM, `\n` or base64 form. With `commit.skipDeploy` off, a draft save gets
|
|
101
|
+
`[skip ci]`, so only a change of published content deploys. The edit form shows the build status of the
|
|
102
|
+
newest commit that did not skip CI (check runs, commit statuses, deployments) next to Save, for GitHub storage with
|
|
102
103
|
`commit.skipDeploy` off: the route `GET /api/_pluto/github/build-status`, a pure reducer, a polling loop,
|
|
103
104
|
and an Info popover with links to the Action or the build. A content type with `liveOnSave` shows
|
|
104
105
|
"Live" at once after a save and the static build as a small spinner. The file adapter has
|
|
105
106
|
`readFile` (safe relative paths) and `_frontmatter` on `get`, and core has the auto-imported
|
|
106
|
-
`markPublicRead(event)` for layers.
|
|
107
|
+
`markPublicRead(event)` for layers. The backend badge shows local mode or the GitHub repo. In local
|
|
108
|
+
mode it counts the commits that are not pushed and has a Push button (`/api/_pluto/github/local/status`
|
|
109
|
+
and `push`, dev local request only). A read-only **Users** page (`/admin/users`) lists the people
|
|
110
|
+
with write access to the repo (`GET /api/_pluto/github/users`), their GitHub role, what it gives in
|
|
111
|
+
Pluto, and who `ALLOWED_USERS` blocks. A static build (`nuxt generate`, or the `github_pages`
|
|
112
|
+
preset) prerenders the site from the files of the checkout with no token, adds no server route,
|
|
113
|
+
keeps the admin out, and takes a sub path from `PLUTO_BASE_URL` (`PlutoHtml` puts it before the
|
|
114
|
+
site paths of a post body). A **Deploy** tab in Settings (`/admin/settings/deploy`) puts the site
|
|
115
|
+
on a host: import links for Vercel and Netlify, a Node host guide, Cloudflare Pages build settings,
|
|
116
|
+
and GitHub Pages (in local mode it writes and commits the workflow and turns Pages on with the
|
|
117
|
+
GitHub CLI). @.claude/skills/git-cms/SKILL.md
|
|
107
118
|
- Lazy admin components: core registers its field widgets and dashboard widgets with
|
|
108
119
|
`defineAsyncComponent`, shows a skeleton per field, widget and settings panel while a chunk
|
|
109
120
|
loads, and warns in dev about a registered component that is not async.
|
|
110
121
|
@.claude/skills/extension-registry/SKILL.md
|
|
111
|
-
- Admin shell: core owns the `/admin` parent route, the `/admin/login` page, the route guard, the admin banners, the navbar and the user menu. A backend layer registers an auth driver, account menu items, site info and
|
|
122
|
+
- Admin shell: core owns the `/admin` parent route, the `/admin/login` page, the route guard, the admin banners, the navbar and the user menu. A backend layer registers an auth driver, account menu items, site info, link actions and the backend badge of the navbar ("Git · Local", "Git · GitHub", "Database · Supabase", with a popover; `usePlutoBackend`). The admin is for staff only: a signed-in user with no capability goes to `/admin/no-access`. In production, an app with no auth driver (the backend is missing or did not load) sends every admin path to `/admin/not-configured`; in dev the admin opens. The bar shows on `/admin/**` for staff, and on public pages for staff only. A route hides the bar with the page meta `plutoAdminBar: false`, and `isPlutoPreviewPath` marks the preview path. The bar height is the token `--pluto-admin-bar-height`, and `<html>` gets `data-pluto-admin-bar` while the bar shows. A hint cookie puts the bar frame in the server HTML of a public page for staff (the actions and the user menu load after the mount). Content types with `adminBar` get "New <type>" and "Edit <type>" buttons (`usePlutoEditLink`). With two or more types, one "New" menu lists them, grouped by `navGroup`. A `navGroup` also puts related types under one sidebar item (registry bucket `navGroups`, core group `pages`). @.claude/skills/admin-shell/SKILL.md
|
|
112
123
|
- Local layers helper: `plutoLayer(name)` from `@plutocms/core/layers` gives a Nuxt `extends` entry.
|
|
113
124
|
With `PLUTO_LOCAL_ROOT`, it uses the local checkouts of the Pluto layers. @.claude/skills/local-layers/SKILL.md
|
|
114
125
|
- Pluto logo: `PlutoLogo` draws the logo inline. The wordmark takes the text color, so the logo works
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The backend badge of the admin navbar: "Git · GitHub", "Git · Local",
|
|
3
|
+
// "Database · Supabase". It tells the editor where the content goes. A click
|
|
4
|
+
// opens a popover with the details of the backend (its own component). A
|
|
5
|
+
// count (for example the commits that are not pushed) shows as a small pill.
|
|
6
|
+
// The backend comes from the `backend` field of `definePlutoExtension`. See
|
|
7
|
+
// the admin-shell skill.
|
|
8
|
+
const { t } = usePlutoI18n()
|
|
9
|
+
const { backend } = usePlutoBackend()
|
|
10
|
+
const open = ref(false)
|
|
11
|
+
|
|
12
|
+
const kindLabel = computed(() =>
|
|
13
|
+
backend.value?.kind === 'database'
|
|
14
|
+
? t('pluto.backend.kind.database')
|
|
15
|
+
: t('pluto.backend.kind.git')
|
|
16
|
+
)
|
|
17
|
+
const kindIcon = computed(() =>
|
|
18
|
+
backend.value?.kind === 'database'
|
|
19
|
+
? 'i-lucide-database'
|
|
20
|
+
: 'i-lucide-git-branch'
|
|
21
|
+
)
|
|
22
|
+
const buttonLabel = computed(() => {
|
|
23
|
+
const name = `${kindLabel.value} · ${backend.value?.label ?? ''}`
|
|
24
|
+
return backend.value?.countLabel
|
|
25
|
+
? `${t('pluto.backend.open', { name })} (${backend.value.countLabel})`
|
|
26
|
+
: t('pluto.backend.open', { name })
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
watch(open, (value) => {
|
|
30
|
+
if (value) {
|
|
31
|
+
backend.value?.onOpen?.()
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<UPopover
|
|
38
|
+
v-if="backend"
|
|
39
|
+
v-model:open="open"
|
|
40
|
+
:content="{ align: 'start', sideOffset: 4 }"
|
|
41
|
+
>
|
|
42
|
+
<PlutoNavbarHit :label="buttonLabel" size="sm">
|
|
43
|
+
<span class="flex items-center gap-x-1.5">
|
|
44
|
+
<UIcon :name="kindIcon" class="size-4 shrink-0" />
|
|
45
|
+
<span class="hidden md:inline">{{ kindLabel }}</span>
|
|
46
|
+
<span class="text-muted hidden md:inline">·</span>
|
|
47
|
+
<span class="text-muted hidden max-w-32 truncate md:inline">
|
|
48
|
+
{{ backend.label }}
|
|
49
|
+
</span>
|
|
50
|
+
<span
|
|
51
|
+
v-if="backend.count"
|
|
52
|
+
:title="backend.countLabel"
|
|
53
|
+
class="bg-warning text-inverted rounded-full px-1.5 text-[0.6875rem]/4 font-semibold tabular-nums"
|
|
54
|
+
>
|
|
55
|
+
{{ backend.count }}
|
|
56
|
+
</span>
|
|
57
|
+
</span>
|
|
58
|
+
</PlutoNavbarHit>
|
|
59
|
+
|
|
60
|
+
<template #content>
|
|
61
|
+
<div class="w-80 max-w-[calc(100vw-2rem)] p-4 text-sm">
|
|
62
|
+
<div class="mb-3 flex items-center gap-x-2">
|
|
63
|
+
<UIcon
|
|
64
|
+
:name="backend.icon || kindIcon"
|
|
65
|
+
class="text-highlighted size-5 shrink-0"
|
|
66
|
+
/>
|
|
67
|
+
<div class="min-w-0">
|
|
68
|
+
<p class="text-highlighted truncate font-semibold">
|
|
69
|
+
{{ backend.label }}
|
|
70
|
+
</p>
|
|
71
|
+
<p class="text-muted text-xs">
|
|
72
|
+
{{
|
|
73
|
+
backend.kind === 'database'
|
|
74
|
+
? t('pluto.backend.databaseMode')
|
|
75
|
+
: t('pluto.backend.gitMode')
|
|
76
|
+
}}
|
|
77
|
+
</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<component :is="backend.details" v-if="backend.details" />
|
|
81
|
+
</div>
|
|
82
|
+
</template>
|
|
83
|
+
</UPopover>
|
|
84
|
+
</template>
|
|
@@ -85,6 +85,13 @@ const siteDomain = computed(() => domainFromUrl(site.url.value) || host)
|
|
|
85
85
|
-->
|
|
86
86
|
<div class="-mx-1 h-full min-w-0 overflow-hidden px-1">
|
|
87
87
|
<ul class="flex h-full min-w-0 items-center gap-x-1 text-sm">
|
|
88
|
+
<!-- Where the content goes: Git or a database. Admin pages only. -->
|
|
89
|
+
<ClientOnly>
|
|
90
|
+
<li v-if="isAdmin" class="h-full shrink-0">
|
|
91
|
+
<PlutoAdminBackendBadge />
|
|
92
|
+
</li>
|
|
93
|
+
</ClientOnly>
|
|
94
|
+
|
|
88
95
|
<PlutoNavbarButton
|
|
89
96
|
v-if="isAdmin"
|
|
90
97
|
:label="t('pluto.navbar.viewSite')"
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The popover body of the backend badge (`backend.details`) for the Git CMS.
|
|
3
|
+
// Local mode (`fs` store): the saves are commits in the local repo, the
|
|
4
|
+
// commits that are not pushed, and a Push button. GitHub mode: the repo and
|
|
5
|
+
// the branch that each save commits to, with links to GitHub.
|
|
6
|
+
import { usePlutoGithubLocalGit } from '../../../../modules/pluto-github/runtime/app/composables/pluto-github-local-git'
|
|
7
|
+
import { usePlutoGithubSession } from '../../../../modules/pluto-github/runtime/app/composables/pluto-github-session'
|
|
8
|
+
|
|
9
|
+
const { t } = usePlutoI18n()
|
|
10
|
+
const errorText = usePlutoErrorText()
|
|
11
|
+
const config = useRuntimeConfig().public.plutoGithub as
|
|
12
|
+
| {
|
|
13
|
+
storage?: string
|
|
14
|
+
repo?: string
|
|
15
|
+
branch?: string
|
|
16
|
+
}
|
|
17
|
+
| undefined
|
|
18
|
+
const local = config?.storage === 'fs'
|
|
19
|
+
const repo = config?.repo || ''
|
|
20
|
+
const branch = config?.branch || 'main'
|
|
21
|
+
const session = usePlutoGithubSession()
|
|
22
|
+
const git = usePlutoGithubLocalGit()
|
|
23
|
+
const pushError = ref('')
|
|
24
|
+
const pushed = ref(false)
|
|
25
|
+
|
|
26
|
+
const repoUrl = computed(() => (repo ? `https://github.com/${repo}` : ''))
|
|
27
|
+
const commitsUrl = computed(() =>
|
|
28
|
+
repo ? `https://github.com/${repo}/commits/${encodeURIComponent(branch)}` : ''
|
|
29
|
+
)
|
|
30
|
+
const visibility = computed(() => {
|
|
31
|
+
const value = session.repo.value?.private
|
|
32
|
+
if (value === undefined) {
|
|
33
|
+
return ''
|
|
34
|
+
}
|
|
35
|
+
return value
|
|
36
|
+
? t('pluto.github.backend.private')
|
|
37
|
+
: t('pluto.github.backend.public')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// What the local repo needs, in one line. `null` while the first read runs.
|
|
41
|
+
const localState = computed(() => {
|
|
42
|
+
const status = git.status.value
|
|
43
|
+
if (!status) {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
if (!status.repo) {
|
|
47
|
+
return status.reason === 'no-git'
|
|
48
|
+
? t('pluto.github.local.noGit')
|
|
49
|
+
: t('pluto.github.local.notARepo')
|
|
50
|
+
}
|
|
51
|
+
if (!status.branch) {
|
|
52
|
+
return t('pluto.github.local.detached')
|
|
53
|
+
}
|
|
54
|
+
if (!status.remote) {
|
|
55
|
+
return t('pluto.github.local.noRemote')
|
|
56
|
+
}
|
|
57
|
+
return status.ahead
|
|
58
|
+
? t('pluto.github.local.ahead', status.ahead)
|
|
59
|
+
: t('pluto.github.local.upToDate')
|
|
60
|
+
})
|
|
61
|
+
const canPush = computed(() => {
|
|
62
|
+
const status = git.status.value
|
|
63
|
+
return Boolean(
|
|
64
|
+
status?.repo && status.branch && status.remote && status.ahead > 0
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
async function push() {
|
|
69
|
+
pushError.value = ''
|
|
70
|
+
pushed.value = false
|
|
71
|
+
try {
|
|
72
|
+
await git.push()
|
|
73
|
+
pushed.value = true
|
|
74
|
+
} catch (error) {
|
|
75
|
+
pushError.value = errorText(error, 'pluto.github.local.errors.failed')
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<template>
|
|
81
|
+
<div class="space-y-3">
|
|
82
|
+
<template v-if="local">
|
|
83
|
+
<p class="text-muted">{{ t('pluto.github.local.description') }}</p>
|
|
84
|
+
|
|
85
|
+
<dl class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1">
|
|
86
|
+
<template v-if="git.status.value?.repo && git.status.value.branch">
|
|
87
|
+
<dt class="text-muted">{{ t('pluto.github.backend.branch') }}</dt>
|
|
88
|
+
<dd class="text-highlighted truncate font-mono text-xs/5">
|
|
89
|
+
{{ git.status.value.branch }}
|
|
90
|
+
</dd>
|
|
91
|
+
</template>
|
|
92
|
+
<template v-if="repo">
|
|
93
|
+
<dt class="text-muted">{{ t('pluto.github.backend.repo') }}</dt>
|
|
94
|
+
<dd class="truncate">
|
|
95
|
+
<ULink :to="repoUrl" target="_blank" class="font-mono text-xs/5">
|
|
96
|
+
{{ repo }}
|
|
97
|
+
</ULink>
|
|
98
|
+
</dd>
|
|
99
|
+
</template>
|
|
100
|
+
</dl>
|
|
101
|
+
|
|
102
|
+
<div
|
|
103
|
+
class="border-default flex items-center justify-between gap-3 rounded-md border px-3 py-2"
|
|
104
|
+
>
|
|
105
|
+
<p v-if="localState" class="text-highlighted min-w-0">
|
|
106
|
+
{{ localState }}
|
|
107
|
+
</p>
|
|
108
|
+
<USkeleton v-else class="h-4 w-32" />
|
|
109
|
+
<UButton
|
|
110
|
+
v-if="canPush"
|
|
111
|
+
:label="t('pluto.github.local.push')"
|
|
112
|
+
:loading="git.pushing.value"
|
|
113
|
+
icon="i-lucide-upload"
|
|
114
|
+
size="xs"
|
|
115
|
+
@click="push"
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<p
|
|
120
|
+
v-if="pushed && !canPush"
|
|
121
|
+
class="text-success flex items-center gap-1.5"
|
|
122
|
+
role="status"
|
|
123
|
+
>
|
|
124
|
+
<UIcon name="i-lucide-check" class="size-4" />
|
|
125
|
+
{{ t('pluto.github.local.pushed') }}
|
|
126
|
+
</p>
|
|
127
|
+
<p v-if="pushError" class="text-error" role="alert">
|
|
128
|
+
{{ pushError }}
|
|
129
|
+
</p>
|
|
130
|
+
<p v-else-if="canPush" class="text-muted text-xs">
|
|
131
|
+
{{ t('pluto.github.local.pushHint') }}
|
|
132
|
+
</p>
|
|
133
|
+
</template>
|
|
134
|
+
|
|
135
|
+
<template v-else>
|
|
136
|
+
<p class="text-muted">
|
|
137
|
+
{{ t('pluto.github.backend.description') }}
|
|
138
|
+
</p>
|
|
139
|
+
<dl class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1">
|
|
140
|
+
<dt class="text-muted">{{ t('pluto.github.backend.repo') }}</dt>
|
|
141
|
+
<dd class="flex min-w-0 items-center gap-2">
|
|
142
|
+
<span class="text-highlighted truncate font-mono text-xs/5">
|
|
143
|
+
{{ repo }}
|
|
144
|
+
</span>
|
|
145
|
+
<UBadge
|
|
146
|
+
v-if="visibility"
|
|
147
|
+
:label="visibility"
|
|
148
|
+
color="neutral"
|
|
149
|
+
size="sm"
|
|
150
|
+
variant="subtle"
|
|
151
|
+
/>
|
|
152
|
+
</dd>
|
|
153
|
+
<dt class="text-muted">{{ t('pluto.github.backend.branch') }}</dt>
|
|
154
|
+
<dd class="text-highlighted truncate font-mono text-xs/5">
|
|
155
|
+
{{ branch }}
|
|
156
|
+
</dd>
|
|
157
|
+
</dl>
|
|
158
|
+
<div v-if="repoUrl" class="flex flex-wrap gap-2">
|
|
159
|
+
<UButton
|
|
160
|
+
:label="t('pluto.github.backend.openRepo')"
|
|
161
|
+
:to="repoUrl"
|
|
162
|
+
color="neutral"
|
|
163
|
+
icon="i-pluto-github"
|
|
164
|
+
size="xs"
|
|
165
|
+
target="_blank"
|
|
166
|
+
variant="outline"
|
|
167
|
+
/>
|
|
168
|
+
<UButton
|
|
169
|
+
:label="t('pluto.github.backend.commits')"
|
|
170
|
+
:to="commitsUrl"
|
|
171
|
+
color="neutral"
|
|
172
|
+
icon="i-lucide-history"
|
|
173
|
+
size="xs"
|
|
174
|
+
target="_blank"
|
|
175
|
+
variant="outline"
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</template>
|
|
179
|
+
</div>
|
|
180
|
+
</template>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
// A toolbar item of the edit form (`contentToolbarItems`). It shows the build
|
|
3
|
-
// of the
|
|
3
|
+
// of the newest commit of the content branch that did not skip CI (a draft
|
|
4
|
+
// save skips it), so a draft that was never public in this page shows nothing:
|
|
5
|
+
// a spinner while the site builds,
|
|
4
6
|
// a check mark when it is live, and an error icon when the build failed. The
|
|
5
7
|
// Info button opens the details, with a link to each Action or build.
|
|
6
8
|
// The CMS plugin registers it only for GitHub storage with `skipDeploy` off.
|
|
@@ -12,22 +14,33 @@ import type {
|
|
|
12
14
|
PlutoBuildState,
|
|
13
15
|
} from '../../../../modules/pluto-github/runtime/shared/build-status'
|
|
14
16
|
import { usePlutoGithubBuildStatus } from '../../../../modules/pluto-github/runtime/app/composables/pluto-github-build-status'
|
|
17
|
+
import { isPublicItem } from '../../../../modules/pluto-github/runtime/shared/client-rules'
|
|
15
18
|
import { savedAgo } from '../../../composables/internal/content-dirty'
|
|
16
19
|
|
|
17
20
|
const { t, locale } = usePlutoI18n()
|
|
18
21
|
const form = usePlutoContentForm()
|
|
19
22
|
const { status, check, afterSave } = usePlutoGithubBuildStatus()
|
|
20
23
|
|
|
24
|
+
// True when the item was public in this page: published when the page opened, or after a save.
|
|
25
|
+
// A draft save skips the deploy, so a draft that was never public has no build to show.
|
|
26
|
+
const everPublic = ref(
|
|
27
|
+
form ? isPublicItem(form.type.value, form.item.value) : true
|
|
28
|
+
)
|
|
29
|
+
|
|
21
30
|
// Read when the edit page opens, and after each save. A new item has no commit yet:
|
|
22
31
|
// the form goes to the edit page after the first save, and this item mounts again there.
|
|
23
32
|
onMounted(() => {
|
|
24
|
-
if (form?.editing.value) {
|
|
33
|
+
if (form?.editing.value && everPublic.value) {
|
|
25
34
|
check()
|
|
26
35
|
}
|
|
27
36
|
})
|
|
28
37
|
// True after a save in this page. With `liveOnSave` the change is live from then on.
|
|
29
38
|
const savedHere = ref(false)
|
|
30
|
-
form?.onSaved(() => {
|
|
39
|
+
form?.onSaved((saved) => {
|
|
40
|
+
everPublic.value ||= isPublicItem(form.type.value, saved)
|
|
41
|
+
if (!everPublic.value) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
31
44
|
savedHere.value = true
|
|
32
45
|
afterSave()
|
|
33
46
|
})
|
|
@@ -130,7 +143,7 @@ const commitTime = computed(() => ago(status.value?.committedAt))
|
|
|
130
143
|
</script>
|
|
131
144
|
|
|
132
145
|
<template>
|
|
133
|
-
<div v-if="summary" class="flex items-center gap-1">
|
|
146
|
+
<div v-if="summary && everPublic" class="flex items-center gap-1">
|
|
134
147
|
<!-- A live region: a screen reader hears the change. -->
|
|
135
148
|
<div
|
|
136
149
|
role="status"
|