Skip to content

Commit d232c36

Browse files
committed
feat: add clustering
1 parent 9ffa466 commit d232c36

File tree

10 files changed

+1064
-114
lines changed

10 files changed

+1064
-114
lines changed

.github/workflows/autofix.yml

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# needed to securely identify the workflow
2+
name: autofix.ci
3+
4+
on:
5+
pull_request:
6+
branches:
7+
- main
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
code:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
- run: corepack enable
19+
- uses: actions/setup-node@v4
20+
with:
21+
node-version: 20
22+
cache: "pnpm"
23+
24+
- name: 📦 Install dependencies
25+
run: pnpm install
26+
27+
# - name: 📦 Install browsers
28+
# run: pnpm playwright install
29+
30+
- name: 🔠 Fix lint errors
31+
run: pnpm lint --fix
32+
33+
# - name: 🧪 Update unit test snapshots
34+
# run: pnpm test:unit -u
35+
36+
# - name: 🏃 Update component test snapshots
37+
# run: pnpm test:nuxt -u
38+
39+
# - name: 🖥️ Update browser test snapshots
40+
# run: pnpm test:browser --update-snapshots
41+
42+
- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c

.github/workflows/ci.yml

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: ci
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
push:
8+
branches:
9+
- main
10+
11+
jobs:
12+
lint:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
- run: corepack enable
18+
- uses: actions/setup-node@v4
19+
with:
20+
node-version: 20
21+
cache: pnpm
22+
23+
- name: 📦 Install dependencies
24+
run: pnpm install
25+
26+
- name: 🔠 Lint project
27+
run: pnpm lint
28+
29+
test:
30+
runs-on: ubuntu-latest
31+
32+
steps:
33+
- uses: actions/checkout@v4
34+
- run: corepack enable
35+
- uses: actions/setup-node@v4
36+
with:
37+
node-version: 20
38+
cache: pnpm
39+
40+
- name: 📦 Install dependencies
41+
run: pnpm install
42+
43+
- name: 💪 Type check
44+
run: pnpm test:types
45+
46+
# - name: 🧪 Unit test
47+
# run: pnpm test:unit
48+
49+
# - name: 🏃 Component tests
50+
# run: pnpm test:nuxt
51+
52+
# browser:
53+
# runs-on: ubuntu-latest
54+
# container:
55+
# image: mcr.microsoft.com/playwright:v1.48.0-focal
56+
57+
# steps:
58+
# - uses: actions/checkout@v4
59+
# - run: corepack enable
60+
# - uses: actions/setup-node@v4
61+
# with:
62+
# node-version: 20
63+
# cache: pnpm
64+
65+
# - name: 📦 Install dependencies
66+
# run: pnpm install
67+
68+
# - name: 🖥️ Test project (browser)
69+
# run: pnpm test:browser

