Skip to content

Commit 740f90b

Browse files
tdgaocoolbot100schyzmanDragon-Seeker
authored
feat: modview (#7623)
* fix: wrong close panel arrow * fix: muted active tags * feat: add issues list panel, issue picker, and issue cards * fix: qa around links and license * feat: add favicon for project review page * feat: go to next in queue after submitting review outcome * feat: make panel.title optional and add page title * feat: bring back guidance Url * fix: dock layout is reset for conditional tabs * feat: add orgs to members section, add permissions number in tab, fix focus state when press keybind * feat: add server project details section and polish permissions * remove: gallery image specific review panels * feat: editable fields for title, slug, summary, tags, links, license, description, and versions * feat: style tabs as tabs * feat: add server details review panel * feat: add review options to server details panel * feat: make contact input optional. * fix: ai disclosure section positioning * feat: allow any all or none inputs on reupload/reupload * fix: various disclosure options and empty list state * fix: more consistent disclosure toggle labeling * fix: mark optional fields as optional * allow moderating already approved projects * fix: prepr * feat: more context for review panels * fix: non english disclosure only show when theres disclosures * feat: show misused disclosure only when disclosure enabled * feat: add issue priority * feat: add Alerts priority for temp server issue * feat: add pixel art rendering option for image viewer * fix: panel in a closed tab stays showing briefly * feat: add red star for required field labels * fix: versions review panel was not hidden when shouldShow is false * fix: cant scroll page when leaving project review * feat: add page loading bar when going to next project * feat: persist selected issues in local storage * feat: sort deps by type then name and accodion on deps * feat: add open in slicer button * feat: permissions QA - unreviewed are open by default - add sha-1 and db id on files - add image viewer - resizable text area - original project dont collapse accordion - selecting custom license focuses on license link right away * feat: open derivative content link * feat: add resize vertical for disclosure text areas and fix toggle spacing * fix: dont show license for serv proj * feat: show thread first in tabs, add issue count * feat: field hover bg and modview review panel styling * refactor: put panel findings count badge slotted in with section * fix: button sizing * fix: popover covered content and remove dead code * feat: fancier icons on stuff * feat: bold headings * feat: show author for server modpack content and show org stats * feat: add project status icon to title * feat: rearrange queue buttons * feat: add shared panel activation context and pressing hotkey to open tab sets first inline panel as active * feat: show issue message is edited and improve issue picker * fix: clicking button in inline panel makes panel active state stick * fix: contrast is too high on toggle buttons in off state * fix: anchored panel still sticky after toggling in it * small change * remove: history tab * revert: conversation thread to match main * feat: hooking up moderation issues backend * fix: license merge * pnpm prepr * small * feat: use review panel title for project review disclosure cards * feat: add failed tech review badge * fix: show issue list in order of defined priority * fix(messaging): issues above instead of below * feat: remove corrections applied and use suggestions/remove issue facets * fix: types * refactor: rename to message-box * feat: add resizable message box * feat: add message library * feat: allow facets to use context * feat: all issues are sent as issues, and with facet acknowledge checkmark * feat: add specific gallery image issues, update messaging where appropriate, and properly show/hide auto resolved issues on creator side * organize metadata and versions issues * feat: add bulk apply environment suggestion * fix: handle re-rev on links that have issues but were moved * feat: add location option on issues * feat: improve version card * feat: browser history forward back works for queue * pnpm prepr * feat: license qa * feat: show member and org status in project page for staff * feat: add option to disable auto go next on submit review * small * feat: add persisted show pixel art images, and thread image viewer * fix: thread message not sent when approve project * feat: add thread image viewing to moderation thread too * feat: add monetization and visibility to editing options * feat: show org members better * feat: update frontend to for user_addressed/moderator_verified on facet * pnpm prepr * feat: add mark addressed facet * polish: styles * feat: custom issue maker * feat: revert all customizations on issues * feat: keybind fixes * pnpm prepr * feat: customizable misc panels * refactor: rename facet to actions in moderation issues * fix: misc panel caused error * feat: show summary suggestion for formatting message * fix: custom issue persistance on rerev, adding as not resolved * fix: not resolved button * feat: add open in modview and fix re-rev not resolved issues did not re-generate facets data properly * feat: add flagged issues to moderation thread * small fix * pnpm prepr * polish * feat: add staff only mark as resolved/unresolved on issues, add issues to project button, and better auto approval message * fix lock file * feat: add user options overflow menu * feat: add back corrections applied message * feat: change auto approval copy * pnpm prepr * fix lockfile * fix: only show requesting status row when status is processing * fix: dont go next when click overflow buttons in review decision buttons * feat: mark as addressed is no longer disabled when theres a draf tvalue and saves the field * feat: hold shift click to copy modrinth.com perma link * feat: i18n thread messages, fix spacing, and update reviewed project message * feat: make moderation buttons orange * fix: table typing and links page project issue card * feat: allow all links to be saved with mark as addressed if all are edited in draft * fix: submitted review issue count * feat: add RemoveIcon action and AddDisclosures action * fix: storybook and lint * fix: remove tags dropdown is required * feat: make issue control ids global * fix: ensure all issue control ids prefixed with panel name * Move title similarities / modpack * feat: add <copy-code> and <image-viewer> blocks * feat: Summary issues allow optional suggestion * pnpm prepr * fix: usued import * fix: import error * remove: previous moderation checklist * feat: add project review moderation lock * feat: format time tooltip * feat: add keybind to toggle project icon viewer * feat: add back ai version scan * feat: style project issue card better * fix: rebuild issue for re-rev * fix: issue card doesnt auto close if previously expanded from addressed * feat: Add Slicer tab to moderation project view Uses Iframe and bridge script to communicate with slicer to manage current workspace. * feat: Add support for external file slicer open urls within slicer tab * feat: implement pop out panels with drag and drop * feat: add issues storybook and modpack tool buttons * feat: hot reloading on issues messages * fix: add missing gallery original id and auto resolve fields * fix: dont show both location buttons and mark as addressed * Various layout and message tweaks * feat: title layout tweaks + support optional suggestions * fix: clean up summary file * fix: handle mark as address for multiple project areas and staff mark as addressed not disabled * setup for link suggestions * fix: remove unnecessary frontend validation and use backend error for patch thread failure * small: restore addressing only facets in card instead of all facets * fix: properly disable mark as addressed for disclosures, and change disclosure to be only one card on top * fix: server language and addresses mark as addressed to check draft values and can save field * feat: add links suggestions * format * fix: resubmit for review color * fix: teleport target could fail if you open combobox in modal * feat: show license requires source code in link * feat: add slug indication if its different * feat: r1 and r3.3 tweaks + allow optional * feat: smarter context for various version buttons * feat: project issue cards to be more like accodions and auto collapse if theres 5 or more issues * pnpm prepr * feat: icon issues * feat: 1.5 / piracy msg * fix: once above now below * pnpm prepr * pnpm prepr 2 * remove: bottom tools panel * fix: slicer messed up dockview tabs * fix: version caching * fix: slicer loading and move file slicer buttons into file overflow * feat: allow open in slicer again which cancels current loading * fix: slicer popout * fix: revert drag tab out to use drag events instead of pointer events * feat: allow custom components in message thread * feat: add setting for sending issues as thread message * feat: always show misused slug button. * fix: gallery review panels dont show because ids missing * fix: add temp server rule as issue. * fix: license review panel layout * fix: slicer loading bug and how popout panels get restored * feat: show image metadata * feat: adjust bottom bar button sizes * polish: version card * feat: add warning icon for temp host * feat: add edit server details modal * fix: info button not show with hide title * feat: re-order member statuses * feat: add ability to edit/remove icon * fix: clicking items in multi-select closes when its in a floating panel * feat: update user actions * pnpm prepr * feat: show completed, skipped, and remaining * feat: add additional link info on link hover * feat: pre fetch next project in queue once current is finished * polish: styles * fix: edge case when popout tab disappears and reappears conditionally for project type * feat: show link tooltip above and add setting for it * feat: improve keybinds and settings organization and styles * pnpm prepr * feat: display deployment commit/branch in settings modal * pnpm prepr * fix: add re-rev panel in misc section if was reviewed but no previous structured issues * pnpm prepr * fix: nonEnglish disclosure toggle not showing when it should * feat: smarter nonEnglishDisclosure check * fix: shortcuts and add keep popout closed button * pnpm prepr * polish: version card for lots of dependencies * feat: remove issue id and add issue title * feat: add % placeholders to message library * feat: add max width option on review panels * fix: undefined * pnpm prepr --------- Co-authored-by: coolbot100s <76798835+coolbot100s@users.noreply.github.com> Co-authored-by: chyzman <chyzalt@gmail.com> Co-authored-by: Blodhgarm <blodhgrm@gmail.com>
1 parent 990337a commit 740f90b

430 files changed

Lines changed: 34343 additions & 11126 deletions

File tree

Some content is hidden

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

‎apps/frontend/AGENTS.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ Nuxt 3 with SSR — pages are server-rendered and hydrated on the client. Uses `
2828

2929
Rule of thumb: if it doesn't depend on Nuxt-specific APIs or website-only features, it should be in `packages/ui`.
3030

31+
### Project Review Popout Windows
32+
33+
Project review panels can be displayed in multiple browser documents while sharing the main window's Vue application and JavaScript context. Use an anchor, panel, or event target's `ownerDocument` for DOM queries and its `defaultView` for window behavior. Global `document` and `window` still identify the main window after a panel moves.
34+
35+
Use the review shortcut context's listeners (`shortcuts.listen` or `useReviewEventListener`) for review interactions that must work in both the workspace and popouts. Shared overlay and modal behavior follows the multiple-document guidance in `packages/ui/AGENTS.md`.
36+
3137
## Data Fetching
3238

3339
Use `@modrinth/api-client` via `injectModrinthClient()` for all API calls. See `packages/api-client/AGENTS.md` for the full API client documentation.

‎apps/frontend/COPYING.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ This includes, but may not be limited to, the following files:
2121
- src/public/favicon-light-32x32.png
2222
- src/public/favicon-settings-32x32.png
2323
- src/public/favicon-light-settings-32x32.png
24+
- src/public/favicon-review.svg
25+
- src/public/favicon-light-review.svg
2426
- src/public/dev-favicons/\*
2527
- src/public/news/\*
2628

‎apps/frontend/nuxt.config.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import { fileURLToPath } from 'url'
55
import svgLoader from 'vite-svg-loader'
66

77
import { GenericModrinthClient, type Labrinth } from '../../packages/api-client/src/index.ts'
8+
import moderationMessages, {
9+
moderationMessagesDirectory,
10+
} from '../../packages/moderation/vite-plugin'
811

912
const STAGING_API_URL = 'https://staging-api.modrinth.com/v2/'
1013
const STAGING_SHARED_INSTANCES_API_URL = 'https://staging-shared-instances.modrinth.com'
@@ -26,6 +29,7 @@ export default defineNuxtConfig({
2629
srcDir: 'src/',
2730
alias: {
2831
'@modrinth/api-client': API_CLIENT_SOURCE,
32+
'#messages': moderationMessagesDirectory,
2933
},
3034
app: {
3135
buildAssetsDir: '/_assets/',
@@ -82,6 +86,7 @@ export default defineNuxtConfig({
8286
dedupe: ['vue'],
8387
},
8488
plugins: [
89+
moderationMessages(),
8590
svgLoader({
8691
svgoConfig: {
8792
plugins: [

‎apps/frontend/package.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
"wrangler": "^4.115.0"
3939
},
4040
"dependencies": {
41+
"@codemirror/commands": "^6.3.2",
42+
"@codemirror/language": "^6.9.3",
43+
"@codemirror/view": "^6.22.1",
4144
"@contentauth/c2pa-web": "^0.13.1",
4245
"@floating-ui/vue": "^2.0.1",
4346
"@formatjs/intl-localematcher": "^0.5.4",
@@ -59,6 +62,7 @@
5962
"ansi-to-html": "^0.7.2",
6063
"chart.js": "^4.5.1",
6164
"dayjs": "^1.11.7",
65+
"dockview-vue": "^8.2.0",
6266
"dompurify": "^3.1.7",
6367
"fuse.js": "^6.6.2",
6468
"highlight.js": "^11.7.0",

‎apps/frontend/src/assets/styles/layout.scss‎

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,6 @@
111111
max-width: 100%;
112112
}
113113
}
114-
115-
&.align-x {
116-
margin: 0 auto;
117-
}
118-
119-
&.align-l {
120-
margin: 0 0 0 auto;
121-
}
122-
123-
&.align-r {
124-
margin: 0 auto 0 0;
125-
}
126114
}
127115

128116
.normal-page__sidebar {
@@ -152,18 +140,6 @@
152140
'sidebar'
153141
/ 100%;
154142

155-
&.align-x {
156-
margin: 0 auto;
157-
}
158-
159-
&.align-l {
160-
margin: 0 0 0 auto;
161-
}
162-
163-
&.align-r {
164-
margin: 0 auto 0 0;
165-
}
166-
167143
@media screen and (min-width: 1024px) {
168144
&.sidebar {
169145
grid-template:
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<template>
2+
<div class="mt-1 flex flex-wrap gap-x-2 gap-y-1 text-xs">
3+
<Tooltip
4+
v-for="stat in statusStats"
5+
:key="stat.status"
6+
:text="formatMessage(stat.message, { count: stat.count })"
7+
:aria-label="formatMessage(stat.message, { count: stat.count })"
8+
class="flex items-center gap-0.5 font-medium tabular-nums"
9+
:class="stat.color"
10+
>
11+
<component :is="stat.icon" class="size-[12px] shrink-0" aria-hidden="true" />
12+
<span aria-hidden="true">{{ stat.count }}</span>
13+
</Tooltip>
14+
</div>
15+
</template>
16+
17+
<script setup lang="ts">
18+
import { CheckIcon, ShieldAlertIcon } from '@modrinth/assets'
19+
import { defineMessages, PROJECT_STATUS_ICONS, Tooltip, useVIntl } from '@modrinth/ui'
20+
import { computed } from 'vue'
21+
22+
const props = defineProps<{
23+
stats: { status: string; count: number }[]
24+
}>()
25+
const { formatMessage } = useVIntl()
26+
const messages = defineMessages({
27+
approvedCount: {
28+
id: 'moderation.project-review.approvedCount',
29+
defaultMessage: '{count} approved',
30+
},
31+
archivedCount: {
32+
id: 'moderation.project-review.archivedCount',
33+
defaultMessage: '{count} archived',
34+
},
35+
unlistedCount: {
36+
id: 'moderation.project-review.unlistedCount',
37+
defaultMessage: '{count} unlisted',
38+
},
39+
withheldCount: {
40+
id: 'moderation.project-review.withheldCount',
41+
defaultMessage: '{count} withheld',
42+
},
43+
processingCount: {
44+
id: 'moderation.project-review.processingCount',
45+
defaultMessage: '{count} under review',
46+
},
47+
draftCount: {
48+
id: 'moderation.project-review.draftCount',
49+
defaultMessage: '{count} draft',
50+
},
51+
rejectedCount: {
52+
id: 'moderation.project-review.rejectedCount',
53+
defaultMessage: '{count} rejected',
54+
},
55+
techReviewFailedCount: {
56+
id: 'moderation.project-review.techReviewFailedCount',
57+
defaultMessage: '{count} failed technical review',
58+
},
59+
privateCount: {
60+
id: 'moderation.project-review.privateCount',
61+
defaultMessage: '{count} private',
62+
},
63+
scheduledCount: {
64+
id: 'moderation.project-review.scheduledCount',
65+
defaultMessage: '{count} scheduled',
66+
},
67+
unknownCount: {
68+
id: 'moderation.project-review.unknownCount',
69+
defaultMessage: '{count} unknown',
70+
},
71+
})
72+
const statusMessages = {
73+
tech_review_failed: messages.techReviewFailedCount,
74+
approved: messages.approvedCount,
75+
unlisted: messages.unlistedCount,
76+
private: messages.privateCount,
77+
withheld: messages.withheldCount,
78+
rejected: messages.rejectedCount,
79+
draft: messages.draftCount,
80+
processing: messages.processingCount,
81+
archived: messages.archivedCount,
82+
scheduled: messages.scheduledCount,
83+
unknown: messages.unknownCount,
84+
}
85+
const statusColors = {
86+
approved: 'text-green',
87+
archived: 'text-purple',
88+
unlisted: 'text-purple',
89+
withheld: 'text-red',
90+
processing: 'text-orange',
91+
draft: 'text-blue',
92+
rejected: 'text-red',
93+
private: 'text-purple',
94+
scheduled: 'text-orange',
95+
unknown: 'text-orange',
96+
tech_review_failed: 'text-red',
97+
}
98+
const statusStats = computed(() =>
99+
Object.entries(statusMessages)
100+
.map(([key, message]) => {
101+
const status = key as keyof typeof statusMessages
102+
return {
103+
status,
104+
message,
105+
count: props.stats.find((stat) => stat.status === status)?.count ?? 0,
106+
icon:
107+
status === 'tech_review_failed'
108+
? ShieldAlertIcon
109+
: status === 'approved'
110+
? CheckIcon
111+
: PROJECT_STATUS_ICONS[status],
112+
color: statusColors[status],
113+
}
114+
})
115+
.filter((stat) => stat.count > 0),
116+
)
117+
</script>

‎apps/frontend/src/components/analytics-dashboard/analytics-chart/index.vue‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
</div>
8181

8282
<div class="pointer-events-none absolute inset-0 z-[20] overflow-hidden rounded-xl">
83-
<AnalyticsLoadingBar :loading="isDataLoading" />
83+
<PageLoadingBar :loading="isDataLoading" />
8484
</div>
8585

8686
<div v-if="isDataLoading" class="absolute inset-0 z-[19] overflow-hidden rounded-xl">
@@ -98,14 +98,13 @@
9898
</template>
9999

100100
<script setup lang="ts">
101-
import { useVIntl } from '@modrinth/ui'
101+
import { PageLoadingBar, useVIntl } from '@modrinth/ui'
102102
103103
import { getDefaultAnalyticsGraphProjectEventsVisibility } from '~/components/analytics-dashboard/analytics-route-query'
104104
import type { AnalyticsGroupByPreset } from '~/providers/analytics/analytics'
105105
import { injectAnalyticsDashboardContext } from '~/providers/analytics/analytics'
106106
107107
import { analyticsMessages } from '../analytics-messages.ts'
108-
import AnalyticsLoadingBar from '../AnalyticsLoadingBar.vue'
109108
import AnalyticsChartLegend from './analytics-chart-header/AnalyticsChartLegend.vue'
110109
import AnalyticsChartRenderLimitModal from './analytics-chart-header/AnalyticsChartRenderLimitModal.vue'
111110
import AnalyticsChartHeader from './analytics-chart-header/index.vue'

‎apps/frontend/src/components/analytics-dashboard/analytics-table/index.vue‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div class="relative overflow-hidden rounded-2xl">
3-
<AnalyticsLoadingBar :loading="isDataLoading" />
3+
<PageLoadingBar :loading="isDataLoading" />
44
<Table
55
v-model:selected-ids="tableSelectedGraphDatasetIds"
66
:sort-column="displayedSortColumn"
@@ -194,6 +194,7 @@ import { TeleportOverflowMenu } from '@modrinth/ui'
194194
import {
195195
type ButtonMenuOption,
196196
Input,
197+
PageLoadingBar,
197198
Pagination,
198199
Table,
199200
useFormatNumber,
@@ -221,7 +222,6 @@ import {
221222
analyticsMessages,
222223
analyticsTableMessages,
223224
} from '../analytics-messages.ts'
224-
import AnalyticsLoadingBar from '../AnalyticsLoadingBar.vue'
225225
import {
226226
isNoDependentAnalyticsBreakdownValue,
227227
isUnknownAnalyticsBreakdownValue,
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<template>
2+
<div
3+
ref="interactionScope"
4+
class="editable-review-section flex h-full min-h-0 flex-col gap-2.5 overflow-y-auto overflow-x-hidden px-2 pb-2"
5+
>
6+
<div
7+
class="sticky top-0 z-10 flex shrink-0 items-start justify-between gap-4 bg-surface-1 pt-2"
8+
>
9+
<ReviewPanel
10+
mode="inline"
11+
:target="{ kind: 'description' }"
12+
hide-title
13+
:disabled="isLoading || !!error"
14+
:interaction-scope="interactionScope"
15+
class="!w-auto min-w-0 flex-1 pb-2.5"
16+
/>
17+
<div class="mt-0.5 flex shrink-0 justify-end gap-1">
18+
<template v-if="editing">
19+
<Button type="quiet" size="xs" :disabled="saving" @click="resetEditing">
20+
{{ formatMessage(commonMessages.cancelButton) }}
21+
</Button>
22+
<Button
23+
type="colored"
24+
color="brand"
25+
size="xs"
26+
:loading="saving"
27+
:disabled="!valid"
28+
@click="save"
29+
>
30+
{{ formatMessage(commonMessages.saveButton) }}
31+
</Button>
32+
</template>
33+
<EditButton
34+
v-else-if="project && !isLoading && !error"
35+
:section="formatMessage(messages.description)"
36+
:disabled="saving"
37+
@click="startEditing"
38+
/>
39+
</div>
40+
</div>
41+
<div class="min-h-0 flex-1">
42+
<p v-if="!selection" class="m-0 text-secondary">
43+
{{ formatMessage(messages.empty) }}
44+
</p>
45+
<p v-else-if="isLoading" role="status" class="m-0">
46+
{{ formatMessage(messages.loading) }}
47+
</p>
48+
<div v-else-if="error" role="alert">
49+
<p class="m-0">{{ formatMessage(messages.loadError) }}</p>
50+
<Button @click="refresh">{{ formatMessage(messages.retry) }}</Button>
51+
</div>
52+
<div v-else-if="editing" class="flex min-h-full flex-col gap-2">
53+
<TemplateMarkdownEditor
54+
ref="editor"
55+
v-model="draft"
56+
:disabled="saving"
57+
:hide-formatting-buttons="
58+
settings.get(moderationSettings.General.HideMarkdownFormattingButtons)
59+
"
60+
/>
61+
<p v-if="validationMessage" class="m-0 text-sm text-red" role="alert">
62+
{{ validationMessage }}
63+
</p>
64+
</div>
65+
<ProjectPageDescription
66+
v-else-if="project?.description?.trim()"
67+
class="text-sm leading-tight"
68+
:description="project.description"
69+
/>
70+
<p v-else class="m-0 text-secondary">
71+
{{ formatMessage(messages.emptyDescription) }}
72+
</p>
73+
</div>
74+
</div>
75+
</template>
76+
77+
<script setup lang="ts">
78+
import { moderationSettings } from '@modrinth/moderation'
79+
import { Button, commonMessages, ProjectPageDescription, useVIntl } from '@modrinth/ui'
80+
import { useTemplateRef } from 'vue'
81+
82+
import TemplateMarkdownEditor from '~/components/project-review/message-thread/template-markdown-editor.vue'
83+
import { useModerationSettings } from '~/composables/moderation'
84+
import { injectProjectReviewPageContext } from '~/providers/project-review'
85+
86+
import { projectReviewMessages as messages } from '../messages'
87+
import EditButton from '../project-info/edit/button.vue'
88+
import { useProjectTextEdit } from '../project-info/edit/use-project-text-edit'
89+
import ReviewPanel from '../review-panel/index.vue'
90+
91+
const { formatMessage } = useVIntl()
92+
const settings = useModerationSettings()
93+
const interactionScope = useTemplateRef<HTMLElement>('interactionScope')
94+
const { selection, isLoading, error, refresh } = injectProjectReviewPageContext()
95+
const editor = useTemplateRef<InstanceType<typeof TemplateMarkdownEditor>>('editor')
96+
const {
97+
project,
98+
saving,
99+
editing,
100+
draft,
101+
valid,
102+
validationMessage,
103+
startEditing,
104+
resetEditing,
105+
save,
106+
} = useProjectTextEdit('description', () => editor.value?.focus())
107+
</script>

0 commit comments

Comments
 (0)