Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"typescript": "~5.8.3",
"typescript-eslint": "^8.34.1",
"vite": "^7.0.0",
"vite-plugin-chrome-extension": "^0.0.7",
"vite-tsconfig-paths": "^5.1.4"
},
"eslintIgnore": [
Expand Down
6 changes: 3 additions & 3 deletions apps/extension/public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"description": "Chrome Extension boilerplate created with React Typescript.",
"manifest_version": 3,
"version": "1.0.0",
"permissions": ["notifications", "storage", "activeTab"],
"host_permissions": ["http://localhost:8080/*", "https://*/*"],
"permissions": ["tabs", "notifications", "storage", "activeTab"],
"host_permissions": ["<all_urls>"],
"icons": {
"16": "icon16.png",
"48": "icon48.png",
Expand All @@ -22,7 +22,7 @@
},
"content_scripts": [
{
"matches": ["http://localhost/*"],
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
Expand Down
26 changes: 24 additions & 2 deletions apps/extension/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
import * as React from 'react';
import { useState } from 'react';
import logo from './logo.png';
// import './App.css';
import ModalPop from '@components/modalPop/ModalPop';
import { useEffect, useState } from 'react';

const App = () => {
return <ModalPop />;
const [url, setUrl] = useState('');

useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const activeTab = tabs[0];
if (activeTab?.url) {
setUrl(activeTab.url);
console.log('📌 URL:', activeTab.url);

chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
console.log('저장');
});
}
});
}, []);
Comment on lines +10 to +22
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

에러 핸들링 추가를 권장합니다.

chrome.tabs.query와 chrome.storage.local.set 작업에 에러 핸들링이 필요합니다.

다음과 같이 에러 핸들링을 추가하는 것을 권장합니다:

  useEffect(() => {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      const activeTab = tabs[0];
      if (activeTab?.url) {
        setUrl(activeTab.url);
        console.log('📌 URL:', activeTab.url);

-        chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
-          console.log('저장');
-        });
+        chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
+          if (chrome.runtime.lastError) {
+            console.error('저장 실패:', chrome.runtime.lastError);
+          } else {
+            console.log('저장 완료');
+          }
+        });
      }
    });
  }, []);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const activeTab = tabs[0];
if (activeTab?.url) {
setUrl(activeTab.url);
console.log('📌 URL:', activeTab.url);
chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
console.log('저장');
});
}
});
}, []);
useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const activeTab = tabs[0];
if (activeTab?.url) {
setUrl(activeTab.url);
console.log('📌 URL:', activeTab.url);
chrome.storage.local.set({ bookmarkedUrl: activeTab.url }, () => {
if (chrome.runtime.lastError) {
console.error('저장 실패:', chrome.runtime.lastError);
} else {
console.log('저장 완료');
}
});
}
});
}, []);
🤖 Prompt for AI Agents
In apps/extension/src/App.tsx around lines 10 to 22, the chrome.tabs.query and
chrome.storage.local.set calls lack error handling. To fix this, add checks for
errors in their callbacks by inspecting chrome.runtime.lastError and handle or
log any errors appropriately. This will ensure that any issues during these
asynchronous operations are caught and managed gracefully.

return (
<div>
<h1 className="text-lg font-bold">📎 북마크 저장!</h1>
<p className="mt-2 break-words text-sm">{url}</p>
<ModalPop urlInfo={url} />;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

JSX 구문 오류를 수정해주세요.

JSX 요소 뒤에 세미콜론이 잘못 위치했습니다.

다음과 같이 수정해주세요:

-      <ModalPop urlInfo={url} />;
+      <ModalPop urlInfo={url} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ModalPop urlInfo={url} />;
<ModalPop urlInfo={url} />
🤖 Prompt for AI Agents
In apps/extension/src/App.tsx at line 27, remove the semicolon immediately
following the JSX element <ModalPop urlInfo={url} /> as it is a syntax error in
JSX. The JSX element should be written without a trailing semicolon.

</div>
);
};

export default App;
Empty file removed apps/extension/src/background.js
Empty file.
15 changes: 15 additions & 0 deletions apps/extension/src/background.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
console.log('bg');
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'PAGE_DURATION') {
const { url, duration } = message.payload;
console.log(`🌐 ${url} 체류시간: ${duration}초`);

// optionally alert
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon48.png',
title: '체류시간 측정',
message: `${new URL(url).hostname}에서 ${duration}초 머물렀어요!`,
});
}
});
Comment on lines +2 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

