Skip to content

Commit 99f2d14

Browse files
authored
Add build and lint CI (#70)
1 parent 1760447 commit 99f2d14

14 files changed

+267
-85
lines changed

.eslintrc.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"extends": ["next/core-web-vitals", "next/typescript"]
2+
"extends": ["next/core-web-vitals", "next/typescript", "prettier"]
33
}

.github/workflows/build-and-test.yaml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Lint and Build
2+
permissions:
3+
contents: read
4+
pull-requests: read
5+
on:
6+
push:
7+
branches: [main]
8+
pull_request:
9+
branches: [main]
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: pnpm/action-setup@v4
17+
- name: Use Node.js 22
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: 22
21+
cache: "pnpm"
22+
23+
- name: Install dependencies
24+
run: pnpm install
25+
26+
- name: ESLint
27+
run: pnpm lint
28+
29+
- name: Prettier
30+
run: pnpm format:check
31+
32+
- name: Ensure build succeeds
33+
run: pnpm build

.prettierignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.github/
2+
dist/
3+
docs/
4+
node_modules/
5+
.next/
6+
yarn.lock
7+
pnpm-lock.yaml
8+

.prettierrc

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"singleQuote": false,
3+
"trailingComma": "es5",
4+
"semi": true,
5+
"tabWidth": 2,
6+
"printWidth": 100,
7+
"importOrder": ["<THIRD_PARTY_MODULES>", "^[./]"],
8+
"importOrderSeparation": false,
9+
"importOrderSortSpecifiers": true,
10+
"importOrderParserPlugins": ["typescript", "jsx"],
11+
"plugins": ["@trivago/prettier-plugin-sort-imports"]
12+
}

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,3 @@ You'll also need an agent to speak with. Try our sample voice assistant agent fo
3636
## Contributing
3737

3838
This template is open source and we welcome contributions! Please open a PR or issue through GitHub, and don't forget to join us in the [LiveKit Community Slack](https://livekit.io/join-slack)!
39-

TEMPLATE.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ A Next.js frontend for a simple AI voice assistant using LiveKit's official [Jav
77
When deployed in a sandbox, LiveKit will host an instance of this application for you, providing a unique, shareable URL through which you can access it. Any agents running with the same LiveKit project credentials will join, meaning that you can rapidly iterate on your agent prototypes, and share the results instantly with friends and colleagues. To begin testing your agent, deploy this app in sandbox then set up an agent on your local machine using the [LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/):
88

99
```console
10-
lk app create --template voice-pipeline-agent-python
10+
lk app create --template voice-pipeline-agent-python
1111
```
1212

1313
**NOTE:** For a list of all available templates, run `lk app list-templates`.
1414

15-
1615
## Installation
1716

1817
To run this application locally, clone the repo or use the [LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/):

app/api/connection-details/route.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import {
2-
AccessToken,
3-
AccessTokenOptions,
4-
VideoGrant,
5-
} from "livekit-server-sdk";
1+
import { AccessToken, AccessTokenOptions, VideoGrant } from "livekit-server-sdk";
62
import { NextResponse } from "next/server";
73

84
// NOTE: you are expected to define the following environment variables in `.env.local`:
@@ -37,7 +33,7 @@ export async function GET() {
3733
const roomName = `voice_assistant_room_${Math.floor(Math.random() * 10_000)}`;
3834
const participantToken = await createParticipantToken(
3935
{ identity: participantIdentity },
40-
roomName,
36+
roomName
4137
);
4238

4339
// Return connection details
@@ -59,10 +55,7 @@ export async function GET() {
5955
}
6056
}
6157