app/app.vue

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<script setup lang="ts">
2+
import hexRgb from 'hex-rgb'
3+
4+
const { data: clusters, refresh, status } = useFetch('/api/clusters/nuxt/nuxt')
5+
6+
function labelColors(color: string) {
7+
const value = hexRgb(color)
8+
9+
return {
10+
'--label-r': value.red,
11+
'--label-g': value.green,
12+
'--label-b': value.blue,
13+
}
14+
}
15+
16+
const stateColors: Record<string, string> = {
17+
open: 'text-green-500',
18+
closed: 'text-purple-500',
19+
merged: 'text-purple-500',
20+
}
21+
22+
const openState = reactive<Record<string, boolean>>({})
23+
24+
async function updateSearch() {}
25+
</script>
26+
27+
<template>
28+
<main class="font-sans m-2">
29+
<nav class="flex flex-row items-center gap-2 ">
30+
<h1 class="text-lg">
31+
unsight.dev
32+
</h1>
33+
<p class="flex gap-1 items-center bg-yellow-400 rounded-full color-black px-2 py-[2px] text-xs font-bold">
34+
proof of concept
35+
</p>
36+
</nav>
37+
<p class="flex gap-2 items-center">
38+
nuxt/nuxt
39+
<button
40+
class="rounded-full w-7 h-7 flex place-items-center border-solid border border-gray-700 bg-transparent color-gray-400 py-2 hover:color-gray-200 active:color-white focus:color-gray-200 hover:border-gray-400 active:border-white focus:border-gray-400 transition-colors"
41+
:class="{ 'animate-spin opacity-50 pointer-events-none': status === 'pending' }"
42+
@click="() => refresh()"
43+
>
44+
<Icon
45+
size="medium"
46+
class="text-gray-400"
47+
name="tabler-refresh"
48+
alt="refresh data"
49+
/>
50+
</button>
51+
</p>
52+
<form
53+
v-if="false"
54+
class="border-solid border border-gray-600 rounded-md flex flex-row items-center relative"
55+
disabled
56+
@submit.prevent="updateSearch"
57+
>
58+
<!-- <label>
59+
<input
60+
id="show-closed"
61+
type="checkbox"
62+
class="mr-2"
63+
>
64+
include closed issues
65+
</label> -->
66+
<input
67+
type="text"
68+
class="bg-transparent pl-8 pr-2 py-2 color-white border-0 flex-grow"
69+
placeholder="repository"
70+
>
71+
<Icon
72+
size="large"
73+
class="absolute ml-2 text-gray-400"
74+
name="tabler-search"
75+
/>
76+
</form>
77+
<section
78+
v-for="(cluster, c) of clusters"
79+
:key="c"
80+
class="flex flex-col gap-4 md:rounded-md md:border-solid border border-gray-700 md:px-4 pb-8 mt-6 columns-1 lg:columns-2 border-b-solid"
81+
>
82+
<h2>
83+
<span class="text-gray-500 inline-block mr-1 font-normal">#</span>{{ c + 1 }}
84+
</h2>
85+
<article
86+
v-for="(issue, i) of openState[c] !== true ? cluster.slice(0, 5) : cluster"
87+
:key="i"
88+
>
89+
<NuxtLink
90+
class="flex flex-row gap-2 leading-tightest no-underline color-current"
91+
:href="issue.html_url"
92+
>
93+
<Icon
94+
size="large"
95+
class="flex-shrink-0"
96+
:class="stateColors[issue.state] || 'text-gray-800'"
97+
:name="issue.pull_request ? 'tabler-git-pull-request' : issue.state === 'closed' ? 'tabler-circle-check' : 'tabler-circle-dot'"
98+
/>
99+
<div class="flex flex-row gap-2 flex-wrap md:flex-nowrap md:pb-6 flex-grow">
100+
<span class="line-clamp-1 flex-grow text-sm md:text-base lg:flex-grow-0">
101+
{{ issue.title }}
102+
</span>
103+
<span
104+
class="text-xs relative md:absolute md:mt-6 text-gray-400 mb-1"
105+
>
106+
<span
107+
v-if="issue.repository"
108+
>
109+
{{ issue.repository?.owner.name }}/{{ issue.repository.name }}
110+
</span>
111+
&middot;
112+
updated
113+
<NuxtTime
114+
:datetime="issue.updated_at"
115+
relative
116+
/>
117+
</span>
118+
<div class="flex flex-row gap-1 items-baseline flex-wrap md:flex-nowrap">
119+
<span
120+
v-for="(label, j) of issue.labels"
121+
:key="j"
122+
class="label bg-gray-200 text-gray-800 rounded-full px-2 py-0.5 whitespace-pre border-solid border-1 text-xs inline-block leading-tight"
123+
:style="labelColors(typeof label === 'string' ? '000000' : label.color || '000000')"
124+
>
125+
{{ typeof label === 'string' ? label : label.name }}
126+
</span>
127+
</div>
128+
</div>
129+
</NuxtLink>
130+
</article>
131+
<button
132+
v-if="cluster.length > 5 && openState[c] !== true"
133+
class="rounded-md border-solid border border-gray-700 bg-transparent color-gray-400 py-2 hover:color-gray-200 active:color-white focus:color-gray-200 hover:border-gray-400 active:border-white focus:border-gray-400 transition-colors"
134+
@click="openState[c] = !openState[c]"
135+
>
136+
show {{ cluster.length - 5 }} more
137+
</button>
138+
</section>
139+
</main>
140+
<footer class="font-sans mt-auto p-2 text-center text-sm opacity-75 hover:opacity-100">
141+
<!-- <a
142+
class="hover:underline text-white"
143+
href="https://github.com/danielroe/unsight.dev"
144+
>
145+
source
146+
</a>
147+
&middot; -->
148+
made with ❤️ by <a
149+
class="font-semibold hover:underline text-white"
150+
href="https://bsky.app/profile/danielroe.dev"
151+
>
152+
@danielroe.dev
153+
</a>
154+
</footer>
155+
</template>
156+
157+
<style scoped>
158+
.label {
159+
background: rgba(var(--label-r), var(--label-g), var(--label-b), 0.30);
160+
color: white;
161+
border-color: rgba(var(--label-r), var(--label-g), var(--label-b), 0.7);
162+
}
163+
</style>
164+
165+
<style>
166+
:root {
167+
background-color: #202830;
168+
color: white;
169+
}
170+
</style>

