Skip to content

Commit 4970a12

Browse files
authored
Hljs marked (#593)
2 parents dee7625 + c9c037d commit 4970a12

File tree

5 files changed

+206
-113
lines changed

5 files changed

+206
-113
lines changed

packages/vue/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"url-parse": "1.5.10"
2323
},
2424
"dependencies": {
25-
"@highlightjs/vue-plugin": "^2.1.0",
25+
"@highlightjs/vue-plugin": "^2.1.2",
2626
"@mdi/font": "^7.3.67",
2727
"@nilock2/pouchdb-authentication": "^1.0.2",
2828
"@tonaljs/tonal": "^3.6.0",
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<template>
2+
<div class="code-block-wrapper pa-2">
3+
<div class="language-indicator" v-if="language">{{ language }}</div>
4+
<highlightjs :language="language" :code="code" />
5+
</div>
6+
</template>
7+
8+
<script setup lang="ts">
9+
import hljs from 'highlight.js/lib/core';
10+
import hljsVuePlugin from '@highlightjs/vue-plugin';
11+
import { getCurrentInstance } from 'vue';
12+
13+
// Import only the languages you need
14+
import javascript from 'highlight.js/lib/languages/javascript';
15+
import typescript from 'highlight.js/lib/languages/typescript';
16+
import css from 'highlight.js/lib/languages/css';
17+
import html from 'highlight.js/lib/languages/xml';
18+
import bash from 'highlight.js/lib/languages/bash';
19+
import json from 'highlight.js/lib/languages/json';
20+
import go from 'highlight.js/lib/languages/go';
21+
import python from 'highlight.js/lib/languages/python';
22+
23+
// Register languages
24+
hljs.registerLanguage('js', javascript);
25+
hljs.registerLanguage('javascript', javascript);
26+
hljs.registerLanguage('ts', typescript);
27+
hljs.registerLanguage('typescript', typescript);
28+
hljs.registerLanguage('css', css);
29+
hljs.registerLanguage('html', html);
30+
hljs.registerLanguage('bash', bash);
31+
hljs.registerLanguage('sh', bash);
32+
hljs.registerLanguage('json', json);
33+
hljs.registerLanguage('go', go);
34+
hljs.registerLanguage('golang', go);
35+
hljs.registerLanguage('python', python);
36+
37+
// Get access to the current component instance
38+
const instance = getCurrentInstance();
39+
if (instance) {
40+
// Register the component locally
41+
instance.appContext.app.component('highlightjs', hljsVuePlugin.component);
42+
}
43+
44+
defineProps({
45+
code: {
46+
type: String,
47+
required: true,
48+
},
49+
language: {
50+
type: String,
51+
default: '',
52+
},
53+
});
54+
</script>
55+
56+
<style>
57+
/* Import a highlight.js theme */
58+
@import 'highlight.js/styles/github.css';
59+
60+
.code-block-wrapper {
61+
margin: 1rem 0;
62+
border-radius: 4px;
63+
background-color: #f6f8fa;
64+
overflow: auto;
65+
position: relative;
66+
}
67+
68+
.language-indicator {
69+
position: absolute;
70+
top: 8px;
71+
right: 10px;
72+
padding: 2px 8px;
73+
font-size: 0.75rem;
74+
color: #666;
75+
background-color: rgba(255, 255, 255, 0.7);
76+
border-radius: 4px;
77+
z-index: 1;
78+
font-family: monospace;
79+
text-transform: lowercase;
80+
}
81+
</style>

packages/vue/src/base-course/Components/MdTokenRenderer.vue

Lines changed: 107 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
<span v-if="isText(token)">
33
<span v-if="!token.tokens || token.tokens.length === 0">
44
<span v-if="isComponent(token)">
5-
<component :is="parsedComponent(token).is" v-if="!last" :text="parsedComponent(token).text" />
5+
<!-- <component :is="parsedComponent(token).is" v-if="!last" :text="parsedComponent(token).text" /> -->
6+
<component :is="getComponent(parsedComponent(token).is)" v-if="!last" :text="parsedComponent(token).text" />
67
</span>
78
<span v-else-if="containsComponent(token)">
89
<md-token-renderer v-for="(subTok, j) in splitTextToken(token)" :key="j" :token="subTok" />
@@ -92,7 +93,8 @@
9293

9394
<span v-else-if="token.type === 'html'" v-html="token.raw"></span>
9495

95-
<highlightjs v-else-if="token.type === 'code'" class="hljs_render pa-2" :language="token.lang" :code="token.text" />
96+
<code-block-renderer v-else-if="token.type === 'code'" :code="token.text" :language="token.lang" />
97+
<!-- <highlightjs v-else-if="token.type === 'code'" class="hljs_render pa-2" :language="token.lang" :code="token.text" /> -->
9698

9799
<code v-else-if="token.type === 'codespan'" class="codespan" v-html="token.text"></code>
98100

@@ -107,112 +109,122 @@
107109
</em>
108110
</template>
109111

110-
<script lang="ts">
111-
import RadioMultipleChoice from '@/base-course/Components/RadioMultipleChoice.vue';
112+
<script setup lang="ts">
112113
import {
113-
containsComponent,
114-
isComponent,
115-
splitParagraphToken,
116-
splitTextToken,
114+
containsComponent as _containsComponent,
115+
isComponent as _isComponent,
116+
splitParagraphToken as _splitParagraphToken,
117+
splitTextToken as _splitTextToken,
117118
TokenOrComponent,
118119
} from '@/courses/default/questions/fillIn';
120+
import CodeBlockRenderer from './CodeBlockRenderer.vue';
119121
import FillInInput from '@/courses/default/questions/fillIn/fillInInput.vue';
120-
import { marked } from 'marked';
121-
import { defineComponent } from 'vue';
122+
import { MarkedToken, Tokens } from 'marked';
123+
import { markRaw } from 'vue';
124+
import { PropType } from 'vue';
125+
126+
// Register components to be used in the template
127+
// In Vue 3 with <script setup>, components used via :is must be explicitly registered
128+
const components = {
129+
fillIn: markRaw(FillInInput),
130+
// Add other dynamic components here
131+
};
132+
133+
// Define component props
134+
defineProps({
135+
token: {
136+
type: Object as PropType<TokenOrComponent>,
137+
required: true,
138+
},
139+
last: {
140+
type: Boolean,
141+
required: false,
142+
default: false,
143+
},
144+
});
122145
123-
// import hljs from 'highlight.js';
124-
// import 'highlight.js/styles/atelier-seaside-light.css'; // Move CSS import here
125-
// import 'highlight.js/styles/atelier-seaside-light.css';
126-
import hljsVuePlugin from '@highlightjs/vue-plugin';
146+
// Methods
147+
function isComponent(token: MarkedToken): boolean {
148+
const result = _isComponent(token);
149+
return result;
150+
}
127151
128-
export default defineComponent({
129-
name: 'MdTokenRenderer',
152+
function containsComponent(token: MarkedToken): boolean {
153+
const result = _containsComponent(token);
154+
return result;
155+
}
130156
131-
components: {
132-
fillIn: FillInInput,
133-
RadioMultipleChoice,
134-
highlightjs: hljsVuePlugin.component,
135-
},
157+
function splitTextToken(token: MarkedToken): Tokens.Text[] {
158+
return _splitTextToken(token);
159+
}
136160
137-
props: {
138-
token: {
139-
type: Object as () => marked.Token | TokenOrComponent,
140-
required: true,
141-
},
142-
last: {
143-
type: Boolean,
144-
required: false,
145-
default: false,
146-
},
147-
},
161+
function splitParagraphToken(token: Tokens.Paragraph): TokenOrComponent[] {
162+
return _splitParagraphToken(token);
163+
}
148164
149-
methods: {
150-
isComponent(token: marked.Token): boolean {
151-
return isComponent(token);
152-
},
153-
154-
containsComponent(token: marked.Token): boolean {
155-
return containsComponent(token);
156-
},
157-
158-
splitTextToken(token: marked.Tokens.Text): marked.Token[] {
159-
return splitTextToken(token);
160-
},
161-
162-
splitParagraphToken(token: marked.Tokens.Paragraph): TokenOrComponent[] {
163-
return splitParagraphToken(token);
164-
},
165-
166-
parsedComponent(
167-
token: marked.Tokens.Text
168-
): {
169-
is: string;
170-
text: string;
171-
} {
172-
// [ ] switching on component types & loading custom component
173-
//
174-
// sketch:
175-
// const demoustached = token.text.slice(2, token.text.length - 2);
176-
// const firstToken = demoustached.split(' ')[0];
177-
// if (firstToken.charAt(firstToken.length - 1) == '>') {
178-
// return {
179-
// is: firstToken.slice(0, firstToken.length - 1),
180-
// text: demoustached.slice(firstToken.length + 1, demoustached.length),
181-
// };
182-
// }
183-
184-
return {
185-
is: 'fillIn',
186-
text: token.text,
187-
};
188-
},
189-
190-
decodeBasicEntities(text: string): string {
191-
return text
192-
.replace(/&#39;/g, "'")
193-
.replace(/&quot;/g, '"')
194-
.replace(/&amp;/g, '&')
195-
.replace(/&lt;/g, '<')
196-
.replace(/&gt;/g, '>');
197-
},
198-
199-
isText(tok: TokenOrComponent): tok is marked.Tokens.Text {
200-
return (tok as marked.Tokens.Tag).inLink === undefined && tok.type === 'text';
201-
},
202-
},
165+
function parsedComponent(token: MarkedToken): {
166+
is: string;
167+
text: string;
168+
} {
169+
// [ ] switching on component types & loading custom component
170+
//
171+
// sketch:
172+
// const demoustached = token.text.slice(2, token.text.length - 2);
173+
// const firstToken = demoustached.split(' ')[0];
174+
// if (firstToken.charAt(firstToken.length - 1) == '>') {
175+
// return {
176+
// is: firstToken.slice(0, firstToken.length - 1),
177+
// text: demoustached.slice(firstToken.length + 1, demoustached.length),
178+
// };
179+
// }
180+
181+
let text = '';
182+
if ('text' in token && typeof token.text === 'string') {
183+
text = token.text;
184+
} else if ('raw' in token && typeof token.raw === 'string') {
185+
text = token.raw;
186+
}
187+
188+
// This now returns a component from our registered components object
189+
return {
190+
is: 'fillIn', // This should match a key in the components object
191+
text,
192+
};
193+
}
194+
195+
function getComponent(componentName: string) {
196+
// Return the component instance from our components object
197+
return components[componentName as keyof typeof components];
198+
}
199+
200+
function decodeBasicEntities(text: string): string {
201+
return text
202+
.replace(/&#39;/g, "'")
203+
.replace(/&quot;/g, '"')
204+
.replace(/&amp;/g, '&')
205+
.replace(/&lt;/g, '<')
206+
.replace(/&gt;/g, '>');
207+
}
208+
209+
function isText(tok: TokenOrComponent): boolean {
210+
return (tok as any).inLink === undefined && tok.type === 'text';
211+
}
212+
213+
// Make these functions and objects available to the template
214+
defineExpose({
215+
isComponent,
216+
containsComponent,
217+
splitTextToken,
218+
splitParagraphToken,
219+
parsedComponent,
220+
decodeBasicEntities,
221+
isText,
222+
components,
223+
getComponent,
203224
});
204225
</script>
205226

206227
<style lang="css" scoped>
207-
/* @import './../../../node_modules/highlight.js/styles/atelier-seaside-light.css'; */
208-
/* @import './../../../node_modules/highlight.js/styles/stackoverflow-light.css'; */
209-
/* @import './../../../node_modules/highlight.js/styles/xt256.css'; */
210-
/* @import './../../../node_modules/highlight.js/styles/zenburn.css'; */
211-
/* @import './../../../node_modules/highlight.js/styles/tomorrow.css'; */
212-
/* @import './../../../node_modules/highlight.js/styles/lioshi.css'; */
213-
/* @import './../../../node_modules/highlight.js/styles/rainbow.css'; */
214-
/* @import './../../../node_modules/highlight.js/styles/monokai-sublime.css'; */
215-
216228
blockquote {
217229
border-left: 3px teal solid;
218230
padding-left: 8px;

0 commit comments

Comments
 (0)