Skip to content

Commit b8b098f

Browse files
committed
feat(app): option to edit setup block
1 parent 513552b commit b8b098f

File tree

4 files changed

+151
-23
lines changed

4 files changed

+151
-23
lines changed

packages/app/components/VDialog.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function onClick() {
4242

4343
<teleport to="body">
4444
<transition name="fade">
45-
<div v-if="show" class="fixed inset-0 flex items-center justify-center">
45+
<div v-if="show" class="fixed inset-0 flex items-center justify-center z-20">
4646
<div
4747
class="absolute z-10 inset-0 flex bg-b-primary/25 backdrop-blur-sm"
4848
@click="show = false"

packages/app/i18n/en-US.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export default {
114114
collectionNotFound: '@:collection not found: {1}',
115115
directoryEntryNotFound: 'Entry not found: {0}',
116116
directoryEntryAlreadyExists: 'Entry already exists: {0}',
117+
errorRenderingBlocks: 'Error rendering blocks',
117118
errorRenderingBlock: 'Error rendering block: {0}',
118119
},
119120
markdownEditor: {

packages/app/modules/editor/components/MSetup.vue

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ import * as Vue from 'vue'
33
44
import { waitFor } from '@composables/utils'
55
import { useContext } from '../composable/context'
6-
import { Node as MarkdownNode } from '@language-kit/markdown'
6+
import { Node as MarkdownNode, NodeType } from '@language-kit/markdown'
77
import { useEvaluation } from '@modules/evaluation/composables/use-evaluation'
88
import { createParser } from '@modules/evaluation/parser/parser'
99
import { useNodeHelper } from '@modules/evaluation/helpers/node-helper'
1010
import npmResolver from '@modules/evaluation/resolvers/npm'
11+
import { useMagicKeys, whenever } from '@vueuse/core'
12+
import MonacoEditor from '../../monaco/components/MEditor.vue'
13+
import { NodeWithId } from '../types/node'
1114
12-
const props = defineProps({
13-
modelValue: {
14-
type: Object as () => MarkdownNode,
15-
required: true,
16-
},
15+
const model = defineModel({
16+
type: Object as PropType<NodeWithId>,
17+
required: true,
1718
})
1819
1920
const loading = ref(false)
@@ -37,14 +38,9 @@ async function load() {
3738
3839
loading.value = true
3940
40-
const startCodeIndex = props.modelValue.tokens.findIndex((t) => t.value === '\n')
41+
if (!model.value.isComponent()) return
4142
42-
const code = props.modelValue.tokens
43-
.slice(startCodeIndex + 1, props.modelValue.tokens.length - 2)
44-
.map((t) => t.value)
45-
.join('')
46-
47-
const nodes = parser.toNodes(code)
43+
const nodes = parser.toNodes(model.value.body)
4844
4945
const toExportNames = [] as string[]
5046
@@ -67,11 +63,72 @@ async function load() {
6763
loading.value = false
6864
}
6965
70-
watch(() => props.modelValue, load, {
66+
watch(() => model.value, load, {
7167
immediate: true,
7268
})
69+
70+
// edit
71+
72+
const dialog = ref(false)
73+
const text = ref('')
74+
75+
const keys = useMagicKeys()
76+
77+
function save() {
78+
const payload = [':: setup', text.value, '', '::', ''].join('\n')
79+
80+
const tokens = parser.toTokens(payload)
81+
82+
// remove eof
83+
tokens.pop()
84+
85+
const node = new NodeWithId(model.value.id, {
86+
...model.value,
87+
tokens,
88+
})
89+
90+
if (node.isComponent()) {
91+
node.name = 'setup'
92+
node.body = text.value
93+
}
94+
95+
model.value = node
96+
97+
dialog.value = false
98+
99+
load()
100+
}
101+
102+
whenever(keys.Alt_S, () => {
103+
dialog.value = !dialog.value
104+
})
105+
106+
watch(dialog, (v) => {
107+
if (!v) return
108+
109+
text.value = model.value.isComponent() ? model.value.body : ''
110+
})
73111
</script>
74112

75113
<template>
76-
<div class="hidden"></div>
114+
<v-dialog v-model="dialog">
115+
<v-card width="800" height="500" color="b-secondary">
116+
<v-card-head padding>
117+
<v-card-title>
118+
{{ $t('editEntity', [$t('block')]) }}
119+
</v-card-title>
120+
121+
<div class="ml-auto flex gap-x-4">
122+
<v-btn color="danger" @click="dialog = false">
123+
{{ $t('cancel') }}
124+
</v-btn>
125+
126+
<v-btn @click="save">
127+
{{ $t('save') }}
128+
</v-btn>
129+
</div>
130+
</v-card-head>
131+
<MonacoEditor v-model="text" language="javascript" autofocus />
132+
</v-card>
133+
</v-dialog>
77134
</template>

packages/app/modules/editor/components/NodeEditor.vue

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import MParagraph from './MParagraph.vue'
66
import MSetup from './MSetup.vue'
77
import MComponent from './MComponent.vue'
88
import { provideNodeEditor } from '../composable/node-editor'
9+
import { NodeType } from '@language-kit/markdown'
10+
import { Token, TokenType } from '@language-kit/lexer'
11+
import { Icon } from '@iconify/vue'
912
1013
const nodes = defineModel({
1114
type: Array as PropType<NodeWithId[]>,
@@ -16,34 +19,101 @@ const emit = defineEmits(['change'])
1619
1720
const editor = provideNodeEditor()
1821
19-
function shouldHide(node: NodeWithId) {
22+
function isSetupNode(node: NodeWithId) {
2023
if (!node.isComponent()) return
2124
2225
return node.name === 'setup'
2326
}
2427
25-
function isSetup(node: NodeWithId) {
26-
if (!node.isComponent()) return
28+
function findNodeSetup() {
29+
return nodes.value.find(isSetupNode)
30+
}
2731
28-
return node.name === 'setup'
32+
function findStartNode() {
33+
const setupNode = findNodeSetup()
34+
35+
if (!setupNode) return nodes.value[0]
36+
37+
const setupIndex = nodes.value.indexOf(setupNode)
38+
39+
return nodes.value.find((n, i) => {
40+
if (i <= setupIndex) return false
41+
42+
return n.tokens.find((t) => t.type !== TokenType.BreakLine)
43+
})
2944
}
3045
3146
function updateNode(index: number, node: NodeWithId) {
3247
nodes.value.splice(index, 1, node)
3348
3449
emit('change', nodes.value)
3550
}
51+
52+
// Hidden blocks
53+
54+
const hiddenIds = ref<string[]>([])
55+
56+
function setHideIds() {
57+
hiddenIds.value = []
58+
59+
const startNode = findStartNode()
60+
61+
if (!startNode) return
62+
63+
const startNodeIndex = nodes.value.indexOf(startNode)
64+
65+
if (startNodeIndex === -1) return
66+
67+
nodes.value.forEach((n, index) => {
68+
if (index < startNodeIndex) {
69+
hiddenIds.value.push(n.id)
70+
}
71+
})
72+
}
73+
74+
watch(nodes, setHideIds, {
75+
immediate: true,
76+
})
77+
78+
// handle errors
79+
80+
const error = ref<Error | null>(null)
81+
82+
watch(nodes, () => {
83+
error.value = null
84+
})
85+
86+
onErrorCaptured((err) => {
87+
error.value = err
88+
})
3689
</script>
3790
<template>
38-
<div class="h-full overflow-auto pb-80 border-l border-b-secondary/25">
91+
<div
92+
v-if="error"
93+
class="h-full w-full flex items-center justify-center border-l border-b-secondary/25"
94+
>
95+
<div class="text-center">
96+
<div class="text-6xl text-danger mb-4">
97+
<Icon icon="mdi:alert-circle-outline" class="mx-auto" />
98+
</div>
99+
<div class="text-2xl font-bold">
100+
{{ $t('errors.errorRenderingBlocks') }}
101+
</div>
102+
<div class="text-1xl text-danger">
103+
{{ error.message }}
104+
</div>
105+
</div>
106+
</div>
107+
108+
<div v-else class="h-full overflow-auto pb-80 border-l border-b-secondary/25">
39109
<div
40110
v-for="(node, index) in nodes"
41111
:key="node.id"
42-
:class="shouldHide(node) ? 'hidden' : ''"
112+
:class="hiddenIds.includes(node.id) ? 'hidden' : ''"
43113
class="w-full"
44114
>
45115
<MSetup
46-
v-if="isSetup(node)"
116+
v-if="isSetupNode(node)"
47117
:model-value="node"
48118
@update:model-value="updateNode(index, $event)"
49119
/>

0 commit comments

Comments
 (0)