Skip to content

Commit de1feb9

Browse files
authored
Merge pull request #121 from nemozak1/develop
Message Docs Improvement
2 parents 810510d + 6549c3b commit de1feb9

File tree

5 files changed

+384
-56
lines changed

5 files changed

+384
-56
lines changed

components.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ declare module 'vue' {
1111
ChatMessage: typeof import('./src/components/ChatMessage.vue')['default']
1212
ChatWidget: typeof import('./src/components/ChatWidget.vue')['default']
1313
ChatWidgetOld: typeof import('./src/components/ChatWidgetOld.vue')['default']
14+
CodeBlock: typeof import('./src/components/CodeBlock.vue')['default']
1415
Collections: typeof import('./src/components/Collections.vue')['default']
1516
Content: typeof import('./src/components/Content.vue')['default']
1617
ElAlert: typeof import('element-plus/es')['ElAlert']
@@ -21,6 +22,7 @@ declare module 'vue' {
2122
ElCollapse: typeof import('element-plus/es')['ElCollapse']
2223
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
2324
ElContainer: typeof import('element-plus/es')['ElContainer']
25+
ElContainter: typeof import('element-plus/es')['ElContainter']
2426
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
2527
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
2628
ElDivider: typeof import('element-plus/es')['ElDivider']
@@ -38,10 +40,12 @@ declare module 'vue' {
3840
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
3941
ElRow: typeof import('element-plus/es')['ElRow']
4042
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
43+
ElTag: typeof import('element-plus/es')['ElTag']
4144
ElTooltip: typeof import('element-plus/es')['ElTooltip']
4245
GlossarySearchNav: typeof import('./src/components/GlossarySearchNav.vue')['default']
4346
HeaderNav: typeof import('./src/components/HeaderNav.vue')['default']
4447
Menu: typeof import('./src/components/Menu.vue')['default']
48+
MessageDocsContent: typeof import('./src/components/MessageDocsContent.vue')['default']
4549
MessageDocsSearchNav: typeof import('./src/components/MessageDocsSearchNav.vue')['default']
4650
Preview: typeof import('./src/components/Preview.vue')['default']
4751
RouterLink: typeof import('vue-router')['RouterLink']

src/components/CodeBlock.vue

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
<!--
2+
- Open Bank Project - API Explorer II
3+
- Copyright (C) 2023-2024, TESOBE GmbH
4+
-
5+
<div v-if="copyable" class="code-block-header">
6+
<button
7+
@click="copyToClipboard"
8+
class="copy-button"
9+
:class="{ 'copied': copied }"
10+
>
11+
<el-icon v-if="!copied" class="icon"><DocumentCopy /></el-icon>
12+
<el-icon v-else class="icon"><Check /></el-icon>
13+
<span v-if="!copied">Copy</span>
14+
<span v-else>Copied!</span>
15+
</button>
16+
</div>rogram is free software: you can redistribute it and/or modify
17+
- it under the terms of the GNU Affero General Public License as published by
18+
- the Free Software Foundation, either version 3 of the License, or
19+
- (at your option) any later version.
20+
-
21+
- This program is distributed in the hope that it will be useful,
22+
- but WITHOUT ANY WARRANTY; without even the implied warranty of
23+
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24+
- GNU Affero General Public License for more details.
25+
-
26+
- You should have received a copy of the GNU Affero General Public License
27+
- along with this program. If not, see <http://www.gnu.org/licenses/>.
28+
-
29+
30+
- TESOBE GmbH
31+
- Osloerstrasse 16/17
32+
- Berlin 13359, Germany
33+
-
34+
- This product includes software developed at
35+
- TESOBE (http://www.tesobe.com/)
36+
-
37+
-->
38+
39+
<script setup lang="ts">
40+
import { ref, onMounted, onUpdated, nextTick } from 'vue'
41+
import { DocumentCopy, Check } from '@element-plus/icons-vue'
42+
43+
// Declare global hljs
44+
declare global {
45+
interface Window {
46+
hljs: {
47+
highlightElement: (element: HTMLElement) => void
48+
}
49+
}
50+
}
51+
52+
interface Props {
53+
code: any
54+
language?: string
55+
copyable?: boolean
56+
}
57+
58+
const props = withDefaults(defineProps<Props>(), {
59+
language: 'json',
60+
copyable: false
61+
})
62+
63+
const codeBlockRef = ref<HTMLElement>()
64+
const copied = ref(false)
65+
66+
const highlight = async () => {
67+
await nextTick()
68+
if (codeBlockRef.value && window.hljs) {
69+
const codeElements = codeBlockRef.value.querySelectorAll('pre code')
70+
codeElements.forEach((block) => {
71+
window.hljs.highlightElement(block as HTMLElement)
72+
})
73+
}
74+
}
75+
76+
const copyToClipboard = async () => {
77+
try {
78+
await navigator.clipboard.writeText(formattedCode)
79+
copied.value = true
80+
setTimeout(() => {
81+
copied.value = false
82+
}, 2000)
83+
} catch (err) {
84+
console.error('Failed to copy: ', err)
85+
}
86+
}
87+
88+
onMounted(() => {
89+
highlight()
90+
})
91+
92+
onUpdated(() => {
93+
highlight()
94+
})
95+
96+
const formattedCode = typeof props.code === 'string'
97+
? props.code
98+
: JSON.stringify(props.code, null, 2)
99+
</script>
100+
101+
<template>
102+
<div ref="codeBlockRef" class="code-block">
103+
<div v-if="copyable" class="code-block-header">
104+
<button
105+
@click="copyToClipboard"
106+
class="copy-button"
107+
:class="{ 'copied': copied }"
108+
>
109+
<span v-if="!copied">
110+
<el-icon :size="10">
111+
<DocumentCopy />
112+
</el-icon>
113+
Copy
114+
</span>
115+
<span v-else>
116+
<el-icon :size="10">
117+
<Check />
118+
</el-icon>
119+
Copied!
120+
</span>
121+
</button>
122+
</div>
123+
<div class="code-container">
124+
<pre><code :class="language">{{ formattedCode }}</code></pre>
125+
</div>
126+
</div>
127+
</template>
128+
129+
<style scoped>
130+
.code-block {
131+
margin: 1rem 0;
132+
border-radius: 8px;
133+
overflow: hidden;
134+
background: #1e1e1e;
135+
border: 1px solid #333;
136+
position: relative;
137+
}
138+
139+
.code-block-header {
140+
background: #2d2d2d;
141+
padding: 0.5rem 1rem;
142+
border-bottom: 1px solid #333;
143+
display: flex;
144+
justify-content: flex-end;
145+
}
146+
147+
.copy-button {
148+
background: #444;
149+
border: 1px solid #666;
150+
color: #ddd;
151+
padding: 0.25rem 0.75rem;
152+
border-radius: 4px;
153+
cursor: pointer;
154+
font-size: 12px;
155+
transition: all 0.2s ease;
156+
display: flex;
157+
align-items: center;
158+
gap: 0.25rem;
159+
}
160+
161+
.copy-button .icon {
162+
font-size: 14px;
163+
}
164+
165+
.copy-button:hover {
166+
background: #555;
167+
border-color: #777;
168+
}
169+
170+
.copy-button.copied {
171+
background: #4caf50;
172+
border-color: #4caf50;
173+
color: white;
174+
}
175+
176+
.code-container {
177+
max-height: 500px;
178+
overflow-y: auto;
179+
}
180+
181+
.code-container pre {
182+
margin: 0;
183+
padding: 1.5rem;
184+
background: #1e1e1e;
185+
color: #ddd;
186+
font-family: 'Fira Code', 'Courier New', monospace;
187+
font-size: 14px;
188+
line-height: 1.5;
189+
overflow-x: auto;
190+
white-space: pre-wrap;
191+
word-wrap: break-word;
192+
}
193+
194+
.code-container code {
195+
background: transparent;
196+
padding: 0;
197+
border-radius: 0;
198+
font-family: inherit;
199+
font-size: inherit;
200+
}
201+
202+
/* Custom scrollbar for code blocks */
203+
.code-container::-webkit-scrollbar {
204+
width: 8px;
205+
}
206+
207+
.code-container::-webkit-scrollbar-track {
208+
background: #2d2d2d;
209+
}
210+
211+
.code-container::-webkit-scrollbar-thumb {
212+
background: #555;
213+
border-radius: 4px;
214+
}
215+
216+
.code-container::-webkit-scrollbar-thumb:hover {
217+
background: #777;
218+
}
219+
220+
.code-container pre::-webkit-scrollbar {
221+
height: 8px;
222+
width: 8px;
223+
}
224+
225+
.code-container pre::-webkit-scrollbar-track {
226+
background: #2d2d2d;
227+
}
228+
229+
.code-container pre::-webkit-scrollbar-thumb {
230+
background: #555;
231+
border-radius: 4px;
232+
}
233+
234+
.code-container pre::-webkit-scrollbar-thumb:hover {
235+
background: #777;
236+
}
237+
238+
/* Syntax highlighting enhancements */
239+
.code-block :deep(.hljs-string) {
240+
color: #98c379;
241+
}
242+
243+
.code-block :deep(.hljs-number) {
244+
color: #d19a66;
245+
}
246+
247+
.code-block :deep(.hljs-literal) {
248+
color: #56b6c2;
249+
}
250+
251+
.code-block :deep(.hljs-attr) {
252+
color: #e06c75;
253+
}
254+
255+
.code-block :deep(.hljs-punctuation) {
256+
color: #abb2bf;
257+
}
258+
</style>

src/components/Preview.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,10 @@ const onError = (error) => {
296296
<JsonEditorVue
297297
v-model="exampleRequestBody"
298298
:stringified="true"
299-
:mode="Mode.tree"
299+
:mode="Mode.text"
300300
v-bind="{/* local props & attrs */}"
301301
:onChange="onJsonEditorChange"
302+
:mainMenuBar="false"
302303
/>
303304
</div>
304305

src/router/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import InternalServerErrorView from '../views/InternalServerErrorView.vue'
3636
import APIServerErrorView from '../views/APIServerErrorView.vue'
3737
import APIServerStatusView from '../views/APIServerStatusView.vue'
3838
import { isServerUp } from '../obp'
39+
import MessageDocsContent from '@/components/CodeBlock.vue'
3940

4041
export default async function router(): Promise<any> {
4142
const isServerActive = await isServerUp()
@@ -60,7 +61,7 @@ export default async function router(): Promise<any> {
6061
{
6162
path: '/message-docs/:id',
6263
name: 'message-docs',
63-
component: isServerActive ? MessageDocsView : InternalServerErrorView
64+
component: isServerActive ? MessageDocsView : InternalServerErrorView,
6465
},
6566
{
6667
path: '/operationid',

0 commit comments

Comments
 (0)