From 8609289d0cc27c61a371cc2773a39cf6f35e3290 Mon Sep 17 00:00:00 2001 From: "James.B" Date: Wed, 11 Jun 2025 15:46:31 +0200 Subject: [PATCH 01/22] transfer query home page to plasmo inline --- src/contents/plasmo-inline.tsx | 64 +++++++++++++- src/pages/Home.tsx | 157 ++++++++++++++------------------- 2 files changed, 127 insertions(+), 94 deletions(-) diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 63b466c2..982b3092 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -1,6 +1,35 @@ +import { ApolloProvider } from "@apollo/client" +import { QueryClient, QueryClientProvider } from "@tanstack/react-query" +import { apolloClient } from "../lib/apolo-client" import type { PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo" -import { useEffect, useRef, useState } from "react" +import React, { useEffect, useRef, useState } from "react" import IntuitionSearchIcon from "~src/components/icons/IntuitionSearchBar" +import { useGetClaimsByUriQuery } from "~src/graphql/src" +const queryClient = new QueryClient() + +function normalizeUrl(input: string): string { + try { + const u = new URL(input) + let hostname = u.hostname.toLowerCase() + if (hostname.startsWith("www.")) hostname = hostname.slice(4) + let pathname = u.pathname + if (pathname.endsWith("/") && pathname.length > 1) { + pathname = pathname.slice(0, -1) + } + return `https://${hostname}${pathname}${u.search}${u.hash}` + } catch { + return input + } +} + +function buildUriRegex(rawUrl: string): string { + const canonical = normalizeUrl(rawUrl) + let withoutProto = canonical.replace(/^https?:\/\//, "") + if (withoutProto.endsWith("/")) withoutProto = withoutProto.slice(0, -1) + const escaped = withoutProto.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + return `^https?:\\/\\/(?:www\\.)?${escaped}\\/?$` +} + export const config: PlasmoCSConfig = { matches: ["https://*/*"] @@ -13,6 +42,28 @@ export const getShadowHostId = () => "plasmo-inline-example-unique-id" function PlasmoInline() { const [positionY, setPositionY] = useState(50) + + const uri = normalizeUrl(window.location.href) + const uriRegex = buildUriRegex(uri) + + const { data, loading } = useGetClaimsByUriQuery({ + uriRegex, + address: "" + }) + + useEffect(() => { + if (!loading && data) { + chrome.storage.local.set({ + claimByUriResult: { + uri, + data + } + }) + console.log("✅ Data injected from GraphQL", data) + } + }, [loading, data]) + + const draggingRef = useRef(false) const handleMouseDown = (e: React.MouseEvent) => { @@ -85,4 +136,13 @@ function PlasmoInline() { ) } -export default PlasmoInline + +const PlasmoInlineWrapper = () => ( + + + + + +) + +export default PlasmoInlineWrapper diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 169d48b7..6b44e301 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,14 +1,13 @@ import React, { useEffect, useState } from "react" import { Link } from "react-router-dom" -import { useQueryClient } from "@tanstack/react-query" import { useTheme } from "~/src/components/ThemeProvider" -import TabSystem from '../components/TabSystem'; +import TabSystem from '../components/TabSystem' import { useStorage } from "@plasmohq/storage/dist/hook" -import { useGetClaimsByUriQuery } from "~src/graphql/src" -import ClaimRowLite from "~src/components/ui/ClaimRowLite"; -import AtomCard from "~src/components/AtomCard"; +import ClaimRowLite from "~src/components/ui/ClaimRowLite" +import AtomCard from "~src/components/AtomCard" import EyeComponent from "~/src/components/3D/EyeComponent" +// Garde uniquement normalizeUrl pour comparer les URLs function normalizeUrl(input: string): string { try { const u = new URL(input) @@ -24,33 +23,17 @@ function normalizeUrl(input: string): string { } } -function buildUriRegex(rawUrl: string): string { - const canonical = normalizeUrl(rawUrl) - let withoutProto = canonical.replace(/^https?:\/\//, "") - - if (withoutProto.endsWith("/")) { - withoutProto = withoutProto.slice(0, -1) - } - const escaped = withoutProto.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") - - return `^https?:\\/\\/(?:www\\.)?${escaped}\\/?$` -} - - function Home() { const { theme } = useTheme() - const [currentUrl, setCurrentUrl] = useState("") + const [currentUrl, setCurrentUrl] = useState("") const [walletAddress] = useStorage("metamask-account", "") const [activeTab, setActiveTab] = useState("Claims") - - useQueryClient() + const [atomsWithTags, setAtomsWithTags] = useState([]) + const [claims, setClaims] = useState([]) + const [isLoading, setIsLoading] = useState(true) const getCurrentUrl = async () => { - const [tab] = await chrome.tabs.query({ - active: true, - lastFocusedWindow: true - }) - console.log(tab.url) + const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true }) return tab.url } @@ -76,41 +59,50 @@ function Home() { } }, []) - const uriRegex = buildUriRegex(currentUrl) - console.log("normalized URL:", currentUrl) - console.log("uriRegex:", uriRegex) + useEffect(() => { + if (!currentUrl) return + + chrome.storage.local.get("claimByUriResult", ({ claimByUriResult }) => { + if (!claimByUriResult?.uri) return + + if (normalizeUrl(claimByUriResult.uri) === normalizeUrl(currentUrl)) { + const atoms = claimByUriResult.data?.atoms ?? [] + + const extractedClaims = Array.from( + new Map( + atoms + .flatMap((atom) => [ + ...(atom.as_object_claims_aggregate?.nodes ?? []), + ...(atom.as_subject_claims_aggregate?.nodes ?? []) + ]) + .map((claim) => [claim.triple_id, claim]) + ).values() + ) + + const atomsWithTags = atoms.map((atom) => { + const tags = atom.as_subject_claims_aggregate?.nodes + ?.filter((claim) => claim.predicate?.label === "has tag") + .map((claim) => claim.object) + .filter(Boolean) + + const uniqueTags = Array.from( + new Map(tags.map((tag) => [tag.id, tag])).values() + ) - const { data, isLoading, error } = useGetClaimsByUriQuery({uriRegex, address: walletAddress }) - const atoms = data?.atoms ?? [] - console.log("current wallet address:", walletAddress); - console.log("Data :", data) + return { + ...atom, + tags: uniqueTags + } + }) - const claims = Array.from( - new Map( - atoms?.flatMap(atom => [...atom.as_object_claims_aggregate.nodes, ...atom.as_subject_claims_aggregate.nodes]) - .map(claim => [claim.triple_id, claim]) + setAtomsWithTags(atomsWithTags) + setClaims(extractedClaims) + } + + setIsLoading(false) + }) + }, [currentUrl]) - ).values() - ) - - console.log("Claims :", claims); - - const atomsWithTags = atoms.map(atom => { - const tags = atom.as_subject_claims_aggregate.nodes - .filter(claim => claim.predicate.label === "has tag") - .map(claim => claim.object) - .filter(Boolean) - - const uniqueTags = Array.from( - new Map(tags.map(tag => [tag.id, tag])).values() - ) - - return { - ...atom, - tags: uniqueTags - } - }) - console.log("Tags :", atomsWithTags) const tabs = [ @@ -118,28 +110,20 @@ function Home() { label: 'Claims', content:
- {isLoading ? "Chargement..." : (typeof data !== "undefined" && claims.length !== 0)? - ( claims.map((claim, index) => ( - console.log(claim), - - - - - )) + {isLoading ? "Loading..." : claims.length > 0 ? ( + claims.map((claim, index) => ( + + )) ) : (

No claims found for this URL.

- - - + Be the first -

)} @@ -149,31 +133,21 @@ function Home() { label: 'Atoms', content:
- {isLoading ? "Chargement...": (typeof data !== "undefined" && atoms.length != 0)? - (atomsWithTags.map((atom) => { - return ( - - ); - })): - ( + {isLoading ? "Loading..." : atomsWithTags.length > 0 ? ( + atomsWithTags.map((atom) => ( + + )) + ) : (

No atoms found for this URL.

- - - + Be the first -

- ) - - } - + )}
- }, ]; @@ -202,7 +176,6 @@ function Home() {
- {error &&

An error occurred while requesting this page.

} Date: Thu, 12 Jun 2025 18:24:18 +0200 Subject: [PATCH 02/22] Inject data after change tabs or refresh page --- src/background.ts | 12 +++++++++ src/contents/plasmo-inline.tsx | 46 +++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/background.ts b/src/background.ts index fa23ef55..a677eebf 100644 --- a/src/background.ts +++ b/src/background.ts @@ -10,3 +10,15 @@ chrome.runtime.onMessage.addListener((message, sender) => { chrome.sidePanel.open({ tabId, windowId }) } }) + +// 1️⃣ Quand l’onglet finit de charger ou change d’URL +chrome.tabs.onUpdated.addListener((tabId, info) => { + if (info.status === "complete") { + chrome.tabs.sendMessage(tabId, { action: "REFRESH_CLAIMS" }); + } +}); + +// 2️⃣ Quand un onglet devient actif +chrome.tabs.onActivated.addListener(({ tabId }) => { + chrome.tabs.sendMessage(tabId, { action: "REFRESH_CLAIMS" }); +}); \ No newline at end of file diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 982b3092..9770016c 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -46,22 +46,50 @@ function PlasmoInline() { const uri = normalizeUrl(window.location.href) const uriRegex = buildUriRegex(uri) - const { data, loading } = useGetClaimsByUriQuery({ + const { data, loading, refetch } = useGetClaimsByUriQuery({ uriRegex, address: "" }) - useEffect(() => { + + // 1) déplace ton injection dans une fonction réutilisable + const inject = () => { if (!loading && data) { - chrome.storage.local.set({ - claimByUriResult: { - uri, - data + chrome.storage.local.set( + { + claimByUriResult: { uri, data } + }, + () => { + console.log("✅ Data injected from GraphQL", data); } - }) - console.log("✅ Data injected from GraphQL", data) + ); } - }, [loading, data]) + }; + + + useEffect(() => { + const listener = (msg: any) => { + if (msg.action === "REFRESH_CLAIMS") { + console.log("[PlasmoInline] → REFRESH_CLAIMS reçu"); + // 1) refetch les données + refetch() + .then(() => { + // 2) puis inject dans le storage + inject(); + }) + .catch((e) => + console.error("[PlasmoInline] refetch() error:", e) + ); + } + }; + + chrome.runtime.onMessage.addListener(listener); + return () => { + chrome.runtime.onMessage.removeListener(listener); + }; + }, [refetch, loading, data, uri]); + + useEffect(inject, [loading, data, uri]); const draggingRef = useRef(false) From ae4a4d8a2ed8aeb8245afcaca67a977c89f9e1aa Mon Sep 17 00:00:00 2001 From: "James.B" Date: Thu, 12 Jun 2025 20:53:40 +0200 Subject: [PATCH 03/22] home page update after switch tab --- src/pages/Home.tsx | 119 +++++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 41 deletions(-) diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 6b44e301..80d6bb78 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -59,49 +59,86 @@ function Home() { } }, []) - useEffect(() => { - if (!currentUrl) return - - chrome.storage.local.get("claimByUriResult", ({ claimByUriResult }) => { - if (!claimByUriResult?.uri) return - - if (normalizeUrl(claimByUriResult.uri) === normalizeUrl(currentUrl)) { - const atoms = claimByUriResult.data?.atoms ?? [] - - const extractedClaims = Array.from( - new Map( - atoms - .flatMap((atom) => [ - ...(atom.as_object_claims_aggregate?.nodes ?? []), - ...(atom.as_subject_claims_aggregate?.nodes ?? []) - ]) - .map((claim) => [claim.triple_id, claim]) - ).values() - ) - - const atomsWithTags = atoms.map((atom) => { - const tags = atom.as_subject_claims_aggregate?.nodes - ?.filter((claim) => claim.predicate?.label === "has tag") - .map((claim) => claim.object) - .filter(Boolean) - - const uniqueTags = Array.from( - new Map(tags.map((tag) => [tag.id, tag])).values() - ) - - return { - ...atom, - tags: uniqueTags - } - }) - - setAtomsWithTags(atomsWithTags) - setClaims(extractedClaims) - } - +useEffect(() => { + // 1) Reset loading & données + setIsLoading(true) + setClaims([]) + setAtomsWithTags([]) + + // Clé utilisée dans le storage + const KEY = "claimByUriResult" + + // 2) Fonction de traitement commune (snapshot + onChanged) + const process = (uri: string, data: any) => { + if (normalizeUrl(uri) !== currentUrl) { + // URL ne matche pas → on vide + setClaims([]) + setAtomsWithTags([]) setIsLoading(false) + return + } + + const atoms = data.atoms ?? [] + + // Extract claims + const extractedClaims = Array.from( + new Map( + atoms + .flatMap((atom) => [ + ...(atom.as_object_claims_aggregate?.nodes ?? []), + ...(atom.as_subject_claims_aggregate?.nodes ?? []), + ]) + .map((c) => [c.triple_id, c]) + ).values() + ) + + // Build atomsWithTags + const withTags = atoms.map((atom) => { + const tags = atom.as_subject_claims_aggregate?.nodes + ?.filter((c) => c.predicate?.label === "has tag") + .map((c) => c.object) + .filter(Boolean) ?? [] + + const unique = Array.from( + new Map(tags.map((t) => [t.id, t])).values() + ) + + return { ...atom, tags: unique } }) - }, [currentUrl]) + + setClaims(extractedClaims) + setAtomsWithTags(withTags) + setIsLoading(false) + } + + // 3) Lecture initiale (“snapshot”) + chrome.storage.local.get(KEY, (items) => { + const stored = items[KEY] + if (stored && stored.uri && stored.data) { + process(stored.uri, stored.data) + } else { + // Pas de données pour cette URL + setIsLoading(false) + } + }) + + // 4) Écoute des changements futurs + const onChange = ( + changes: Record, + areaName: string + ) => { + if (areaName !== "local" || !changes[KEY]) return + const { uri, data } = changes[KEY].newValue + process(uri, data) + } + chrome.storage.onChanged.addListener(onChange) + + // 5) Cleanup + return () => { + chrome.storage.onChanged.removeListener(onChange) + } +}, [currentUrl]) + console.log("Tags :", atomsWithTags) From 8405a7be25c9674f1ab49b84c8ff4a0380f89799 Mon Sep 17 00:00:00 2001 From: "James.B" Date: Fri, 13 Jun 2025 11:02:00 +0200 Subject: [PATCH 04/22] create files url + clean code --- src/contents/plasmo-inline.tsx | 24 +------------ src/lib/url.ts | 22 ++++++++++++ src/pages/Home.tsx | 65 +++++++++------------------------- 3 files changed, 39 insertions(+), 72 deletions(-) create mode 100644 src/lib/url.ts diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 9770016c..65de12bd 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -6,29 +6,7 @@ import React, { useEffect, useRef, useState } from "react" import IntuitionSearchIcon from "~src/components/icons/IntuitionSearchBar" import { useGetClaimsByUriQuery } from "~src/graphql/src" const queryClient = new QueryClient() - -function normalizeUrl(input: string): string { - try { - const u = new URL(input) - let hostname = u.hostname.toLowerCase() - if (hostname.startsWith("www.")) hostname = hostname.slice(4) - let pathname = u.pathname - if (pathname.endsWith("/") && pathname.length > 1) { - pathname = pathname.slice(0, -1) - } - return `https://${hostname}${pathname}${u.search}${u.hash}` - } catch { - return input - } -} - -function buildUriRegex(rawUrl: string): string { - const canonical = normalizeUrl(rawUrl) - let withoutProto = canonical.replace(/^https?:\/\//, "") - if (withoutProto.endsWith("/")) withoutProto = withoutProto.slice(0, -1) - const escaped = withoutProto.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") - return `^https?:\\/\\/(?:www\\.)?${escaped}\\/?$` -} +import { normalizeUrl, buildUriRegex } from "../lib/url" export const config: PlasmoCSConfig = { diff --git a/src/lib/url.ts b/src/lib/url.ts new file mode 100644 index 00000000..f08b2189 --- /dev/null +++ b/src/lib/url.ts @@ -0,0 +1,22 @@ +export function normalizeUrl(input: string): string { + try { + const u = new URL(input) + let hostname = u.hostname.toLowerCase() + if (hostname.startsWith("www.")) hostname = hostname.slice(4) + let pathname = u.pathname + if (pathname.endsWith("/") && pathname.length > 1) { + pathname = pathname.slice(0, -1) + } + return `https://${hostname}${pathname}${u.search}${u.hash}` + } catch { + return input + } +} + +export function buildUriRegex(rawUrl: string): string { + const canonical = normalizeUrl(rawUrl) + let withoutProto = canonical.replace(/^https?:\/\//, "") + if (withoutProto.endsWith("/")) withoutProto = withoutProto.slice(0, -1) + const escaped = withoutProto.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + return `^https?:\\/\\/(?:www\\.)?${escaped}\\/?$` +} \ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 80d6bb78..01bb157c 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -6,22 +6,8 @@ import { useStorage } from "@plasmohq/storage/dist/hook" import ClaimRowLite from "~src/components/ui/ClaimRowLite" import AtomCard from "~src/components/AtomCard" import EyeComponent from "~/src/components/3D/EyeComponent" +import { normalizeUrl } from "../lib/url" -// Garde uniquement normalizeUrl pour comparer les URLs -function normalizeUrl(input: string): string { - try { - const u = new URL(input) - let hostname = u.hostname.toLowerCase() - if (hostname.startsWith("www.")) hostname = hostname.slice(4) - let pathname = u.pathname - if (pathname.endsWith("/") && pathname.length > 1) { - pathname = pathname.slice(0, -1) - } - return `https://${hostname}${pathname}${u.search}${u.hash}` - } catch { - return input - } -} function Home() { const { theme } = useTheme() @@ -32,46 +18,35 @@ function Home() { const [claims, setClaims] = useState([]) const [isLoading, setIsLoading] = useState(true) - const getCurrentUrl = async () => { - const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true }) - return tab.url +const refreshActiveTab = async () => { + const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true }) + const url = tab?.url ? normalizeUrl(tab.url) : "" + setCurrentUrl(url) + if (tab?.id !== undefined) { + chrome.tabs.sendMessage(tab.id, { type: "REFRESH_CLAIMS" }) } +} - const refreshUrl = () => { - getCurrentUrl().then((url) => { - if (url) { - setCurrentUrl(normalizeUrl(url)) - } else { - setCurrentUrl("") - } - }) +useEffect(() => { + refreshActiveTab() + chrome.tabs.onActivated.addListener(refreshActiveTab) + chrome.tabs.onUpdated.addListener(refreshActiveTab) + return () => { + chrome.tabs.onActivated.removeListener(refreshActiveTab) + chrome.tabs.onUpdated.removeListener(refreshActiveTab) } +}, []) - useEffect(() => { - getCurrentUrl().then((url) => { - if (url) setCurrentUrl(normalizeUrl(url)) - }) - chrome.tabs.onUpdated.addListener(refreshUrl) - chrome.tabs.onActivated.addListener(refreshUrl) - return () => { - chrome.tabs.onUpdated.removeListener(refreshUrl) - chrome.tabs.onActivated.removeListener(refreshUrl) - } - }, []) useEffect(() => { - // 1) Reset loading & données setIsLoading(true) setClaims([]) setAtomsWithTags([]) - // Clé utilisée dans le storage const KEY = "claimByUriResult" - // 2) Fonction de traitement commune (snapshot + onChanged) const process = (uri: string, data: any) => { if (normalizeUrl(uri) !== currentUrl) { - // URL ne matche pas → on vide setClaims([]) setAtomsWithTags([]) setIsLoading(false) @@ -80,7 +55,6 @@ useEffect(() => { const atoms = data.atoms ?? [] - // Extract claims const extractedClaims = Array.from( new Map( atoms @@ -92,7 +66,6 @@ useEffect(() => { ).values() ) - // Build atomsWithTags const withTags = atoms.map((atom) => { const tags = atom.as_subject_claims_aggregate?.nodes ?.filter((c) => c.predicate?.label === "has tag") @@ -111,18 +84,15 @@ useEffect(() => { setIsLoading(false) } - // 3) Lecture initiale (“snapshot”) chrome.storage.local.get(KEY, (items) => { const stored = items[KEY] if (stored && stored.uri && stored.data) { process(stored.uri, stored.data) } else { - // Pas de données pour cette URL setIsLoading(false) } }) - // 4) Écoute des changements futurs const onChange = ( changes: Record, areaName: string @@ -133,15 +103,12 @@ useEffect(() => { } chrome.storage.onChanged.addListener(onChange) - // 5) Cleanup return () => { chrome.storage.onChanged.removeListener(onChange) } }, [currentUrl]) - console.log("Tags :", atomsWithTags) - const tabs = [ { label: 'Claims', From 4d829b4927f49f6130429c0490b6abbbc48cc667 Mon Sep 17 00:00:00 2001 From: "James.B" Date: Fri, 13 Jun 2025 11:46:46 +0200 Subject: [PATCH 05/22] add wallet address --- src/background.ts | 2 -- src/contents/plasmo-inline.tsx | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/background.ts b/src/background.ts index a677eebf..b2e956d9 100644 --- a/src/background.ts +++ b/src/background.ts @@ -11,14 +11,12 @@ chrome.runtime.onMessage.addListener((message, sender) => { } }) -// 1️⃣ Quand l’onglet finit de charger ou change d’URL chrome.tabs.onUpdated.addListener((tabId, info) => { if (info.status === "complete") { chrome.tabs.sendMessage(tabId, { action: "REFRESH_CLAIMS" }); } }); -// 2️⃣ Quand un onglet devient actif chrome.tabs.onActivated.addListener(({ tabId }) => { chrome.tabs.sendMessage(tabId, { action: "REFRESH_CLAIMS" }); }); \ No newline at end of file diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 65de12bd..c7ebc236 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -4,6 +4,7 @@ import { apolloClient } from "../lib/apolo-client" import type { PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo" import React, { useEffect, useRef, useState } from "react" import IntuitionSearchIcon from "~src/components/icons/IntuitionSearchBar" +import { useStorage } from "@plasmohq/storage/dist/hook" import { useGetClaimsByUriQuery } from "~src/graphql/src" const queryClient = new QueryClient() import { normalizeUrl, buildUriRegex } from "../lib/url" @@ -21,12 +22,13 @@ export const getShadowHostId = () => "plasmo-inline-example-unique-id" function PlasmoInline() { const [positionY, setPositionY] = useState(50) + const [walletAddress] = useStorage("metamask-account", "") const uri = normalizeUrl(window.location.href) const uriRegex = buildUriRegex(uri) const { data, loading, refetch } = useGetClaimsByUriQuery({ uriRegex, - address: "" + address: walletAddress }) From d939d984977309663c7463cf43efad215a882b70 Mon Sep 17 00:00:00 2001 From: "James.B" Date: Fri, 13 Jun 2025 13:09:52 +0200 Subject: [PATCH 06/22] add mouse grab --- src/components/icons/IntuitionButtonIcon.tsx | 116 +++++++++++++++++++ src/contents/plasmo-inline.tsx | 26 +++-- 2 files changed, 130 insertions(+), 12 deletions(-) create mode 100644 src/components/icons/IntuitionButtonIcon.tsx diff --git a/src/components/icons/IntuitionButtonIcon.tsx b/src/components/icons/IntuitionButtonIcon.tsx new file mode 100644 index 00000000..6af8f544 --- /dev/null +++ b/src/components/icons/IntuitionButtonIcon.tsx @@ -0,0 +1,116 @@ +import React from "react" + +import { useTheme } from "~/src/components/ThemeProvider" +import { cn } from "~/src/lib/utils" + +interface IntuitionSearchIconProps { + size?: number + className?: string +} + +const IntuitionSearchIcon: React.FC = ({ + size = 32, + className +}) => { + const { theme } = useTheme() + const isDark = theme === "dark" + + return ( +
+ + + + + + + + +
+ ) +} + +export default IntuitionSearchIcon diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index c7ebc236..6e1f8fe4 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -3,7 +3,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { apolloClient } from "../lib/apolo-client" import type { PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo" import React, { useEffect, useRef, useState } from "react" -import IntuitionSearchIcon from "~src/components/icons/IntuitionSearchBar" +import IntuitionButtonIcon from "~src/components/icons/IntuitionSearchBar" import { useStorage } from "@plasmohq/storage/dist/hook" import { useGetClaimsByUriQuery } from "~src/graphql/src" const queryClient = new QueryClient() @@ -21,6 +21,8 @@ export const getShadowHostId = () => "plasmo-inline-example-unique-id" function PlasmoInline() { const [positionY, setPositionY] = useState(50) + const [isHolding, setIsHolding] = useState(false) + const draggingRef = useRef(false) const [walletAddress] = useStorage("metamask-account", "") const uri = normalizeUrl(window.location.href) @@ -31,8 +33,6 @@ function PlasmoInline() { address: walletAddress }) - - // 1) déplace ton injection dans une fonction réutilisable const inject = () => { if (!loading && data) { chrome.storage.local.set( @@ -51,10 +51,8 @@ function PlasmoInline() { const listener = (msg: any) => { if (msg.action === "REFRESH_CLAIMS") { console.log("[PlasmoInline] → REFRESH_CLAIMS reçu"); - // 1) refetch les données refetch() .then(() => { - // 2) puis inject dans le storage inject(); }) .catch((e) => @@ -69,22 +67,23 @@ function PlasmoInline() { }; }, [refetch, loading, data, uri]); - useEffect(inject, [loading, data, uri]); + useEffect(inject, [loading, data, uri]); - const draggingRef = useRef(false) const handleMouseDown = (e: React.MouseEvent) => { + e.preventDefault() + setIsHolding(true) + draggingRef.current = false + const startY = e.clientY const startPositionY = positionY - draggingRef.current = false const handleMouseMove = (moveEvent: MouseEvent) => { const deltaY = moveEvent.clientY - startY if (Math.abs(deltaY) > 5) { draggingRef.current = true } - if (draggingRef.current) { const newY = startPositionY + (deltaY / window.innerHeight) * 100 setPositionY(Math.min(90, Math.max(0, newY))) @@ -95,8 +94,11 @@ function PlasmoInline() { window.removeEventListener("mousemove", handleMouseMove) window.removeEventListener("mouseup", handleMouseUp) + setIsHolding(false) // ← on revient à grab + + // si c'était juste un clic (pas un vrai drag), on ouvre if (!draggingRef.current) { - handleSidePanel() + chrome.runtime.sendMessage({ type: "open_sidepanel" }) } } @@ -121,7 +123,7 @@ function PlasmoInline() { background: "black", color: "white", border: "1px solid #fff", - cursor: "grab", + cursor: isHolding ? "grabbing" : "grab", zIndex: 9999, opacity: 0.2, transition: "opacity 0.3s ease" @@ -133,7 +135,7 @@ function PlasmoInline() { e.currentTarget.style.opacity = "0.2" }} > - {}} size={35} position={{ x: 0, y: 0 }} From 76187b4115f0d866e510c952bbdcc07e82c80cd0 Mon Sep 17 00:00:00 2001 From: "James.B" Date: Fri, 13 Jun 2025 18:09:14 +0200 Subject: [PATCH 07/22] add loading spinner speed & dynamic scam/trust stroke colors --- src/components/icons/IntuitionButtonIcon.tsx | 138 ++++++++----------- src/contents/plasmo-inline.tsx | 24 +++- 2 files changed, 74 insertions(+), 88 deletions(-) diff --git a/src/components/icons/IntuitionButtonIcon.tsx b/src/components/icons/IntuitionButtonIcon.tsx index 6af8f544..54db70ee 100644 --- a/src/components/icons/IntuitionButtonIcon.tsx +++ b/src/components/icons/IntuitionButtonIcon.tsx @@ -1,116 +1,92 @@ import React from "react" - import { useTheme } from "~/src/components/ThemeProvider" import { cn } from "~/src/lib/utils" -interface IntuitionSearchIconProps { - size?: number +interface IntuitionButtonIconProps { className?: string + size?: number + position?: { x?: number | string; y?: number | string } + loading?: boolean + highlightColor?: string + onClick?: () => void } -const IntuitionSearchIcon: React.FC = ({ - size = 32, - className +const IntuitionButtonIcon: React.FC = ({ + className, + size = 50, + position = { x: "0px", y: "0px" }, + loading = false, + highlightColor, + onClick }) => { const { theme } = useTheme() - const isDark = theme === "dark" + const defaultStroke = theme === "dark" ? "white" : "black" + const strokeColor = highlightColor ?? defaultStroke + const cursor = loading ? "wait" : "pointer" return (
+ style={{ + width: size, + height: size, + transform: `translate(${position.x}, ${position.y})`, + cursor + }} + onClick={onClick} + > - + style={{ width: size, height: size }} + > - -
) } -export default IntuitionSearchIcon +export default IntuitionButtonIcon diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 6e1f8fe4..9a27f9c3 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -3,7 +3,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { apolloClient } from "../lib/apolo-client" import type { PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo" import React, { useEffect, useRef, useState } from "react" -import IntuitionButtonIcon from "~src/components/icons/IntuitionSearchBar" +import IntuitionButtonIcon from "~src/components/icons/IntuitionButtonIcon" import { useStorage } from "@plasmohq/storage/dist/hook" import { useGetClaimsByUriQuery } from "~src/graphql/src" const queryClient = new QueryClient() @@ -28,7 +28,7 @@ function PlasmoInline() { const uri = normalizeUrl(window.location.href) const uriRegex = buildUriRegex(uri) - const { data, loading, refetch } = useGetClaimsByUriQuery({ + const { data, loading, isLoading, refetch } = useGetClaimsByUriQuery({ uriRegex, address: walletAddress }) @@ -69,6 +69,18 @@ function PlasmoInline() { useEffect(inject, [loading, data, uri]); + const atoms = data?.atoms ?? [] + const allClaims = atoms.flatMap(atom => [ + ...(atom.as_object_claims_aggregate?.nodes ?? []), + ...(atom.as_subject_claims_aggregate?.nodes ?? []) + ]) + const hasScam = allClaims.some( + c => c.predicate?.label === "is" && c.object?.label === "Scam" + ) + const hasTrustworthy = !hasScam && allClaims.some( + c => c.predicate?.label === "is" && c.object?.label === "Trustworthy" + ) + const highlightColor = hasScam ? "red" : hasTrustworthy ? "green" : undefined const handleMouseDown = (e: React.MouseEvent) => { @@ -94,9 +106,8 @@ function PlasmoInline() { window.removeEventListener("mousemove", handleMouseMove) window.removeEventListener("mouseup", handleMouseUp) - setIsHolding(false) // ← on revient à grab + setIsHolding(false) - // si c'était juste un clic (pas un vrai drag), on ouvre if (!draggingRef.current) { chrome.runtime.sendMessage({ type: "open_sidepanel" }) } @@ -106,9 +117,6 @@ function PlasmoInline() { window.addEventListener("mouseup", handleMouseUp) } - const handleSidePanel = () => { - chrome.runtime.sendMessage({ type: "open_sidepanel" }) - } return (
@@ -138,6 +146,8 @@ function PlasmoInline() { {}} size={35} + loading = {isLoading} + highlightColor={highlightColor} position={{ x: 0, y: 0 }} className="hover:opacity-80 transition-opacity" /> From b17fe87c721d8b5182db7fb1b6b2460c9c08d431 Mon Sep 17 00:00:00 2001 From: "James.B" Date: Wed, 18 Jun 2025 15:30:38 +0200 Subject: [PATCH 08/22] add popup + vote button --- src/components/WarningPopup.tsx | 75 +++++++++++++++++++++++++++++++++ src/components/content.tsx | 2 - src/contents/plasmo-inline.tsx | 57 ++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 src/components/WarningPopup.tsx diff --git a/src/components/WarningPopup.tsx b/src/components/WarningPopup.tsx new file mode 100644 index 00000000..533d159c --- /dev/null +++ b/src/components/WarningPopup.tsx @@ -0,0 +1,75 @@ +import React, { useEffect, useState } from "react" +import { cn } from "~/src/lib/utils" +import VoteButtons from "~src/components/VoteButtons" + + +interface WarningPopupProps { + message: string + offset: number + bgColor?: string + vaultId?: bigint + counterVaultId?: bigint + numPositionsFor?: number + numPositionsAgainst?: number + initialVote?: VoteChoice +} + +/** + * A small popup to display status warnings or confirmations. + * Appears below its parent icon at a given offset. + */ +const WarningPopup: React.FC = ({ + message, + offset, + bgColor = "red", + vaultId, + counterVaultId, + numPositionsFor, + numPositionsAgainst, + initialVote +}) => { + + const [visible, setVisible] = useState(false) + + useEffect(() => { + const timer = setTimeout(() => setVisible(true), 10) + return () => clearTimeout(timer) + }, []) + + return ( +
e.stopPropagation()} + onClick={(e) => e.stopPropagation()} + style={{ + position: "absolute", + top: offset, + left: "50%", + transform: "translateX(-70%)", + background: bgColor, + color: "white", + padding: "4px 8px", + marginTop: "2px", + borderRadius: "4px", + fontSize: "0.75rem", + whiteSpace: "nowrap", + opacity: visible ? 1 : 0, + transition: "opacity 150ms ease-in", + pointerEvents: "auto", + zIndex: 1000 + }} + > + {message} + + +
+ ) +} + +export default WarningPopup diff --git a/src/components/content.tsx b/src/components/content.tsx index 6da0eb15..6391046e 100644 --- a/src/components/content.tsx +++ b/src/components/content.tsx @@ -39,8 +39,6 @@ import ProfileLayout from "./profile/ProfileLayout" import "../styles/global.css" import umamiScriptUrl from "url:../../assets/umami.js" -import TagsPage from "~src/pages/TagsPage" - const API_URL = "https://prod.base.intuition-api.com/v1/graphql" configureClient({ apiUrl: API_URL }) diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 9a27f9c3..334bdc89 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -8,6 +8,7 @@ import { useStorage } from "@plasmohq/storage/dist/hook" import { useGetClaimsByUriQuery } from "~src/graphql/src" const queryClient = new QueryClient() import { normalizeUrl, buildUriRegex } from "../lib/url" +import WarningPopup from "~/src/components/WarningPopup" export const config: PlasmoCSConfig = { @@ -20,6 +21,7 @@ export const getInlineAnchor: PlasmoGetInlineAnchor = () => export const getShadowHostId = () => "plasmo-inline-example-unique-id" function PlasmoInline() { + const iconSize = 35 const [positionY, setPositionY] = useState(50) const [isHolding, setIsHolding] = useState(false) const draggingRef = useRef(false) @@ -117,6 +119,31 @@ function PlasmoInline() { window.addEventListener("mouseup", handleMouseUp) } + const targetClaim = allClaims.find( + c => c.predicate?.label === (hasScam ? "is" : "is") && + c.object?.label === (hasScam ? "Scam" : "Trustworthy") + ) + console.log("DATA CLAIM SCAM OR TRUST", targetClaim) + + const vaultId = targetClaim?.vault?.id + ? BigInt(targetClaim.vault.id) + : undefined + const counterVaultId = targetClaim?.counter_vault?.id + ? BigInt(targetClaim.counter_vault.id) + : undefined + const numPositionsFor = targetClaim?.vault?.positions_aggregate.aggregate?.count + const numPositionsAgainst = targetClaim?.counter_vault?.positions_aggregate.aggregate?.count + + const userStake = Number(targetClaim?.vault?.positions?.[0]?.shares ?? 0) + const userCounterStake = Number(targetClaim?.counter_vault?.positions?.[0]?.shares ?? 0) + + const initialVote: VoteChoice | undefined = + userStake > 0 + ? "for" + : userCounterStake > 0 + ? "against" + : undefined + return (
@@ -145,12 +172,40 @@ function PlasmoInline() { > {}} - size={35} + size={iconSize} loading = {isLoading} highlightColor={highlightColor} position={{ x: 0, y: 0 }} className="hover:opacity-80 transition-opacity" /> + + {highlightColor === "red" && ( + + )} + + {highlightColor === "green" && ( + + )} + +
) From c7a81c11a7f87ae1341a98945d3c43c60ae8c260 Mon Sep 17 00:00:00 2001 From: "James.B" Date: Wed, 18 Jun 2025 21:04:35 +0200 Subject: [PATCH 09/22] style popup --- src/components/WarningPopup.tsx | 39 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/components/WarningPopup.tsx b/src/components/WarningPopup.tsx index 533d159c..6ca1d78e 100644 --- a/src/components/WarningPopup.tsx +++ b/src/components/WarningPopup.tsx @@ -36,6 +36,9 @@ const WarningPopup: React.FC = ({ return () => clearTimeout(timer) }, []) + const textColor = bgColor === "red" ? "#ef4444" : "#22c55e" // rouge-500 ou green-500 + const borderColor = bgColor === "red" ? "#dc2626" : "#16a34a" // rouge-600 ou green-600 + return (
= ({ position: "absolute", top: offset, left: "50%", - transform: "translateX(-70%)", - background: bgColor, - color: "white", - padding: "4px 8px", - marginTop: "2px", - borderRadius: "4px", - fontSize: "0.75rem", + transform: "translateX(-80%)", + background: "#0f0f0f", + color: textColor, + padding: "4px", + marginTop: "8px", + borderRadius: "8px", + fontSize: "0.875rem", + fontWeight: "500", whiteSpace: "nowrap", opacity: visible ? 1 : 0, - transition: "opacity 150ms ease-in", + transition: "all 200ms ease-in-out", pointerEvents: "auto", - zIndex: 1000 + zIndex: 1000, + border: `1px solid ${borderColor}`, + boxShadow: `0 4px 12px rgba(0, 0, 0, 0.3), 0 0 0 1px ${borderColor}20`, + textAlign: "center", // Texte centré + fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif", + letterSpacing: "0.025em", + backdropFilter: "blur(8px)", + minWidth: "140px" }} > - {message} +
+ {message} +
Date: Thu, 19 Jun 2025 16:42:41 +0200 Subject: [PATCH 10/22] fix bug popup animation --- src/components/WarningPopup.tsx | 91 +++++++++++++++++++-------------- src/contents/plasmo-inline.tsx | 50 +++++++++--------- 2 files changed, 77 insertions(+), 64 deletions(-) diff --git a/src/components/WarningPopup.tsx b/src/components/WarningPopup.tsx index 6ca1d78e..0742bd73 100644 --- a/src/components/WarningPopup.tsx +++ b/src/components/WarningPopup.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react" import { cn } from "~/src/lib/utils" import VoteButtons from "~src/components/VoteButtons" - interface WarningPopupProps { message: string offset: number @@ -12,13 +11,14 @@ interface WarningPopupProps { numPositionsFor?: number numPositionsAgainst?: number initialVote?: VoteChoice + forceVisible?: boolean } /** * A small popup to display status warnings or confirmations. * Appears below its parent icon at a given offset. */ -const WarningPopup: React.FC = ({ +const WarningPopup: React.FC = ({ message, offset, bgColor = "red", @@ -26,67 +26,84 @@ const WarningPopup: React.FC = ({ counterVaultId, numPositionsFor, numPositionsAgainst, - initialVote + initialVote, + forceVisible = false }) => { + const [visible, setVisible] = useState(forceVisible) + const [shouldRender, setShouldRender] = useState(forceVisible) - const [visible, setVisible] = useState(false) + const ANIMATION_DURATION = 500 useEffect(() => { - const timer = setTimeout(() => setVisible(true), 10) - return () => clearTimeout(timer) - }, []) + if (forceVisible) { + setShouldRender(true) + setVisible(true) + } else { + setVisible(false) + const tm = setTimeout(() => setShouldRender(false), ANIMATION_DURATION) + return () => clearTimeout(tm) + } + }, [forceVisible]) + + const textColor = bgColor === "red" ? "#b50606" : "#228e01" + const borderColor = bgColor === "red" ? "#b50606" : "#228e01" - const textColor = bgColor === "red" ? "#ef4444" : "#22c55e" // rouge-500 ou green-500 - const borderColor = bgColor === "red" ? "#dc2626" : "#16a34a" // rouge-600 ou green-600 + if (!shouldRender) return null return (
e.stopPropagation()} - onClick={(e) => e.stopPropagation()} + onMouseDown={e => e.stopPropagation()} + onClick={e => e.stopPropagation()} style={{ position: "absolute", top: offset, left: "50%", - transform: "translateX(-80%)", - background: "#0f0f0f", + transform: ` + translateX(-80%) + translateY(${visible ? 0 : '-5px'}) + `, + background: "#0f0f0f", color: textColor, padding: "4px", - marginTop: "8px", borderRadius: "8px", - fontSize: "0.875rem", - fontWeight: "500", + fontSize: "0.875rem", + fontWeight: "500", whiteSpace: "nowrap", - opacity: visible ? 1 : 0, - transition: "all 200ms ease-in-out", pointerEvents: "auto", - zIndex: 1000, + zIndex: 9998, border: `1px solid ${borderColor}`, boxShadow: `0 4px 12px rgba(0, 0, 0, 0.3), 0 0 0 1px ${borderColor}20`, - textAlign: "center", // Texte centré + textAlign: "center", fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif", letterSpacing: "0.025em", backdropFilter: "blur(8px)", - minWidth: "140px" + minWidth: "140px", + opacity: visible ? 1 : 0, + transition: ` + opacity ${ANIMATION_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1), + transform ${ANIMATION_DURATION}ms cubic-bezier(0.34, 1.56, 0.64, 1) + `, + willChange: "transform, opacity" }} > -
- {message} -
+
+ {message} +
- +
) } diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 334bdc89..691282a3 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -25,6 +25,8 @@ function PlasmoInline() { const [positionY, setPositionY] = useState(50) const [isHolding, setIsHolding] = useState(false) const draggingRef = useRef(false) + const [hovered, setHovered] = useState(false) + const [autoVisible, setAutoVisible] = useState(false) const [walletAddress] = useStorage("metamask-account", "") const uri = normalizeUrl(window.location.href) @@ -47,7 +49,11 @@ function PlasmoInline() { ); } }; - + useEffect(() => { + setAutoVisible(true) + const timer = setTimeout(() => setAutoVisible(false), 4000) + return () => clearTimeout(timer) + }, []) useEffect(() => { const listener = (msg: any) => { @@ -83,6 +89,7 @@ function PlasmoInline() { c => c.predicate?.label === "is" && c.object?.label === "Trustworthy" ) const highlightColor = hasScam ? "red" : hasTrustworthy ? "green" : undefined + const showPopup = Boolean(highlightColor) && (hovered || autoVisible) const handleMouseDown = (e: React.MouseEvent) => { @@ -123,16 +130,17 @@ function PlasmoInline() { c => c.predicate?.label === (hasScam ? "is" : "is") && c.object?.label === (hasScam ? "Scam" : "Trustworthy") ) + console.log("DATA CLAIM SCAM OR TRUST", targetClaim) - const vaultId = targetClaim?.vault?.id + const vaultId = targetClaim?.vault?.id ? BigInt(targetClaim.vault.id) : undefined - const counterVaultId = targetClaim?.counter_vault?.id + const counterVaultId = targetClaim?.counter_vault?.id ? BigInt(targetClaim.counter_vault.id) : undefined - const numPositionsFor = targetClaim?.vault?.positions_aggregate.aggregate?.count - const numPositionsAgainst = targetClaim?.counter_vault?.positions_aggregate.aggregate?.count + const numPositionsFor = targetClaim?.vault?.positions_aggregate.aggregate?.count + const numPositionsAgainst = targetClaim?.counter_vault?.positions_aggregate.aggregate?.count const userStake = Number(targetClaim?.vault?.positions?.[0]?.shares ?? 0) const userCounterStake = Number(targetClaim?.counter_vault?.positions?.[0]?.shares ?? 0) @@ -163,49 +171,37 @@ function PlasmoInline() { opacity: 0.2, transition: "opacity 0.3s ease" }} - onMouseEnter={(e) => { + onMouseEnter={e => { + setHovered(true) e.currentTarget.style.opacity = "1" }} - onMouseLeave={(e) => { + onMouseLeave={e => { + setHovered(false) e.currentTarget.style.opacity = "0.2" }} > {}} size={iconSize} - loading = {isLoading} + loading={isLoading} highlightColor={highlightColor} position={{ x: 0, y: 0 }} className="hover:opacity-80 transition-opacity" /> - {highlightColor === "red" && ( + {(highlightColor === "red" || highlightColor === "green") && ( )} - - {highlightColor === "green" && ( - - )} - -
) From a2f9d1dfa7d1e9b03676bf497d81099fc123d1a7 Mon Sep 17 00:00:00 2001 From: "James.B" Date: Thu, 19 Jun 2025 19:07:28 +0200 Subject: [PATCH 11/22] fix display dropdown --- package.json | 2 + pnpm-lock.yaml | 381 +++++++++++++++++++++++++++++- src/components/ReportDropdown.tsx | 52 ++++ src/contents/plasmo-inline.tsx | 44 +++- 4 files changed, 458 insertions(+), 21 deletions(-) create mode 100644 src/components/ReportDropdown.tsx diff --git a/package.json b/package.json index 413b8fd1..8c6f900f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,9 @@ "@graphql-codegen/typescript-document-nodes": "^4.0.11", "@plasmohq/storage": "^1.15.0", "@radix-ui/react-collapsible": "^1.1.4", + "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-hover-card": "^1.1.7", + "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-slot": "^1.1.2", "@tanstack/react-query": "^5.69.0", "@types/three": "^0.175.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8001e72..cda56d2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,9 +32,15 @@ importers: '@radix-ui/react-collapsible': specifier: ^1.1.4 version: 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.15 + version: 2.1.15(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-hover-card': specifier: ^1.1.7 version: 1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-icons': + specifier: ^1.3.2 + version: 1.3.2(react@18.2.0) '@radix-ui/react-slot': specifier: ^1.1.2 version: 1.1.2(@types/react@18.2.48)(react@18.2.0) @@ -2451,6 +2457,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-avatar@1.1.3': resolution: {integrity: sha512-Paen00T4P8L8gd9bNsRMw7Cbaz85oxiv+hzomsRZgFm2byltPFDtfcoqlWJ8GyZlIBWgLssJlzLCnKU0G0302g==} peerDependencies: @@ -2516,6 +2535,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-compose-refs@1.1.1': resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} peerDependencies: @@ -2587,6 +2619,28 @@ packages: '@types/react': optional: true + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.10': + resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dismissable-layer@1.1.5': resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==} peerDependencies: @@ -2613,8 +2667,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.6': - resolution: {integrity: sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==} + '@radix-ui/react-dropdown-menu@2.1.15': + resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -2635,6 +2689,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-focus-guards@1.1.2': + resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-focus-scope@1.1.2': resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==} peerDependencies: @@ -2648,6 +2711,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-hover-card@1.1.7': resolution: {integrity: sha512-HwM03kP8psrv21J1+9T/hhxi0f5rARVbqIZl9+IAq13l4j4fX+oGIuxisukZZmebO7J35w9gpoILvtG8bbph0w==} peerDependencies: @@ -2661,6 +2737,11 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-icons@1.3.2': + resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc + '@radix-ui/react-id@1.1.0': resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} peerDependencies: @@ -2692,6 +2773,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-menu@2.1.15': + resolution: {integrity: sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-menu@2.1.6': resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==} peerDependencies: @@ -2757,6 +2851,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popper@1.2.7': + resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-portal@1.1.4': resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==} peerDependencies: @@ -2783,6 +2890,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-presence@1.1.2': resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} peerDependencies: @@ -2809,6 +2929,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-presence@1.1.4': + resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-primitive@2.0.2': resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==} peerDependencies: @@ -2835,6 +2968,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-progress@1.1.2': resolution: {integrity: sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==} peerDependencies: @@ -2861,6 +3007,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-roving-focus@1.1.10': + resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-roving-focus@1.1.2': resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==} peerDependencies: @@ -2931,6 +3090,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-switch@1.1.3': resolution: {integrity: sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ==} peerDependencies: @@ -3006,6 +3174,24 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-escape-keydown@1.1.0': resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} peerDependencies: @@ -6566,7 +6752,7 @@ snapshots: '@radix-ui/react-checkbox': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-context-menu': 2.2.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-dialog': 1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-dropdown-menu': 2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-dropdown-menu': 2.1.15(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-hover-card': 1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-label': 2.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-navigation-menu': 1.2.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -9400,6 +9586,15 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-avatar@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) @@ -9472,6 +9667,18 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-collection@1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-compose-refs@1.1.1(@types/react@18.2.48)(react@18.2.0)': dependencies: react: 18.2.0 @@ -9538,6 +9745,25 @@ snapshots: optionalDependencies: '@types/react': 18.2.48 + '@radix-ui/react-direction@1.1.1(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -9564,15 +9790,15 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 - '@radix-ui/react-dropdown-menu@2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) - '@radix-ui/react-menu': 2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.2.48)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) optionalDependencies: @@ -9585,6 +9811,12 @@ snapshots: optionalDependencies: '@types/react': 18.2.48 + '@radix-ui/react-focus-guards@1.1.2(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + '@radix-ui/react-focus-scope@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) @@ -9596,6 +9828,17 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-hover-card@1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -9613,6 +9856,10 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-icons@1.3.2(react@18.2.0)': + dependencies: + react: 18.2.0 + '@radix-ui/react-id@1.1.0(@types/react@18.2.48)(react@18.2.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) @@ -9636,6 +9883,32 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-menu@2.1.15(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.2.48)(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.6.3(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-menu@2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -9743,6 +10016,24 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-popper@1.2.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/rect': 1.1.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-portal@1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -9763,6 +10054,16 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-portal@1.1.9(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-presence@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) @@ -9783,6 +10084,16 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-presence@1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-primitive@2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) @@ -9801,6 +10112,15 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-progress@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) @@ -9829,6 +10149,23 @@ snapshots: '@types/react': 18.2.48 '@types/react-dom': 18.2.18 + '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -9915,6 +10252,13 @@ snapshots: optionalDependencies: '@types/react': 18.2.48 + '@radix-ui/react-slot@1.2.3(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + '@radix-ui/react-switch@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -9992,6 +10336,21 @@ snapshots: optionalDependencies: '@types/react': 18.2.48 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.2.48)(react@18.2.0)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) diff --git a/src/components/ReportDropdown.tsx b/src/components/ReportDropdown.tsx new file mode 100644 index 00000000..43d7b9b7 --- /dev/null +++ b/src/components/ReportDropdown.tsx @@ -0,0 +1,52 @@ +// src/components/ReportDropdown.tsx +import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; +import { CheckIcon } from "@radix-ui/react-icons"; +import React, { useState } from "react"; + +const options = [ + { label: "Trustworthy", value: "trustworthy" }, + { label: "Scam", value: "scam" }, +]; + +export const ReportDropdown = ({ + onSelect, + defaultValue = "trustworthy", +}: { + onSelect?: (value: string) => void; + defaultValue?: string; +}) => { + const [selected, setSelected] = useState(defaultValue); + + const handleSelect = (value: string) => { + setSelected(value); + onSelect?.(value); + }; + + return ( + + + + + + {options.map((option) => ( + handleSelect(option.value)} + className={`flex items-center px-2 py-1 cursor-pointer rounded hover:bg-gray-100 ${ + selected === option.value ? "font-bold" : "" + }`} + > + {selected === option.value && ( + + )} + {option.label} + + ))} + + + ); +}; + +export default ReportDropdown; \ No newline at end of file diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 691282a3..e94d15e6 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -9,7 +9,7 @@ import { useGetClaimsByUriQuery } from "~src/graphql/src" const queryClient = new QueryClient() import { normalizeUrl, buildUriRegex } from "../lib/url" import WarningPopup from "~/src/components/WarningPopup" - +import ReportDropdown from "~src/components/ReportDropdown" export const config: PlasmoCSConfig = { matches: ["https://*/*"] @@ -27,6 +27,8 @@ function PlasmoInline() { const draggingRef = useRef(false) const [hovered, setHovered] = useState(false) const [autoVisible, setAutoVisible] = useState(false) + const [showDropdown, setShowDropdown] = useState(false) + const hoverTimeout = useRef(null) const [walletAddress] = useStorage("metamask-account", "") const uri = normalizeUrl(window.location.href) @@ -55,6 +57,18 @@ function PlasmoInline() { return () => clearTimeout(timer) }, []) + const handleIntuitionMouseEnter = () => { + hoverTimeout.current = setTimeout(() => { + setShowDropdown(true) + }, 500) + } + const handleIntuitionMouseLeave = () => { + if (hoverTimeout.current) { + clearTimeout(hoverTimeout.current) + hoverTimeout.current = null + } + } + useEffect(() => { const listener = (msg: any) => { if (msg.action === "REFRESH_CLAIMS") { @@ -91,6 +105,7 @@ function PlasmoInline() { const highlightColor = hasScam ? "red" : hasTrustworthy ? "green" : undefined const showPopup = Boolean(highlightColor) && (hovered || autoVisible) + const isWarningPopupActive = (highlightColor === "red" || highlightColor === "green") && (hovered || autoVisible) const handleMouseDown = (e: React.MouseEvent) => { e.preventDefault() @@ -178,17 +193,26 @@ function PlasmoInline() { onMouseLeave={e => { setHovered(false) e.currentTarget.style.opacity = "0.2" + setShowDropdown(false) }} > - {}} - size={iconSize} - loading={isLoading} - highlightColor={highlightColor} - position={{ x: 0, y: 0 }} - className="hover:opacity-80 transition-opacity" - /> - +
+ {showDropdown && !isWarningPopupActive && } +
+ {}} + size={iconSize} + loading={isLoading} + highlightColor={highlightColor} + position={{ x: 0, y: 0 }} + className="hover:opacity-80 transition-opacity" + /> +
+
{(highlightColor === "red" || highlightColor === "green") && ( Date: Fri, 20 Jun 2025 12:04:11 +0200 Subject: [PATCH 12/22] add iconPlus --- src/background.ts | 26 +++++++- src/components/ReportDropdown.tsx | 72 +++++++++++---------- src/components/content.tsx | 103 +++++++++++++++++------------- src/contents/plasmo-inline.tsx | 31 ++++++++- src/popup/index.tsx | 30 +++++---- src/sidepanel/index.tsx | 17 +++-- 6 files changed, 177 insertions(+), 102 deletions(-) diff --git a/src/background.ts b/src/background.ts index b2e956d9..f9d89f4c 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,15 +1,37 @@ export {} +let pendingSidepanelRoute: string | null = null; +let sidepanelPort: chrome.runtime.Port | null = null; + +chrome.runtime.onConnect.addListener((port) => { + if (port.name === "sidepanel-nav") { + sidepanelPort = port; + port.onMessage.addListener((msg) => { + if (msg === "SIDEPANEL_READY" && pendingSidepanelRoute) { + console.log("[BG] Port: sending NAVIGATE_SIDEPANEL", pendingSidepanelRoute); + port.postMessage({ action: "NAVIGATE_SIDEPANEL", route: pendingSidepanelRoute }); + pendingSidepanelRoute = null; + } + }); + port.onDisconnect.addListener(() => { + sidepanelPort = null; + }); + } +}); + chrome.runtime.onMessage.addListener((message, sender) => { + console.log("[BG] Received message:", message, "from", sender); if (message.type === "open_sidepanel") { const tabId = sender.tab?.id const windowId = sender.tab?.windowId if (!tabId || !windowId) return - chrome.sidePanel.open({ tabId, windowId }) + pendingSidepanelRoute = message.route || null; + + chrome.sidePanel.open({ tabId, windowId }); } -}) +}); chrome.tabs.onUpdated.addListener((tabId, info) => { if (info.status === "complete") { diff --git a/src/components/ReportDropdown.tsx b/src/components/ReportDropdown.tsx index 43d7b9b7..3c256429 100644 --- a/src/components/ReportDropdown.tsx +++ b/src/components/ReportDropdown.tsx @@ -1,50 +1,54 @@ // src/components/ReportDropdown.tsx import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; -import { CheckIcon } from "@radix-ui/react-icons"; -import React, { useState } from "react"; +import React from "react"; + +// Icône chevron bas (SVG inline, pas besoin de dépendance) +const ChevronDown = () => ( + + + +); const options = [ { label: "Trustworthy", value: "trustworthy" }, { label: "Scam", value: "scam" }, ]; -export const ReportDropdown = ({ - onSelect, - defaultValue = "trustworthy", -}: { - onSelect?: (value: string) => void; - defaultValue?: string; -}) => { - const [selected, setSelected] = useState(defaultValue); - - const handleSelect = (value: string) => { - setSelected(value); - onSelect?.(value); - }; - +const ReportDropdown = () => { return ( - - - {options.map((option) => ( - handleSelect(option.value)} - className={`flex items-center px-2 py-1 cursor-pointer rounded hover:bg-gray-100 ${ - selected === option.value ? "font-bold" : "" - }`} - > - {selected === option.value && ( - - )} - {option.label} - - ))} - + + e.stopPropagation()} + onMouseDown={e => e.stopPropagation()} + > + {options.map((option) => ( + e.stopPropagation()} + onMouseDown={e => e.stopPropagation()} + > + {option.label} + + ))} + + ); }; diff --git a/src/components/content.tsx b/src/components/content.tsx index 6391046e..52c85cb5 100644 --- a/src/components/content.tsx +++ b/src/components/content.tsx @@ -4,9 +4,9 @@ import { apolloClient } from '~src/lib/apolo-client' import React, { type ReactNode, useEffect } from "react" import { Route, - BrowserRouter as Router, Routes, - Navigate + Navigate, + useNavigate } from "react-router-dom" import { configureClient } from "~src/graphql/src" @@ -63,57 +63,68 @@ const Content = ({ children }: ContentProps) => { }, []) const { navType } = useNavigation() + const navigate = useNavigate(); + + useEffect(() => { + const port = chrome.runtime.connect({ name: "sidepanel-nav" }); + port.postMessage("SIDEPANEL_READY"); + port.onMessage.addListener((msg) => { + if (msg.action === "NAVIGATE_SIDEPANEL" && msg.route) { + console.log("[SIDEPANEL] NAVIGATE_SIDEPANEL received via port, navigating to:", msg.route); + navigate(msg.route); + } + }); + return () => port.disconnect(); + }, [navigate]); return ( - - - {navType === "classic" && } -
- {children} -
- - } /> - } /> - - }> - } /> - }> - - } /> - } /> - - - - } /> - } /> - - - } /> - } /> + + {navType === "classic" && } +
+ {children} +
+ + } /> + } /> + + }> + } /> + }> + + } /> + } /> - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
-
- {navType === "classic" ? ( - - ) : ( - <> - - - )} - + + } /> + } /> + + + } /> + } /> + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+
+
+ {navType === "classic" ? ( + + ) : ( + <> + + + )}
diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index e94d15e6..7da2714a 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -10,6 +10,8 @@ const queryClient = new QueryClient() import { normalizeUrl, buildUriRegex } from "../lib/url" import WarningPopup from "~/src/components/WarningPopup" import ReportDropdown from "~src/components/ReportDropdown" +import "../styles/global.css" +import IntuitionIconPlus from "~src/components/icons/intuition_icon_plus" export const config: PlasmoCSConfig = { matches: ["https://*/*"] @@ -196,13 +198,38 @@ function PlasmoInline() { setShowDropdown(false) }} > -
+
{showDropdown && !isWarningPopupActive && }
+ {(!data || atoms.length === 0) && ( +
{ + e.stopPropagation(); + console.log("[INLINE] Click on IntuitionIconPlus: sending open_sidepanel with /page-form"); + chrome.runtime.sendMessage({ type: "open_sidepanel", route: "/page-form" }, (response) => { + console.log("[INLINE] open_sidepanel message sent, response:", response); + }); + }} + onMouseDown={e => e.stopPropagation()} + title="Add your Intuition" + > + +
+ )} {}} size={iconSize} diff --git a/src/popup/index.tsx b/src/popup/index.tsx index 59b74820..95f25aec 100644 --- a/src/popup/index.tsx +++ b/src/popup/index.tsx @@ -8,6 +8,8 @@ import GroupParticlesCanvas from "~src/components/ui/ParticulBg/GroupParticlesCa import { ThemeProvider } from "../components/ThemeProvider" import { NavigationProvider } from "~src/components/layout/NavigationProvider" +import { BrowserRouter as Router } from "react-router-dom" + import "../styles/global.css" @@ -26,20 +28,22 @@ function IndexPopup() { return ( -
- - -
- + +
+ + +
+ +
+ +
- - -
+ ) diff --git a/src/sidepanel/index.tsx b/src/sidepanel/index.tsx index 764450f2..aaba1b99 100644 --- a/src/sidepanel/index.tsx +++ b/src/sidepanel/index.tsx @@ -5,9 +5,14 @@ import ParticlesCanvas from "~src/components/ui/ParticulBg/ParticlesCanvas" import GroupParticlesCanvas from "~src/components/ui/ParticulBg/GroupParticlesCanvas" import { ThemeProvider } from "~src/components/ThemeProvider" import { NavigationProvider } from "~src/components/layout/NavigationProvider" +import { BrowserRouter as Router } from "react-router-dom" function IndexSidepanel() { + useEffect(() => { + chrome.runtime.sendMessage({ action: "SIDEPANEL_READY" }); + }, []); + useEffect(() => { umami("Open Side Panel") const port = chrome.runtime.connect({ name: "sidepanel" }) @@ -29,11 +34,13 @@ function IndexSidepanel() { <> -
- - - -
+ +
+ + + +
+
From f4a734a1359abfa2285e4b1302475b2cd016361c Mon Sep 17 00:00:00 2001 From: "James.B" Date: Tue, 24 Jun 2025 11:30:12 +0200 Subject: [PATCH 13/22] add create signal --- src/background.ts | 2 - src/components/ReportDropdown.tsx | 34 +++--- src/contents/plasmo-inline.tsx | 15 ++- src/contents/usePageMetadataContentScript.tsx | 24 +++++ src/hooks/useCreateTriples.ts | 9 ++ src/hooks/useSignalProcess.tsx | 101 ++++++++++++++++++ 6 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 src/contents/usePageMetadataContentScript.tsx create mode 100644 src/hooks/useSignalProcess.tsx diff --git a/src/background.ts b/src/background.ts index f9d89f4c..fad5b8ea 100644 --- a/src/background.ts +++ b/src/background.ts @@ -8,7 +8,6 @@ chrome.runtime.onConnect.addListener((port) => { sidepanelPort = port; port.onMessage.addListener((msg) => { if (msg === "SIDEPANEL_READY" && pendingSidepanelRoute) { - console.log("[BG] Port: sending NAVIGATE_SIDEPANEL", pendingSidepanelRoute); port.postMessage({ action: "NAVIGATE_SIDEPANEL", route: pendingSidepanelRoute }); pendingSidepanelRoute = null; } @@ -20,7 +19,6 @@ chrome.runtime.onConnect.addListener((port) => { }); chrome.runtime.onMessage.addListener((message, sender) => { - console.log("[BG] Received message:", message, "from", sender); if (message.type === "open_sidepanel") { const tabId = sender.tab?.id const windowId = sender.tab?.windowId diff --git a/src/components/ReportDropdown.tsx b/src/components/ReportDropdown.tsx index 3c256429..549584bd 100644 --- a/src/components/ReportDropdown.tsx +++ b/src/components/ReportDropdown.tsx @@ -1,20 +1,22 @@ // src/components/ReportDropdown.tsx import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import React from "react"; +import { useSignalProcess } from "../hooks/useSignalProcess" -// Icône chevron bas (SVG inline, pas besoin de dépendance) const ChevronDown = () => ( ); -const options = [ - { label: "Trustworthy", value: "trustworthy" }, - { label: "Scam", value: "scam" }, -]; +const SignalDropdown = ({ atoms, uri }) => { + const { handleSignal } = useSignalProcess({ + atoms, + uri, + onSuccess: () => {/* popup succès */}, + onError: () => {/* popup erreur */} + }) -const ReportDropdown = () => { return ( @@ -24,33 +26,25 @@ const ReportDropdown = () => { onClick={e => e.stopPropagation()} onMouseDown={e => e.stopPropagation()} > - Report + Signal - + e.stopPropagation()} onMouseDown={e => e.stopPropagation()} > - {options.map((option) => ( - e.stopPropagation()} - onMouseDown={e => e.stopPropagation()} - > - {option.label} - - ))} + handleSignal("scam")}>Scam + handleSignal("trustworthy")}>Trustworthy ); }; -export default ReportDropdown; \ No newline at end of file +export default SignalDropdown; \ No newline at end of file diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 7da2714a..c647bb69 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -22,6 +22,8 @@ export const getInlineAnchor: PlasmoGetInlineAnchor = () => export const getShadowHostId = () => "plasmo-inline-example-unique-id" +export const useShadowDom = false + function PlasmoInline() { const iconSize = 35 const [positionY, setPositionY] = useState(50) @@ -53,6 +55,7 @@ function PlasmoInline() { ); } }; + useEffect(() => { setAutoVisible(true) const timer = setTimeout(() => setAutoVisible(false), 4000) @@ -184,7 +187,7 @@ function PlasmoInline() { color: "white", border: "1px solid #fff", cursor: isHolding ? "grabbing" : "grab", - zIndex: 9999, + zIndex: 1, opacity: 0.2, transition: "opacity 0.3s ease" }} @@ -199,13 +202,19 @@ function PlasmoInline() { }} >
- {showDropdown && !isWarningPopupActive && } + {showDropdown && !isWarningPopupActive && ( + + )} +
- {(!data || atoms.length === 0) && ( + {(!data || atoms.length === 0) && !isLoading && (
{ + const getMeta = (name: string) => + document.querySelector(`meta[name="${name}"]`)?.getAttribute("content") || "" + + setMeta({ + title: document.title, + description: getMeta("description"), + favicon: [...document.querySelectorAll("link[rel~='icon']")].map(el => el.href)[0] || "", + url: window.location.href + }) + }, []) + + return meta +} diff --git a/src/hooks/useCreateTriples.ts b/src/hooks/useCreateTriples.ts index 16daaeec..9fd88bcb 100644 --- a/src/hooks/useCreateTriples.ts +++ b/src/hooks/useCreateTriples.ts @@ -80,11 +80,20 @@ export const useCreateTriples = () => { } }, [triples]) + const createSingleTriple = async (triple: [bigint, bigint, bigint]) => { + addTriple(triple) + await new Promise(resolve => setTimeout(resolve, 0)) + const result = await createTriples() + clearTriples() // <-- Ajoute cette ligne pour vider le state après + return result + } + return { addTriple, removeTriple, clearTriples, createTriples, + createSingleTriple, triples, isLoading, error, diff --git a/src/hooks/useSignalProcess.tsx b/src/hooks/useSignalProcess.tsx new file mode 100644 index 00000000..3b20e0d0 --- /dev/null +++ b/src/hooks/useSignalProcess.tsx @@ -0,0 +1,101 @@ +import { TripleInput, useCreateTriples } from "./useCreateTriples" // ton hook +import { useCreatePosition } from "./useCreatePosition" // ton hook +import { usePinThingMutation } from "@0xintuition/graphql" // pour créer un atom +import { usePageMetadataContentScript as usePageMetadata } from "../contents/usePageMetadataContentScript" +import { Multivault } from "@0xintuition/protocol" +import { getClients } from "../lib/viemClient" + + +export function useSignalProcess({ atoms, uri, onSuccess, onError }) { + const { createTriples, addTriple } = useCreateTriples() + const { createPosition } = useCreatePosition() + const { mutateAsync: pinThing } = usePinThingMutation() + const pageMeta = usePageMetadata() + + const getAtomWithMostVotes = (atoms) => { + if (!atoms.length) { + console.log("[SignalProcess] Aucun atom existant trouvé pour cette URL.") + return null + } + const best = atoms.reduce((mostVotedAtom, currentAtom) => { + const currentVotes = ( + currentAtom.as_object_claims_aggregate?.nodes.length + + currentAtom.as_subject_claims_aggregate?.nodes.length + ) || 0 + const mostVotes = ( + mostVotedAtom.as_object_claims_aggregate?.nodes.length + + mostVotedAtom.as_subject_claims_aggregate?.nodes.length + ) || 0 + return currentVotes > mostVotes ? currentAtom : mostVotedAtom + }) + console.log("[SignalProcess] Atom avec le plus de votes sélectionné :", best) + return best + } + + const getOrCreateAtom = async () => { + let atom = getAtomWithMostVotes(atoms) + if (!atom) { + console.log("[SignalProcess] Création d'un nouvel atom avec les métadonnées :", pageMeta) + try { + // 1. Pin sur IPFS + const result = await pinThing({ + name: pageMeta.title || "Untitled", + description: pageMeta.description || "", + image: pageMeta.favicon || "", + url: pageMeta.url || uri + }) + console.log("[SignalProcess] Résultat de la mutation pinThing :", result) + const ipfsUri = result?.pinThing?.uri + if (!ipfsUri) { + throw new Error("La mutation pinThing n'a pas retourné d'uri IPFS.") + } + + // 2. Création de l'atom sur la blockchain + const { walletClient, publicClient } = await getClients() + const multivault = new Multivault({ walletClient, publicClient }) + const deposit = await multivault.getAtomCost() + const { vaultId, hash } = await multivault.createAtom({ + uri: ipfsUri, + initialDeposit: deposit, + wait: true + }) + atom = { id: vaultId.toString() } + console.log("[SignalProcess] Nouvel atom créé sur la blockchain :", atom) + } catch (err) { + console.error("[SignalProcess] Erreur lors de la création de l'atom :", err) + throw err + } + } + return atom + } + + const handleSignal = async (type: "scam" | "trustworthy") => { + try { + console.log("[SignalProcess] Début du process de signal :", type) + let atom = await getOrCreateAtom() + console.log("[SignalProcess] Atom utilisé pour le triple :", atom) + + const tripleInput: TripleInput = [ + BigInt(atom.id), + 877n, + type === "scam" ? 1775n : 14n + ] + console.log("[SignalProcess] tripleInput :", tripleInput) + + addTriple(tripleInput) + await new Promise(resolve => setTimeout(resolve, 0)) + const { vaultIds } = await createTriples() + console.log("[SignalProcess] Triple créé, vaultIds :", vaultIds) + + await createPosition({ vaultId: vaultIds[0] }) + console.log("[SignalProcess] Position créée sur le vault :", vaultIds[0]) + + onSuccess?.() + } catch (e) { + console.error("[SignalProcess] Erreur dans handleSignal :", e) + onError?.(e) + } + } + + return { handleSignal } +} \ No newline at end of file From 9ebeb885b33cce493333a64fc758daecffaa7c9d Mon Sep 17 00:00:00 2001 From: "James.B" Date: Tue, 24 Jun 2025 15:15:32 +0200 Subject: [PATCH 14/22] fix bug double clic for create triple --- src/hooks/useCreateSingleTriple.ts | 43 +++++++++++++++++++ .../usePageMetadataContentScript.tsx | 0 src/hooks/useSignalProcess.tsx | 20 +++------ src/lib/config.ts | 2 +- 4 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 src/hooks/useCreateSingleTriple.ts rename src/{contents => hooks}/usePageMetadataContentScript.tsx (100%) diff --git a/src/hooks/useCreateSingleTriple.ts b/src/hooks/useCreateSingleTriple.ts new file mode 100644 index 00000000..0ea9ee11 --- /dev/null +++ b/src/hooks/useCreateSingleTriple.ts @@ -0,0 +1,43 @@ +// src/hooks/useCreateSingleTriple.ts +import { useCallback, useState } from "react" +import { Multivault } from "@0xintuition/protocol" +import { getClients } from "../lib/viemClient" + +export function useCreateSingleTriple() { + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState(null) + const [txHash, setTxHash] = useState(null) + const [vaultId, setVaultId] = useState(null) + + const createSingleTriple = useCallback( + async (tripleInput: [bigint, bigint, bigint]) => { + setIsLoading(true) + setError(null) + setTxHash(null) + setVaultId(null) + try { + const { walletClient, publicClient } = await getClients() + const multivault = new Multivault({ walletClient, publicClient }) + const cost = await multivault.getTripleCost() + const { vaultId, hash } = await multivault.createTriple({ + subjectId: tripleInput[0], + predicateId: tripleInput[1], + objectId: tripleInput[2], + initialDeposit: cost, + wait: true + }) + setVaultId(vaultId) + setTxHash(hash) + return { vaultId, hash } + } catch (e: any) { + setError(e.message || "Erreur inconnue") + throw e + } finally { + setIsLoading(false) + } + }, + [] + ) + + return { createSingleTriple, isLoading, error, txHash, vaultId } +} \ No newline at end of file diff --git a/src/contents/usePageMetadataContentScript.tsx b/src/hooks/usePageMetadataContentScript.tsx similarity index 100% rename from src/contents/usePageMetadataContentScript.tsx rename to src/hooks/usePageMetadataContentScript.tsx diff --git a/src/hooks/useSignalProcess.tsx b/src/hooks/useSignalProcess.tsx index 3b20e0d0..3ff44085 100644 --- a/src/hooks/useSignalProcess.tsx +++ b/src/hooks/useSignalProcess.tsx @@ -1,13 +1,13 @@ -import { TripleInput, useCreateTriples } from "./useCreateTriples" // ton hook +import { useCreateSingleTriple } from "./useCreateSingleTriple" // ton hook import { useCreatePosition } from "./useCreatePosition" // ton hook import { usePinThingMutation } from "@0xintuition/graphql" // pour créer un atom -import { usePageMetadataContentScript as usePageMetadata } from "../contents/usePageMetadataContentScript" +import { usePageMetadataContentScript as usePageMetadata } from "./usePageMetadataContentScript" import { Multivault } from "@0xintuition/protocol" import { getClients } from "../lib/viemClient" export function useSignalProcess({ atoms, uri, onSuccess, onError }) { - const { createTriples, addTriple } = useCreateTriples() + const { createSingleTriple } = useCreateSingleTriple() const { createPosition } = useCreatePosition() const { mutateAsync: pinThing } = usePinThingMutation() const pageMeta = usePageMetadata() @@ -37,7 +37,6 @@ export function useSignalProcess({ atoms, uri, onSuccess, onError }) { if (!atom) { console.log("[SignalProcess] Création d'un nouvel atom avec les métadonnées :", pageMeta) try { - // 1. Pin sur IPFS const result = await pinThing({ name: pageMeta.title || "Untitled", description: pageMeta.description || "", @@ -50,7 +49,6 @@ export function useSignalProcess({ atoms, uri, onSuccess, onError }) { throw new Error("La mutation pinThing n'a pas retourné d'uri IPFS.") } - // 2. Création de l'atom sur la blockchain const { walletClient, publicClient } = await getClients() const multivault = new Multivault({ walletClient, publicClient }) const deposit = await multivault.getAtomCost() @@ -75,20 +73,16 @@ export function useSignalProcess({ atoms, uri, onSuccess, onError }) { let atom = await getOrCreateAtom() console.log("[SignalProcess] Atom utilisé pour le triple :", atom) - const tripleInput: TripleInput = [ + const tripleInput: [bigint, bigint, bigint] = [ BigInt(atom.id), 877n, type === "scam" ? 1775n : 14n ] console.log("[SignalProcess] tripleInput :", tripleInput) - addTriple(tripleInput) - await new Promise(resolve => setTimeout(resolve, 0)) - const { vaultIds } = await createTriples() - console.log("[SignalProcess] Triple créé, vaultIds :", vaultIds) - - await createPosition({ vaultId: vaultIds[0] }) - console.log("[SignalProcess] Position créée sur le vault :", vaultIds[0]) + const { vaultId } = await createSingleTriple(tripleInput) + await createPosition({ vaultId }) + console.log("[SignalProcess] Position créée sur le vault :", vaultId) onSuccess?.() } catch (e) { diff --git a/src/lib/config.ts b/src/lib/config.ts index 5f087ee9..c2b950c9 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -2,7 +2,7 @@ import { base, baseSepolia } from "viem/chains" const CURRENT_ENV = process.env.NODE_ENV -export const IS_DEV = CURRENT_ENV !== "production" +export const IS_DEV = CURRENT_ENV === "production" export const SELECTED_CHAIN = IS_DEV ? baseSepolia : base From 616b97bf9b227ee7b885a0a0ce6c87ef9154783c Mon Sep 17 00:00:00 2001 From: "James.B" Date: Tue, 24 Jun 2025 20:58:17 +0200 Subject: [PATCH 15/22] fix bug detection scam --- src/contents/plasmo-inline.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index c647bb69..f3d3c2d5 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -101,11 +101,14 @@ function PlasmoInline() { ...(atom.as_object_claims_aggregate?.nodes ?? []), ...(atom.as_subject_claims_aggregate?.nodes ?? []) ]) + const IS_ID = 877 + const SCAM_ID = 1775 + const TRUSTWORTHY_ID = 14 const hasScam = allClaims.some( - c => c.predicate?.label === "is" && c.object?.label === "Scam" + c => c.predicate?.id == IS_ID && c.object?.id == SCAM_ID ) - const hasTrustworthy = !hasScam && allClaims.some( - c => c.predicate?.label === "is" && c.object?.label === "Trustworthy" + const hasTrustworthy = allClaims.some( + c => c.predicate?.id == IS_ID && c.object?.id == TRUSTWORTHY_ID ) const highlightColor = hasScam ? "red" : hasTrustworthy ? "green" : undefined const showPopup = Boolean(highlightColor) && (hovered || autoVisible) @@ -147,8 +150,8 @@ function PlasmoInline() { } const targetClaim = allClaims.find( - c => c.predicate?.label === (hasScam ? "is" : "is") && - c.object?.label === (hasScam ? "Scam" : "Trustworthy") + c => c.predicate?.id == IS_ID && + (c.object?.id == SCAM_ID || c.object?.id == TRUSTWORTHY_ID) ) console.log("DATA CLAIM SCAM OR TRUST", targetClaim) From cc111694f44ad943e28f6c657674336e756b3269 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 29 Jul 2025 14:53:48 +0200 Subject: [PATCH 16/22] fix bug version-1.5 --- src/contents/plasmo-inline.tsx | 50 +++++++++++---------- src/pages/Home.tsx | 79 ++++++++++------------------------ 2 files changed, 50 insertions(+), 79 deletions(-) diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index f3d3c2d5..042e5241 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -5,20 +5,26 @@ import type { PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo" import React, { useEffect, useRef, useState } from "react" import IntuitionButtonIcon from "~src/components/icons/IntuitionButtonIcon" import { useStorage } from "@plasmohq/storage/dist/hook" -import { useGetClaimsByUriQuery } from "~src/graphql/src" -const queryClient = new QueryClient() +import { useGetTriplesByUriQuery } from "@warzieram/graphql" import { normalizeUrl, buildUriRegex } from "../lib/url" import WarningPopup from "~/src/components/WarningPopup" import ReportDropdown from "~src/components/ReportDropdown" import "../styles/global.css" import IntuitionIconPlus from "~src/components/icons/intuition_icon_plus" +const queryClient = new QueryClient() + export const config: PlasmoCSConfig = { matches: ["https://*/*"] } -export const getInlineAnchor: PlasmoGetInlineAnchor = () => - document.querySelector("body") +export const getInlineAnchor: PlasmoGetInlineAnchor = () => { + const body = document.querySelector("body") + if (!body) { + throw new Error("Body element not found") + } + return body +} export const getShadowHostId = () => "plasmo-inline-example-unique-id" @@ -38,10 +44,10 @@ function PlasmoInline() { const uri = normalizeUrl(window.location.href) const uriRegex = buildUriRegex(uri) - const { data, loading, isLoading, refetch } = useGetClaimsByUriQuery({ + const { data, loading, refetch } = useGetTriplesByUriQuery({ variables: { uriRegex, address: walletAddress - }) + }}) const inject = () => { if (!loading && data) { @@ -98,17 +104,17 @@ function PlasmoInline() { const atoms = data?.atoms ?? [] const allClaims = atoms.flatMap(atom => [ - ...(atom.as_object_claims_aggregate?.nodes ?? []), - ...(atom.as_subject_claims_aggregate?.nodes ?? []) + ...(atom.as_object_triples_aggregate?.nodes ?? []), + ...(atom.as_subject_triples_aggregate?.nodes ?? []) ]) const IS_ID = 877 const SCAM_ID = 1775 const TRUSTWORTHY_ID = 14 const hasScam = allClaims.some( - c => c.predicate?.id == IS_ID && c.object?.id == SCAM_ID + c => c.predicate?.term_id == IS_ID && c.object?.term_id == SCAM_ID ) const hasTrustworthy = allClaims.some( - c => c.predicate?.id == IS_ID && c.object?.id == TRUSTWORTHY_ID + c => c.predicate?.term_id == IS_ID && c.object?.term_id == TRUSTWORTHY_ID ) const highlightColor = hasScam ? "red" : hasTrustworthy ? "green" : undefined const showPopup = Boolean(highlightColor) && (hovered || autoVisible) @@ -150,23 +156,23 @@ function PlasmoInline() { } const targetClaim = allClaims.find( - c => c.predicate?.id == IS_ID && - (c.object?.id == SCAM_ID || c.object?.id == TRUSTWORTHY_ID) + c => c.predicate?.term_id == IS_ID && + (c.object?.term_id == SCAM_ID || c.object?.term_id == TRUSTWORTHY_ID) ) console.log("DATA CLAIM SCAM OR TRUST", targetClaim) - const vaultId = targetClaim?.vault?.id - ? BigInt(targetClaim.vault.id) + const vaultId = targetClaim?.term_id + ? BigInt(targetClaim.term_id) : undefined - const counterVaultId = targetClaim?.counter_vault?.id - ? BigInt(targetClaim.counter_vault.id) + const counterVaultId = targetClaim?.counter_term_id + ? BigInt(targetClaim.counter_term_id) : undefined - const numPositionsFor = targetClaim?.vault?.positions_aggregate.aggregate?.count - const numPositionsAgainst = targetClaim?.counter_vault?.positions_aggregate.aggregate?.count + const numPositionsFor = targetClaim?.term?.positions_aggregate.aggregate?.count + const numPositionsAgainst = targetClaim?.counter_term?.positions_aggregate.aggregate?.count - const userStake = Number(targetClaim?.vault?.positions?.[0]?.shares ?? 0) - const userCounterStake = Number(targetClaim?.counter_vault?.positions?.[0]?.shares ?? 0) + const userStake = Number(targetClaim?.positions?.[0]?.shares ?? 0) + const userCounterStake = Number(targetClaim?.counter_positions?.[0]?.shares ?? 0) const initialVote: VoteChoice | undefined = userStake > 0 @@ -217,7 +223,7 @@ function PlasmoInline() { onMouseLeave={handleIntuitionMouseLeave} style={{ display: "flex", position: "relative" }} > - {(!data || atoms.length === 0) && !isLoading && ( + {(!data || atoms.length === 0) && !loading && (
{}} size={iconSize} - loading={isLoading} + loading={loading} highlightColor={highlightColor} position={{ x: 0, y: 0 }} className="hover:opacity-80 transition-opacity" diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 46f2622c..262ebadf 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -5,48 +5,18 @@ import { Link } from "react-router-dom" import { useTheme } from "~/src/components/ThemeProvider" import { useStorage } from "@plasmohq/storage/dist/hook" import TabSystem from "~/src/components/TabSystem" -import ClaimRowLite from "~src/components/ui/ClaimRowLite"; -import AtomCard from "~src/components/AtomCard"; import EyeComponent from "~/src/components/3D/EyeComponent" import AtomCard from "~src/components/AtomCard" import ClaimRowLite from "~src/components/ui/ClaimRowLite" - -import TabSystem from "../components/TabSystem" - -function normalizeUrl(input: string): string { - try { - const u = new URL(input) - let hostname = u.hostname.toLowerCase() - if (hostname.startsWith("www.")) hostname = hostname.slice(4) - let pathname = u.pathname - if (pathname.endsWith("/") && pathname.length > 1) { - pathname = pathname.slice(0, -1) - } - return `https://${hostname}${pathname}${u.search}${u.hash}` - } catch { - return input - } -} - -function buildUriRegex(rawUrl: string): string { - const canonical = normalizeUrl(rawUrl) - let withoutProto = canonical.replace(/^https?:\/\//, "") - - if (withoutProto.endsWith("/")) { - withoutProto = withoutProto.slice(0, -1) - } - const escaped = withoutProto.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") - - return `^https?:\\/\\/(?:www\\.)?${escaped}\\/?$` -} +import { normalizeUrl } from "../lib/url" function Home() { const [currentUrl, setCurrentUrl] = useState("") const [walletAddress] = useStorage("metamask-account", "") const [activeTab, setActiveTab] = useState("Claims") - const [atomsWithTags, setAtomsWithTags] = useState([]) - const [claims, setClaims] = useState([]) + const [claims, setClaims] = useState([]) + const [atomsWithTags, setAtomsWithTags] = useState([]) const [isLoading, setIsLoading] = useState(true) const refreshActiveTab = async () => { @@ -89,22 +59,22 @@ useEffect(() => { const extractedClaims = Array.from( new Map( atoms - .flatMap((atom) => [ - ...(atom.as_object_claims_aggregate?.nodes ?? []), - ...(atom.as_subject_claims_aggregate?.nodes ?? []), + .flatMap((atom: any) => [ + ...(atom.as_object_triples_aggregate?.nodes ?? []), + ...(atom.as_subject_triples_aggregate?.nodes ?? []), ]) - .map((c) => [c.triple_id, c]) + .map((c: any) => [c.term_id, c]) ).values() ) - const withTags = atoms.map((atom) => { + const withTags = atoms.map((atom: any) => { const tags = atom.as_subject_claims_aggregate?.nodes - ?.filter((c) => c.predicate?.label === "has tag") - .map((c) => c.object) + ?.filter((c: any) => c.predicate?.label === "has tag") + .map((c: any) => c.object) .filter(Boolean) ?? [] const unique = Array.from( - new Map(tags.map((t) => [t.id, t])).values() + new Map(tags.map((t: any) => [t.id, t])).values() ) return { ...atom, tags: unique } @@ -145,18 +115,15 @@ useEffect(() => { label: "Claims", content: (
- {loading ? ( + {isLoading ? ( "Loading..." - ) : typeof data !== "undefined" && claims.length !== 0 ? ( + ) : claims.length !== 0 ? ( claims.map( (claim, index) => ( - console.log(claim), - ( - - ) + ) ) ) : ( @@ -180,14 +147,12 @@ useEffect(() => { label: "Atoms", content: (
- {loading ? ( + {isLoading ? ( "Loading..." - ) : typeof data !== "undefined" && atoms.length != 0 ? ( - atomsWithTags.map((atom) => { - return ( - - ) - }) + ) : atomsWithTags.length !== 0 ? ( + atomsWithTags.map((atom) => ( + + )) ) : (

From e03b4d77a89684d876ccb73a79bd7cbc6f0bec33 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 30 Jul 2025 11:54:46 +0200 Subject: [PATCH 17/22] add getStyle to correct the style of the selector --- src/components/ReportDropdown.tsx | 21 ++++++++++++++++----- src/contents/plasmo-inline.tsx | 9 +++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/components/ReportDropdown.tsx b/src/components/ReportDropdown.tsx index 549584bd..24e5b14f 100644 --- a/src/components/ReportDropdown.tsx +++ b/src/components/ReportDropdown.tsx @@ -1,4 +1,3 @@ -// src/components/ReportDropdown.tsx import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import React from "react"; import { useSignalProcess } from "../hooks/useSignalProcess" @@ -21,13 +20,13 @@ const SignalDropdown = ({ atoms, uri }) => { @@ -39,8 +38,20 @@ const SignalDropdown = ({ atoms, uri }) => { onClick={e => e.stopPropagation()} onMouseDown={e => e.stopPropagation()} > - handleSignal("scam")}>Scam - handleSignal("trustworthy")}>Trustworthy + handleSignal("scam")}> + + + handleSignal("trustworthy")}> + + diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 042e5241..eaa0b344 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -1,7 +1,7 @@ import { ApolloProvider } from "@apollo/client" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { apolloClient } from "../lib/apolo-client" -import type { PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo" +import type { PlasmoGetStyle, PlasmoCSConfig, PlasmoGetInlineAnchor } from "plasmo" import React, { useEffect, useRef, useState } from "react" import IntuitionButtonIcon from "~src/components/icons/IntuitionButtonIcon" import { useStorage } from "@plasmohq/storage/dist/hook" @@ -10,6 +10,7 @@ import { normalizeUrl, buildUriRegex } from "../lib/url" import WarningPopup from "~/src/components/WarningPopup" import ReportDropdown from "~src/components/ReportDropdown" import "../styles/global.css" +import styleText from "data-text:../styles/global.css" import IntuitionIconPlus from "~src/components/icons/intuition_icon_plus" const queryClient = new QueryClient() @@ -28,7 +29,11 @@ export const getInlineAnchor: PlasmoGetInlineAnchor = () => { export const getShadowHostId = () => "plasmo-inline-example-unique-id" -export const useShadowDom = false +export const getStyle: PlasmoGetStyle = () => { + const style = document.createElement("style") + style.textContent = styleText + return style +} function PlasmoInline() { const iconSize = 35 From 146a474e3552f72a9ea26169c7261d82a63d0291 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 30 Jul 2025 15:50:49 +0200 Subject: [PATCH 18/22] fix creation signal --- src/components/ReportDropdown.tsx | 4 ++-- src/components/WarningPopup.tsx | 21 ++++++++++++--------- src/contents/plasmo-inline.tsx | 4 ++-- src/hooks/useSignalProcess.tsx | 26 +++++++++++++------------- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/components/ReportDropdown.tsx b/src/components/ReportDropdown.tsx index 24e5b14f..bc9b88fb 100644 --- a/src/components/ReportDropdown.tsx +++ b/src/components/ReportDropdown.tsx @@ -33,14 +33,14 @@ const SignalDropdown = ({ atoms, uri }) => { e.stopPropagation()} onMouseDown={e => e.stopPropagation()} > handleSignal("scam")}> diff --git a/src/components/WarningPopup.tsx b/src/components/WarningPopup.tsx index 0742bd73..c734e855 100644 --- a/src/components/WarningPopup.tsx +++ b/src/components/WarningPopup.tsx @@ -78,7 +78,8 @@ const WarningPopup: React.FC = ({ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif", letterSpacing: "0.025em", backdropFilter: "blur(8px)", - minWidth: "140px", + width: "180px", + minWidth: undefined, opacity: visible ? 1 : 0, transition: ` opacity ${ANIMATION_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1), @@ -91,19 +92,21 @@ const WarningPopup: React.FC = ({ fontWeight: "600", marginBottom: "8px", textTransform: "uppercase", - fontSize: "0.75rem", + fontSize: "1rem", letterSpacing: "0.05em" }}> {message}

- +
+ +
) } diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index eaa0b344..124fcc27 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -265,8 +265,8 @@ function PlasmoInline() {
{(highlightColor === "red" || highlightColor === "green") && ( { if (!atoms.length) { - console.log("[SignalProcess] Aucun atom existant trouvé pour cette URL.") + console.log("[SignalProcess] No existing atom found for this URL.") return null } const best = atoms.reduce((mostVotedAtom, currentAtom) => { @@ -28,14 +28,14 @@ export function useSignalProcess({ atoms, uri, onSuccess, onError }) { ) || 0 return currentVotes > mostVotes ? currentAtom : mostVotedAtom }) - console.log("[SignalProcess] Atom avec le plus de votes sélectionné :", best) + console.log("[SignalProcess] Atom with the most votes selected:", best) return best } const getOrCreateAtom = async () => { let atom = getAtomWithMostVotes(atoms) if (!atom) { - console.log("[SignalProcess] Création d'un nouvel atom avec les métadonnées :", pageMeta) + console.log("[SignalProcess] Creating a new atom with metadata:", pageMeta) try { const result = await pinThing({ name: pageMeta.title || "Untitled", @@ -43,10 +43,10 @@ export function useSignalProcess({ atoms, uri, onSuccess, onError }) { image: pageMeta.favicon || "", url: pageMeta.url || uri }) - console.log("[SignalProcess] Résultat de la mutation pinThing :", result) + console.log("[SignalProcess] pinThing mutation result:", result) const ipfsUri = result?.pinThing?.uri if (!ipfsUri) { - throw new Error("La mutation pinThing n'a pas retourné d'uri IPFS.") + throw new Error("pinThing mutation did not return an IPFS uri.") } const { walletClient, publicClient } = await getClients() @@ -58,9 +58,9 @@ export function useSignalProcess({ atoms, uri, onSuccess, onError }) { wait: true }) atom = { id: vaultId.toString() } - console.log("[SignalProcess] Nouvel atom créé sur la blockchain :", atom) + console.log("[SignalProcess] New atom created on the blockchain:", atom) } catch (err) { - console.error("[SignalProcess] Erreur lors de la création de l'atom :", err) + console.error("[SignalProcess] Error while creating atom:", err) throw err } } @@ -69,24 +69,24 @@ export function useSignalProcess({ atoms, uri, onSuccess, onError }) { const handleSignal = async (type: "scam" | "trustworthy") => { try { - console.log("[SignalProcess] Début du process de signal :", type) + console.log("[SignalProcess] Starting signal process:", type) let atom = await getOrCreateAtom() - console.log("[SignalProcess] Atom utilisé pour le triple :", atom) + console.log("[SignalProcess] Atom used for triple:", atom) const tripleInput: [bigint, bigint, bigint] = [ - BigInt(atom.id), + BigInt(atom.term_id), 877n, type === "scam" ? 1775n : 14n ] - console.log("[SignalProcess] tripleInput :", tripleInput) + console.log("[SignalProcess] tripleInput:", tripleInput) const { vaultId } = await createSingleTriple(tripleInput) await createPosition({ vaultId }) - console.log("[SignalProcess] Position créée sur le vault :", vaultId) + console.log("[SignalProcess] Position created on vault:", vaultId) onSuccess?.() } catch (e) { - console.error("[SignalProcess] Erreur dans handleSignal :", e) + console.error("[SignalProcess] Error in handleSignal:", e) onError?.(e) } } From a82e4267c1819cdfef1fa424bc88f9f0893a39e7 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 31 Jul 2025 20:54:45 +0200 Subject: [PATCH 19/22] fix shadow dom --- src/components/ReportDropdown.tsx | 4 +++- src/contents/plasmo-inline.tsx | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/ReportDropdown.tsx b/src/components/ReportDropdown.tsx index bc9b88fb..2125b630 100644 --- a/src/components/ReportDropdown.tsx +++ b/src/components/ReportDropdown.tsx @@ -16,6 +16,8 @@ const SignalDropdown = ({ atoms, uri }) => { onError: () => {/* popup erreur */} }) + const shadowRoot = document.getElementById("plasmo-inline-example-unique-id")?.shadowRoot + return ( @@ -31,7 +33,7 @@ const SignalDropdown = ({ atoms, uri }) => { - + Date: Thu, 31 Jul 2025 20:59:42 +0200 Subject: [PATCH 20/22] package version --- package.json | 2 +- src/components/ReportDropdown.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8e89cbd7..0ec4a6c0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "intuition-chrome-extension", "displayName": "Intuition Chrome Extension", - "version": "0.1.46", + "version": "0.1.47", "description": "", "author": "THP-Lab.org", "scripts": { diff --git a/src/components/ReportDropdown.tsx b/src/components/ReportDropdown.tsx index 2125b630..def8b4aa 100644 --- a/src/components/ReportDropdown.tsx +++ b/src/components/ReportDropdown.tsx @@ -12,8 +12,8 @@ const SignalDropdown = ({ atoms, uri }) => { const { handleSignal } = useSignalProcess({ atoms, uri, - onSuccess: () => {/* popup succès */}, - onError: () => {/* popup erreur */} + onSuccess: () => {/* popup success */}, + onError: () => {/* popup error */} }) const shadowRoot = document.getElementById("plasmo-inline-example-unique-id")?.shadowRoot From 83d9caba7563bb9de813ebc4a333d1175758c883 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 31 Jul 2025 21:29:46 +0200 Subject: [PATCH 21/22] clean props warning popup --- src/components/WarningPopup.tsx | 35 ++++++++++++++++++++++----------- src/contents/plasmo-inline.tsx | 6 +----- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/components/WarningPopup.tsx b/src/components/WarningPopup.tsx index c734e855..425aa0ab 100644 --- a/src/components/WarningPopup.tsx +++ b/src/components/WarningPopup.tsx @@ -6,11 +6,7 @@ interface WarningPopupProps { message: string offset: number bgColor?: string - vaultId?: bigint - counterVaultId?: bigint - numPositionsFor?: number - numPositionsAgainst?: number - initialVote?: VoteChoice + targetClaim?: any forceVisible?: boolean } @@ -22,11 +18,7 @@ const WarningPopup: React.FC = ({ message, offset, bgColor = "red", - vaultId, - counterVaultId, - numPositionsFor, - numPositionsAgainst, - initialVote, + targetClaim, forceVisible = false }) => { const [visible, setVisible] = useState(forceVisible) @@ -48,6 +40,25 @@ const WarningPopup: React.FC = ({ const textColor = bgColor === "red" ? "#b50606" : "#228e01" const borderColor = bgColor === "red" ? "#b50606" : "#228e01" + const vaultId = targetClaim?.term_id + ? BigInt(targetClaim.term_id) + : undefined + const counterVaultId = targetClaim?.counter_term_id + ? BigInt(targetClaim.counter_term_id) + : undefined + const numPositionsFor = targetClaim?.term?.positions_aggregate?.aggregate?.count + const numPositionsAgainst = targetClaim?.counter_term?.positions_aggregate?.aggregate?.count + + const userStake = Number(targetClaim?.positions?.[0]?.shares ?? 0) + const userCounterStake = Number(targetClaim?.counter_positions?.[0]?.shares ?? 0) + + const initialVote: VoteChoice | undefined = + userStake > 0 + ? "for" + : userCounterStake > 0 + ? "against" + : undefined + if (!shouldRender) return null return ( @@ -100,8 +111,8 @@ const WarningPopup: React.FC = ({
)} From 776e7c01124c86aa3404a59f24c11fd372cb6b6c Mon Sep 17 00:00:00 2001 From: James Date: Thu, 31 Jul 2025 21:43:09 +0200 Subject: [PATCH 22/22] forget useless code --- src/contents/plasmo-inline.tsx | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/contents/plasmo-inline.tsx b/src/contents/plasmo-inline.tsx index 7ca6a42b..7fa6f6fe 100644 --- a/src/contents/plasmo-inline.tsx +++ b/src/contents/plasmo-inline.tsx @@ -166,26 +166,6 @@ function PlasmoInline() { console.log("DATA CLAIM SCAM OR TRUST", targetClaim) - const vaultId = targetClaim?.term_id - ? BigInt(targetClaim.term_id) - : undefined - const counterVaultId = targetClaim?.counter_term_id - ? BigInt(targetClaim.counter_term_id) - : undefined - const numPositionsFor = targetClaim?.term?.positions_aggregate.aggregate?.count - const numPositionsAgainst = targetClaim?.counter_term?.positions_aggregate.aggregate?.count - - const userStake = Number(targetClaim?.positions?.[0]?.shares ?? 0) - const userCounterStake = Number(targetClaim?.counter_positions?.[0]?.shares ?? 0) - - const initialVote: VoteChoice | undefined = - userStake > 0 - ? "for" - : userCounterStake > 0 - ? "against" - : undefined - - return (