Skip to content

Commit f93c11c

Browse files
committed
feat: big UI improvements
1 parent 6754173 commit f93c11c

23 files changed

+1099
-178
lines changed

app.vue

+41-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import { createWebSocket } from "~/composables/createWebSocket";
3+
import { useMagicKeys, whenever } from "@vueuse/core";
34
import { emitter } from "~/src/emitter";
45
56
const { data: threads, refresh } = useFetch("/api/threads");
@@ -68,11 +69,27 @@ async function handleNewExam() {
6869
}
6970
7071
const { signIn, getSession, data } = useAuth();
72+
73+
const { ctrl, n } = useMagicKeys();
74+
75+
watchEffect(() => {
76+
if (ctrl.value && n.value) {
77+
handleNewThread();
78+
}
79+
});
7180
</script>
7281

7382
<template>
7483
<UContainer class="pt-4">
75-
<div class="flex justify-end w-full font-mono mb-4">StudyMATE.ai</div>
84+
<div class="flex justify-end items-center w-full mb-4">
85+
<NuxtLink
86+
class="font-mono mr-4"
87+
to="/"
88+
>
89+
StudyHQ.ai
90+
</NuxtLink>
91+
<SettingsMenu />
92+
</div>
7693

7794
<div class="flex h-full">
7895
<div class="w-72">
@@ -87,7 +104,16 @@ const { signIn, getSession, data } = useAuth();
87104
New Chat</UButton
88105
>
89106
</div>
90-
<UVerticalNavigation :links="links" />
107+
<UVerticalNavigation
108+
class="max-h-[400px] overflow-scroll"
109+
:links="links"
110+
/>
111+
<div
112+
v-if="!links.length"
113+
class="flex justify-center"
114+
>
115+
<p class="text-sm text-gray-500">No chats.</p>
116+
</div>
91117

92118
<UDivider class="my-4" />
93119

@@ -99,11 +125,22 @@ const { signIn, getSession, data } = useAuth();
99125
>New Exam</UButton
100126
>
101127
</div>
102-
<UVerticalNavigation :links="examLinks" />
128+
<UVerticalNavigation
129+
class="max-h-[338px] overflow-scroll"
130+
:links="examLinks"
131+
/>
132+
<div
133+
v-if="!examLinks.length"
134+
class="flex justify-center"
135+
>
136+
<p class="text-sm text-gray-500">No exams.</p>
137+
</div>
103138
</div>
104139

105140
<div class="w-full h-full mb-4">
106-
<NuxtPage />
141+
<UContainer>
142+
<NuxtPage />
143+
</UContainer>
107144
</div>
108145
</div>
109146
</UContainer>

blah.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const text = `
2+
Question 1 (2 marks)
3+
4+
blah
5+
6+
Question 2 (marks)
7+
8+
asdf
9+
10+
Question 3
11+
12+
heheheh
13+
`;
14+
15+
// Regular expression to match each question block
16+
const pattern = /Question \d+.*?(?=(Question \d+|$))/gs;
17+
18+
// Use match to find all relevant parts
19+
const chunks = text.match(pattern);
20+
21+
// Check the output
22+
if (chunks) {
23+
chunks.forEach((chunk, index) => {
24+
// console.log(`----\nChunk ${index + 1}:\n${chunk}`);
25+
console.log(chunk);
26+
console.log("=============");
27+
});
28+
} else {
29+
console.log("No matches found.");
30+
}

components/SettingsMenu.vue

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script setup lang="ts">
2+
const { signOut, data, signIn } = useAuth();
3+
4+
const { loading: signingOut, run: handleSignOut } = useLoading(signOut);
5+
6+
const items = computed(() => {
7+
if (data.value) {
8+
return [
9+
[
10+
{
11+
label: "Sign Out",
12+
click: handleSignOut,
13+
disabled: signingOut.value,
14+
},
15+
],
16+
];
17+
}
18+
19+
return [
20+
[
21+
{
22+
label: "Sign up to start studying!",
23+
click: () => signIn("google"),
24+
},
25+
],
26+
];
27+
});
28+
</script>
29+
30+
<template>
31+
<UDropdown
32+
:items="items"
33+
:popper="{ placement: 'bottom-start' }"
34+
>
35+
<UButton
36+
color="white"
37+
trailing-icon="i-heroicons-bars-3"
38+
/>
39+
</UDropdown>
40+
</template>

logic/__tests__/exams.spec.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, expect, it } from "vitest";
2+
import { splitExamIntoQuestions } from "../exams";
3+
4+
describe("splitExamIntoQuestions", () => {
5+
it("splits into separate questions", () => {
6+
const questions =
7+
splitExamIntoQuestions(`Question 1 (3 marks) Write a Python function.
8+
9+
Question 2 (4 marks) Explain how a for loop works in Python.
10+
11+
Question 3 (2 marks) What is the difference between a list and a tuple in Python?`);
12+
13+
expect(questions).toMatchInlineSnapshot(`
14+
[
15+
"Question 1 (3 marks) Write a Python function.",
16+
"Question 2 (4 marks) Explain how a for loop works in Python.",
17+
"Question 3 (2 marks) What is the difference between a list and a tuple in Python?",
18+
]
19+
`);
20+
});
21+
});

logic/exams.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Exam has n questions, formatted like:
3+
*
4+
* Question 1 (X marks) ...
5+
*
6+
* Question 2 (X marks) ...
7+
*
8+
* Split into questions.
9+
*/
10+
export function splitExamIntoQuestions(exam: string) {
11+
// Regular expression to match each question block
12+
const pattern = /Question \d+.*?(?=(Question \d+|$))/gs;
13+
14+
// Use match to find all relevant parts
15+
const split = exam.match(pattern);
16+
console.log("Splitting", split);
17+
18+
if (!split) {
19+
throw new Error("No questions were found or unexpected format!");
20+
}
21+
22+
return Array.from(split).map((x) => x.trim());
23+
}

mydb.sqlite

-16 KB
Binary file not shown.

0 commit comments

Comments
 (0)