62-
function createParticipantToken(
63-
userInfo: AccessTokenOptions,
64-
roomName: string
65-
) {
58+
function createParticipantToken(userInfo: AccessTokenOptions, roomName: string) {
6659
const at = new AccessToken(API_KEY, API_SECRET, {
6760
...userInfo,
6861
ttl: "15m",

app/layout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import "@livekit/components-styles";
2-
import "./globals.css";
32
import { Public_Sans } from "next/font/google";
3+
import "./globals.css";
44

55
const publicSans400 = Public_Sans({
66
weight: "400",

app/page.tsx

+31-44
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
"use client";
22

3-
import { AnimatePresence, motion } from "framer-motion";
3+
import { CloseIcon } from "@/components/CloseIcon";
4+
import { NoAgentNotification } from "@/components/NoAgentNotification";
45
import {
5-
LiveKitRoom,
6-
useVoiceAssistant,
6+
AgentState,
77
BarVisualizer,
8+
DisconnectButton,
9+
LiveKitRoom,
810
RoomAudioRenderer,
911
VoiceAssistantControlBar,
10-
AgentState,
11-
DisconnectButton,
12+
useVoiceAssistant,
1213
} from "@livekit/components-react";
13-
import { useCallback, useEffect, useState } from "react";
14+
import { useKrispNoiseFilter } from "@livekit/components-react/krisp";
15+
import { AnimatePresence, motion } from "framer-motion";
1416
import { MediaDeviceFailure } from "livekit-client";
17+
import { useCallback, useEffect, useState } from "react";
1518
import type { ConnectionDetails } from "./api/connection-details/route";
16-
import { NoAgentNotification } from "@/components/NoAgentNotification";
17-
import { CloseIcon } from "@/components/CloseIcon";
18-
import { useKrispNoiseFilter } from "@livekit/components-react/krisp";
1919

2020
export default function Page() {
21-
const [connectionDetails, updateConnectionDetails] = useState<
22-
ConnectionDetails | undefined
23-
>(undefined);
21+
const [connectionDetails, updateConnectionDetails] = useState<ConnectionDetails | undefined>(
22+
undefined
23+
);
2424
const [agentState, setAgentState] = useState<AgentState>("disconnected");
2525

2626
const onConnectButtonClicked = useCallback(async () => {
@@ -34,8 +34,7 @@ export default function Page() {
3434
// own participant name, and possibly to choose from existing rooms to join.
3535

3636
const url = new URL(
37-
process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT ??
38-
"/api/connection-details",
37+
process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT ?? "/api/connection-details",
3938
window.location.origin
4039
);
4140
const response = await fetch(url.toString());
@@ -44,10 +43,7 @@ export default function Page() {
4443
}, []);
4544

4645
return (
47-
<main
48-
data-lk-theme="default"
49-
className="h-full grid content-center bg-[var(--lk-bg)]"
50-
>
46+
<main data-lk-theme="default" className="h-full grid content-center bg-[var(--lk-bg)]">
5147
<LiveKitRoom
5248
token={connectionDetails?.participantToken}
5349
serverUrl={connectionDetails?.serverUrl}
@@ -61,20 +57,15 @@ export default function Page() {
6157
className="grid grid-rows-[2fr_1fr] items-center"
6258
>
6359
<SimpleVoiceAssistant onStateChange={setAgentState} />
64-
<ControlBar
65-
onConnectButtonClicked={onConnectButtonClicked}
66-
agentState={agentState}
67-
/>
60+
<ControlBar onConnectButtonClicked={onConnectButtonClicked} agentState={agentState} />
6861
<RoomAudioRenderer />
6962
<NoAgentNotification state={agentState} />
7063
</LiveKitRoom>
7164
</main>
7265
);
7366
}
7467

75-
function SimpleVoiceAssistant(props: {
76-
onStateChange: (state: AgentState) => void;
77-
}) {
68+
function SimpleVoiceAssistant(props: { onStateChange: (state: AgentState) => void }) {
7869
const { state, audioTrack } = useVoiceAssistant();
7970
useEffect(() => {
8071
props.onStateChange(state);
@@ -92,10 +83,7 @@ function SimpleVoiceAssistant(props: {
9283
);
9384
}
9485

95-
function ControlBar(props: {
96-
onConnectButtonClicked: () => void;
97-
agentState: AgentState;
98-
}) {
86+
function ControlBar(props: { onConnectButtonClicked: () => void; agentState: AgentState }) {
9987
/**
10088
* Use Krisp background noise reduction when available.
10189
* Note: This is only available on Scale plan, see {@link https://livekit.io/pricing | LiveKit Pricing} for more details.
@@ -122,21 +110,20 @@ function ControlBar(props: {
122110
)}
123111
</AnimatePresence>
124112
<AnimatePresence>
125-
{props.agentState !== "disconnected" &&
126-
props.agentState !== "connecting" && (
127-
<motion.div
128-
initial={{ opacity: 0, top: "10px" }}
129-
animate={{ opacity: 1, top: 0 }}
130-
exit={{ opacity: 0, top: "-10px" }}
131-
transition={{ duration: 0.4, ease: [0.09, 1.04, 0.245, 1.055] }}
132-
className="flex h-8 absolute left-1/2 -translate-x-1/2 justify-center"
133-
>
134-
<VoiceAssistantControlBar controls={{ leave: false }} />
135-
<DisconnectButton>
136-
<CloseIcon />
137-
</DisconnectButton>
138-
</motion.div>
139-
)}
113+
{props.agentState !== "disconnected" && props.agentState !== "connecting" && (
114+
<motion.div
115+
initial={{ opacity: 0, top: "10px" }}
116+
animate={{ opacity: 1, top: 0 }}
117+
exit={{ opacity: 0, top: "-10px" }}
118+
transition={{ duration: 0.4, ease: [0.09, 1.04, 0.245, 1.055] }}
119+
className="flex h-8 absolute left-1/2 -translate-x-1/2 justify-center"
120+
>
121+
<VoiceAssistantControlBar controls={{ leave: false }} />
122+
<DisconnectButton>
123+
<CloseIcon />
124+
</DisconnectButton>
125+
</motion.div>
126+
)}
140127
</AnimatePresence>
141128
</div>
142129
);

components/CloseIcon.tsx

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
export function CloseIcon() {
22
return (
3-
<svg
4-
width="16"
5-
height="16"
6-
viewBox="0 0 16 16"
7-
fill="none"
8-
xmlns="http://www.w3.org/2000/svg"
9-
>
3+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
104
<path
115
d="M3.33398 3.33334L12.6673 12.6667M12.6673 3.33334L3.33398 12.6667"
126
stroke="currentColor"

components/NoAgentNotification.tsx

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { AgentState } from "@livekit/components-react";
22
import { useEffect, useRef, useState } from "react";
3+
34
interface NoAgentNotificationProps extends React.PropsWithChildren<object> {
45
state: AgentState;
56
}
@@ -24,10 +25,7 @@ export function NoAgentNotification(props: NoAgentNotificationProps) {
2425
useEffect(() => {
2526
if (props.state === "connecting") {
2627
timeoutRef.current = window.setTimeout(() => {
27-
if (
28-
props.state === "connecting" &&
29-
agentHasConnected.current === false
30-
) {
28+
if (props.state === "connecting" && agentHasConnected.current === false) {
3129
setShowNotification(true);
3230
}
3331
}, timeToWaitMs);
@@ -67,8 +65,8 @@ export function NoAgentNotification(props: NoAgentNotificationProps) {
6765
</svg>
6866
</div>
6967
<p className="text-pretty w-max">
70-
It&apos;s quiet... too quiet. Is your agent lost? Ensure your agent
71-
is properly configured and running on your machine.
68+
It&apos;s quiet... too quiet. Is your agent lost? Ensure your agent is properly
69+
configured and running on your machine.
7270
</p>
7371
<a
7472
href="https://docs.livekit.io/agents/quickstarts/s2s/"

package.json

+9-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"dev": "next dev",
77
"build": "next build",
88
"start": "next start",
9-
"lint": "next lint"
9+
"lint": "next lint",
10+
"format:check": "prettier --check .",
11+
"format:write": "prettier --write ."
1012
},
1113
"dependencies": {
1214
"@livekit/components-react": "^2.7.0",
@@ -15,18 +17,22 @@
1517
"framer-motion": "^11.18.0",
1618
"livekit-client": "^2.8.0",
1719
"livekit-server-sdk": "^2.9.7",
18-
"next": "14.2.24",
1920
"react": "^18.3.1",
2021
"react-dom": "^18.3.1"
2122
},
2223
"devDependencies": {
24+
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
2325
"@types/node": "^20.17.13",
2426
"@types/react": "^18.3.18",
2527
"@types/react-dom": "^18.3.5",
2628
"eslint": "^8.57.1",
2729
"eslint-config-next": "14.2.24",
30+
"eslint-config-prettier": "9.1.0",
31+
"next": "14",
2832
"postcss": "^8.5.1",
33+
"prettier": "^3.4.2",
2934
"tailwindcss": "^3.4.17",
3035
"typescript": "^5.7.3"
31-
}
36+
},
37+
"packageManager": "[email protected]"
3238
}

0 commit comments

Comments
 (0)