에러 처리 및 입력 검증 추가 필요

메시지 핸들러에서 다음 사항들이 개선되어야 합니다:

  1. URL 파싱 시 에러 처리
  2. 메시지 payload 구조 검증
  3. chrome.notifications.create 에러 처리

다음과 같이 개선하는 것을 권장합니다:

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'PAGE_DURATION') {
-    const { url, duration } = message.payload;
+    const { url, duration } = message.payload || {};
+    
+    if (!url || !duration) {
+      console.error('Invalid payload structure');
+      return;
+    }
+    
     console.log(`🌐 ${url} 체류시간: ${duration}초`);

     // optionally alert
-    chrome.notifications.create({
-      type: 'basic',
-      iconUrl: 'icon48.png',
-      title: '체류시간 측정',
-      message: `${new URL(url).hostname}에서 ${duration}초 머물렀어요!`,
-    });
+    try {
+      const hostname = new URL(url).hostname;
+      chrome.notifications.create({
+        type: 'basic',
+        iconUrl: 'icon48.png',
+        title: '체류시간 측정',
+        message: `${hostname}에서 ${duration}초 머물렀어요!`,
+      });
+    } catch (error) {
+      console.error('Failed to create notification:', error);
+    }
   }
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'PAGE_DURATION') {
const { url, duration } = message.payload;
console.log(`🌐 ${url} 체류시간: ${duration}초`);
// optionally alert
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon48.png',
title: '체류시간 측정',
message: `${new URL(url).hostname}에서 ${duration}초 머물렀어요!`,
});
}
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'PAGE_DURATION') {
const { url, duration } = message.payload || {};
if (!url || !duration) {
console.error('Invalid payload structure');
return;
}
console.log(`🌐 ${url} 체류시간: ${duration}초`);
// optionally alert
try {
const hostname = new URL(url).hostname;
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon48.png',
title: '체류시간 측정',
message: `${hostname}에서 ${duration}초 머물렀어요!`,
});
} catch (error) {
console.error('Failed to create notification:', error);
}
}
});
🤖 Prompt for AI Agents
In apps/extension/src/background.ts lines 2 to 15, the message handler lacks
error handling and input validation. Add checks to verify that message.payload
exists and contains valid url and duration fields before processing. Wrap the
URL parsing in a try-catch block to handle invalid URLs gracefully. Also,
provide a callback or promise handling for chrome.notifications.create to catch
and log any errors during notification creation.

Comment on lines +2 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

타입을 별도 파일로 분리해두면 재사용성과 안정성이 높아지고, IDE 자동완성도 더 잘 작동해서 분리해두면 좋을 것 같습니다!
현재 메시지 핸들러에서 전달받는 메시지에 타입이 명시되어 있지 않아 런타임 에러도 방지할 수 있어요 👍

// types/messages.ts
export interface PageDurationMessage {
  type: 'PAGE_DURATION';
  payload: {
    url: string;
    duration: number;
  };
}

export type ExtensionMessage = PageDurationMessage;
// background.ts
import type { ExtensionMessage } from '@/types/messages';

chrome.runtime.onMessage.addListener(
  (message: ExtensionMessage, sender, sendResponse) => {
    if (message.type === 'PAGE_DURATION') {
      const { url, duration } = message.payload;
      // 안전하게 로직 처리 가능
    }
  }
);

