diff --git a/src/App.vue b/src/App.vue
index 2cc7eff..ca9f85b 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,9 +1,9 @@
-
diff --git a/src/bus/noteBusEvent.ts b/src/bus/noteBusEvent.ts
new file mode 100644
index 0000000..832efd2
--- /dev/null
+++ b/src/bus/noteBusEvent.ts
@@ -0,0 +1,8 @@
+import { createEventBus } from 'retrobus'
+
+interface EventBusParams {
+ path: string
+ currentNoteSHA?: string
+}
+
+export const noteEventBus = createEventBus('note')
diff --git a/src/components/StackedNote.vue b/src/components/StackedNote.vue
new file mode 100644
index 0000000..02613c0
--- /dev/null
+++ b/src/components/StackedNote.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/src/hooks/useFile.hook.ts b/src/hooks/useFile.hook.ts
new file mode 100644
index 0000000..0a70155
--- /dev/null
+++ b/src/hooks/useFile.hook.ts
@@ -0,0 +1,30 @@
+import { ref } from 'vue'
+import { request } from '@octokit/request'
+import { useMarkdown } from '@/hooks/useMarkdown.hook'
+
+export const useFile = (owner: string, repo: string, sha: string) => {
+ const content = ref('')
+
+ const getContent = async () => {
+ const { render } = useMarkdown()
+ const file = await request(
+ 'GET /repos/{owner}/{repo}/git/blobs/{file_sha}',
+ {
+ owner,
+ repo,
+ file_sha: sha
+ }
+ )
+
+ if (!file) {
+ return
+ }
+ content.value = render(file.data.content)
+ }
+
+ getContent()
+
+ return {
+ content
+ }
+}
diff --git a/src/hooks/useLinks.hook.ts b/src/hooks/useLinks.hook.ts
index 502977f..1bc1c7c 100644
--- a/src/hooks/useLinks.hook.ts
+++ b/src/hooks/useLinks.hook.ts
@@ -1,7 +1,27 @@
-export const useLinks = (className: string) => {
- const linkNote: EventListenerOrEventListenerObject = (e) => {
- e.preventDefault()
- console.log('use links')
+import { noteEventBus } from '@/bus/noteBusEvent'
+import { onUnmounted } from '@vue/runtime-core'
+
+const LINKS = ['http://', 'https://']
+
+export const useLinks = (className: string, sha?: string) => {
+ const linkNote: EventListener = (event) => {
+ event.preventDefault()
+ const target = event.target as HTMLElement
+ const href = target.getAttribute('href')
+
+ if (!href) {
+ return
+ }
+
+ if (LINKS.some((link) => href.startsWith(link))) {
+ window.open(href, '_blank')
+ return
+ }
+
+ noteEventBus.emit({
+ path: href,
+ currentNoteSHA: sha
+ })
}
const selector = `.${className} a`
@@ -21,8 +41,11 @@ export const useLinks = (className: string) => {
})
}
+ onUnmounted(() => {
+ removeListeners()
+ })
+
return {
- listenToClick,
- removeListeners
+ listenToClick
}
}
diff --git a/src/hooks/useMarkdown.hook.ts b/src/hooks/useMarkdown.hook.ts
new file mode 100644
index 0000000..343177a
--- /dev/null
+++ b/src/hooks/useMarkdown.hook.ts
@@ -0,0 +1,20 @@
+import MarkdownIt from 'markdown-it'
+import markdownItClass from '@toycode/markdown-it-class'
+// import sanitizeHtml from 'sanitize-html'
+
+const md = new MarkdownIt().use(markdownItClass, {
+ h1: ['title', 'is-1'],
+ h2: ['subtitle', 'is-2'],
+ h3: ['subtitle', 'is-3'],
+ h4: ['subtitle', 'is-4'],
+ h5: ['subtitle', 'is-5'],
+ h6: ['subtitle', 'is-6'],
+ table: ['table', 'is-striped', 'is-hoverable', 'is-fullwidth']
+})
+
+export const useMarkdown = () => {
+ return {
+ render: (content: string) =>
+ md.render(decodeURIComponent(escape(atob(content))))
+ }
+}
diff --git a/src/hooks/useNote.hook.ts b/src/hooks/useNote.hook.ts
new file mode 100644
index 0000000..27fd9a5
--- /dev/null
+++ b/src/hooks/useNote.hook.ts
@@ -0,0 +1,98 @@
+import { nextTick, onUnmounted, watch } from '@vue/runtime-core'
+import { useRoute, useRouter } from 'vue-router'
+
+import { noteEventBus } from '@/bus/noteBusEvent'
+import { ref } from '@vue/reactivity'
+import { useLinks } from '@/hooks/useLinks.hook'
+import { useRepo } from '@/hooks/useRepo.hook'
+
+const sanitizePath = (path: string) => {
+ if (path.startsWith('./')) {
+ return decodeURIComponent(path.replace('./', ''))
+ }
+ return decodeURIComponent(path)
+}
+
+export const useNote = (user: string, repo: string) => {
+ const { push } = useRouter()
+ const { readme, notFound, tree } = useRepo(user, repo)
+ const { listenToClick } = useLinks('note-display')
+ const { query } = useRoute()
+
+ const stackedNotes = ref(
+ query.stackedNotes
+ ? Array.isArray(query.stackedNotes)
+ ? query.stackedNotes
+ : [query.stackedNotes]
+ : []
+ )
+
+ const unsubscribe = noteEventBus.addEventBusListener(
+ ({ path, currentNoteSHA }) => {
+ const currentFile = tree.value.find((file) => file.sha === currentNoteSHA)
+
+ const absolutePathArray = currentFile?.path?.split('/') ?? []
+ absolutePathArray?.pop()
+ const absolutePath = absolutePathArray.join('/')
+
+ const finalPath = absolutePath
+ ? `${sanitizePath(absolutePath)}/${sanitizePath(path)}`
+ : sanitizePath(path)
+
+ const file = tree.value.find((file) => file.path === finalPath)
+
+ if (!file?.sha || stackedNotes.value.includes(file.sha)) {
+ return
+ }
+
+ const getStackedNotes = () => {
+ if (!file?.sha) {
+ return []
+ }
+
+ if (!currentNoteSHA) {
+ return [file.sha]
+ }
+
+ const [splittedStackedNotes] = stackedNotes.value
+ .join(';')
+ .split(currentNoteSHA)
+
+ return [
+ ...splittedStackedNotes.replaceAll(';;', ';').split(';'),
+ currentNoteSHA,
+ file.sha
+ ].filter((sha) => !!sha)
+ }
+
+ const newStackedNotes = getStackedNotes()
+
+ push({
+ name: 'Note',
+ query: {
+ stackedNotes: newStackedNotes
+ }
+ })
+
+ stackedNotes.value = newStackedNotes
+ }
+ )
+
+ watch(readme, () => {
+ if (readme.value) {
+ nextTick(() => {
+ listenToClick()
+ })
+ }
+ })
+
+ onUnmounted(() => {
+ unsubscribe()
+ })
+
+ return {
+ readme,
+ notFound,
+ stackedNotes
+ }
+}
diff --git a/src/hooks/useRepo.hook.ts b/src/hooks/useRepo.hook.ts
index 90fabc7..4a82b49 100644
--- a/src/hooks/useRepo.hook.ts
+++ b/src/hooks/useRepo.hook.ts
@@ -1,50 +1,67 @@
import { onMounted, ref } from '@vue/runtime-core'
-import MarkdownIt from 'markdown-it'
import { request } from '@octokit/request'
+import { useMarkdown } from '@/hooks/useMarkdown.hook'
-const md = new MarkdownIt()
+interface Tree {
+ path?: string
+ mode?: string
+ type?: string
+ sha?: string
+ size?: number
+ url?: string
+}
export const useRepo = (owner: string, repo: string) => {
+ const { render } = useMarkdown()
const readme = ref(null)
+ const notFound = ref(false)
+ const tree = ref([])
onMounted(async () => {
- const README = await request('GET /repos/{owner}/{repo}/readme', {
- repo,
- owner
- })
-
- if (README) {
- readme.value = md.render(
- decodeURIComponent(escape(atob(README.data.content)))
- )
- }
-
- const commits = await request('GET /repos/{owner}/{repo}/commits', {
- owner,
- repo
- })
-
- const lastCommit = commits.data.shift()
-
- if (!lastCommit) {
- return
- }
-
- const tree = await request(
- 'GET /repos/{owner}/{repo}/git/trees/{tree_sha}',
- {
- owner,
+ try {
+ const README = await request('GET /repos/{owner}/{repo}/readme', {
repo,
- tree_sha: lastCommit.commit.tree.sha,
- recursive: 'true'
- }
- )
+ owner
+ })
- console.log(tree.data.tree.filter((t) => t.type === 'blob'))
+ if (README) {
+ readme.value = render(README.data.content)
+ }
+
+ const commits = await request('GET /repos/{owner}/{repo}/commits', {
+ owner,
+ repo
+ })
+
+ const lastCommit = commits.data.shift()
+
+ if (!lastCommit) {
+ return
+ }
+
+ const treeResponse = await request(
+ 'GET /repos/{owner}/{repo}/git/trees/{tree_sha}',
+ {
+ owner,
+ repo,
+ tree_sha: lastCommit.commit.tree.sha,
+ recursive: 'true'
+ }
+ )
+
+ if (treeResponse) {
+ tree.value = treeResponse.data.tree.filter((t) => t.type === 'blob')
+ console.log(tree.value)
+ }
+ } catch (error) {
+ notFound.value = true
+ }
})
return {
- readme
+ readme,
+ notFound,
+ tree
}
}
diff --git a/src/router/router.ts b/src/router/router.ts
index d44cbb5..7a9b6a1 100644
--- a/src/router/router.ts
+++ b/src/router/router.ts
@@ -1,4 +1,5 @@
-import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
+import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'
+
import Home from '@/views/Home.vue'
const routes: Array = [
@@ -11,6 +12,12 @@ const routes: Array = [
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue')
+ },
+ {
+ path: '/note/:user/:repo',
+ name: 'Note',
+ props: true,
+ component: () => import(/* webpackChunkName: "note" */ '@/views/Note.vue')
}
]
diff --git a/src/styles/app.scss b/src/styles/app.scss
index d3f02bb..5c45711 100644
--- a/src/styles/app.scss
+++ b/src/styles/app.scss
@@ -1,24 +1,12 @@
@charset "utf-8";
@import '~bulma/bulma.sass';
+html {
+ overflow-y: auto;
+}
+
html,
body {
text-align: center;
min-height: 100vh;
}
-
-h1 {
- font-size: 3rem;
-}
-
-h2 {
- font-size: 2.5rem;
-}
-
-h3 {
- font-size: 2rem;
-}
-
-h4 {
- font-size: 1.5rem;
-}
diff --git a/src/views/Home.vue b/src/views/Home.vue
index c976c60..feb846f 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -5,7 +5,7 @@