nuxt.config.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// https://nuxt.com/docs/api/configuration/nuxt-config
22
export default defineNuxtConfig({
3-
modules: ['@unocss/nuxt', '@nuxt/eslint', '@nuxthub/core', '@nuxt/icon'],
3+
modules: ['@unocss/nuxt', '@nuxt/eslint', '@nuxthub/core', '@nuxt/icon', 'nuxt-time'],
44
devtools: { enabled: true },
55
app: { head: { htmlAttrs: { lang: 'en' } } },
66
runtimeConfig: {
@@ -14,6 +14,12 @@ export default defineNuxtConfig({
1414
ai: true,
1515
analytics: true,
1616
cache: true,
17+
vectorize: {
18+
issues: {
19+
dimensions: 1024,
20+
metric: 'euclidean',
21+
},
22+
},
1723
kv: true,
1824
},
1925
eslint: {

package.json

+19-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"dev": "nuxt dev",
88
"generate": "nuxt generate",
99
"preview": "nuxt preview",
10-
"postinstall": "nuxt prepare"
10+
"postinstall": "nuxt prepare && simple-git-hooks",
11+
"lint": "eslint .",
12+
"test:types": "vue-tsc --noEmit"
1113
},
1214
"dependencies": {
1315
"@iconify-json/ri": "1.2.3",
@@ -17,16 +19,31 @@
1719
"@octokit/rest": "^21.0.2",
1820
"@unocss/nuxt": "^0.65.1",
1921
"hex-rgb": "^5.0.0",
22+
"ml-distance": "^4.0.1",
23+
"ml-kmeans": "^6.0.0",
2024
"nuxt": "latest",
25+
"nuxt-time": "1.0.3",
26+
"ohash": "^1.1.4",
2127
"openai": "^4.76.0",
2228
"unocss": "^0.65.1",
2329
"vue": "latest",
2430
"vue-router": "latest"
2531
},
2632
"devDependencies": {
2733
"@nuxt/eslint": "^0.7.2",
28-
"typescript": "^5.7.2",
34+
"lint-staged": "^15.2.10",
35+
"simple-git-hooks": "^2.11.1",
36+
"typescript": "^5.6.3",
37+
"vue-tsc": "^2.1.10",
2938
"wrangler": "^3.93.0"
3039
},
40+
"simple-git-hooks": {
41+
"pre-commit": "npx lint-staged"
42+
},
43+
"lint-staged": {
44+
"*.{js,ts,mjs,cjs,json,.*rc}": [
45+
"npx eslint --fix"
46+
]
47+
},
3148
"packageManager": "[email protected]"
3249
}

0 commit comments

Comments
 (0)