25 changes: 23 additions & 2 deletions apps/extension/src/components/modalPop/ModalPop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,23 @@ import {
} from '@pinback/design-system/ui';
import ModalHeader from './ModalHeader';
import { POP_TEXTAREA_MAX_LENGTH } from '@constants/index';
const ModalPop = () => {
interface ModalPopProps {
urlInfo?: string;
}
const ModalPop = (urlInfo: ModalPopProps) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

매개변수 구조분해 문법 오류 수정 필요

함수 매개변수에서 props 구조분해가 잘못되었습니다.

다음과 같이 수정해야 합니다:

-const ModalPop = (urlInfo: ModalPopProps) => {
+const ModalPop = ({ urlInfo }: ModalPopProps) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const ModalPop = (urlInfo: ModalPopProps) => {
const ModalPop = ({ urlInfo }: ModalPopProps) => {
🤖 Prompt for AI Agents
In apps/extension/src/components/modalPop/ModalPop.tsx at line 15, the function
parameter uses incorrect destructuring syntax for props. Change the parameter
from a single object to destructured props by replacing "urlInfo: ModalPopProps"
with "{ urlInfo }: ModalPopProps" to correctly extract urlInfo from the props
object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 urlInfo에 대한 유효성 검사가 없어, 간단하게 !urlInfo 체크와 try/catchnew URL(urlInfo) 정도의 방어처리만 추가해두면 안정성이 높아질 것 같습니다!😀

const handleSave = () => {
chrome.runtime.sendMessage(
{
type: 'SAVE_BOOKMARK',
payload: {
url: { urlInfo },
},
},
(response) => {
console.log('✅ 응답 받음:', response);
}
);
};
Comment on lines +16 to +28
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

에러 처리 및 payload 구조 개선 필요

handleSave 함수에서 다음 사항들이 개선되어야 합니다:

  1. payload 구조가 잘못됨
  2. 에러 처리 부재

다음과 같이 개선하는 것을 권장합니다:

 const handleSave = () => {
   chrome.runtime.sendMessage(
     {
       type: 'SAVE_BOOKMARK',
       payload: {
-        url: { urlInfo },
+        url: urlInfo,
       },
     },
     (response) => {
-      console.log('✅ 응답 받음:', response);
+      if (chrome.runtime.lastError) {
+        console.error('메시지 전송 실패:', chrome.runtime.lastError);
+      } else {
+        console.log('✅ 응답 받음:', response);
+      }
     }
   );
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleSave = () => {
chrome.runtime.sendMessage(
{
type: 'SAVE_BOOKMARK',
payload: {
url: { urlInfo },
},
},
(response) => {
console.log('✅ 응답 받음:', response);
}
);
};
const handleSave = () => {
chrome.runtime.sendMessage(
{
type: 'SAVE_BOOKMARK',
payload: {
url: urlInfo,
},
},
(response) => {
if (chrome.runtime.lastError) {
console.error('메시지 전송 실패:', chrome.runtime.lastError);
} else {
console.log('✅ 응답 받음:', response);
}
}
);
};
🤖 Prompt for AI Agents
In apps/extension/src/components/modalPop/ModalPop.tsx around lines 16 to 28,
the handleSave function has an incorrect payload structure and lacks error
handling. Fix the payload by removing the unnecessary nested object around
urlInfo so that payload directly contains the url value. Add error handling in
the callback of chrome.runtime.sendMessage to check for runtime.lastError and
handle it appropriately, such as logging or user notification.

const [formState, setFormState] = useState({
date: '',
dateError: '',
Expand Down Expand Up @@ -86,7 +102,12 @@ const ModalPop = () => {
</div>
</div>
</div>
<CommonBtn text="저장하기" size="large" type="green" />
<CommonBtn
text="저장하기"
size="large"
type="green"
onClick={handleSave}
/>
Comment on lines +105 to +110
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

버튼 타입 속성 추가 필요

리트리빙된 학습 내용에 따르면, React 컴포넌트에서 button 요소에는 항상 type 속성을 명시해야 합니다.

CommonBtn 컴포넌트에 type="button" 속성을 추가하는 것을 권장합니다:

 <CommonBtn
+  type="button"
   text="저장하기"
   size="large"
   type="green"
   onClick={handleSave}
 />

참고: 만약 CommonBtn 컴포넌트가 내부적으로 button 요소를 렌더링한다면, 해당 컴포넌트에서 type 속성을 처리하도록 해야 합니다.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/extension/src/components/modalPop/ModalPop.tsx around lines 105 to 110,
the CommonBtn component lacks an explicit button type attribute, which can cause
unintended form submissions. Add a type="button" prop to the CommonBtn usage
here. Also verify that the CommonBtn component correctly passes this type prop
to the underlying button element it renders.

</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/src/content.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
console.log('Hello from content script!');
console.log('Hello from content!');
2 changes: 1 addition & 1 deletion apps/extension/src/popup.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// src/popup.tsx
import React from 'react';
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import './App.css';
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default defineConfig({
rollupOptions: {
input: {
popup: resolve(__dirname, 'index.html'),
background: resolve(__dirname, 'src/background.Js'),
background: resolve(__dirname, 'src/background.ts'),
content: resolve(__dirname, 'src/content.ts'),
},
output: {
Expand Down
Loading