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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chilled-kids-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@mod-protocol/react-editor": patch
---

feat: expose tiptap editor config
8 changes: 8 additions & 0 deletions .changeset/fair-tigers-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@mod-protocol/react-ui-shadcn": patch
"@miniapps/url-render": patch
"@mod-protocol/react": patch
"@mod-protocol/core": patch
---

feat: text component variants: `bold`, `secondary`, `regular`
6 changes: 6 additions & 0 deletions .changeset/modern-ways-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@mod-protocol/core": minor
"api": minor
---

chore: users mentioned in NFT metadata can either be an FID or FarcasterUser object depending on context
5 changes: 5 additions & 0 deletions .changeset/small-houses-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@mod-protocol/react-editor": patch
---

fix: placeholder text
33 changes: 19 additions & 14 deletions docs/pages/integrate/creation.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Image from 'next/image'
import Image from "next/image";

# Creation Mods

Expand Down Expand Up @@ -56,15 +56,26 @@ import {
import { CreationMod } from "@mod-protocol/react";
import { useEditor, EditorContent } from "@mod-protocol/react-editor";
import { creationMods } from "@mod-protocol/mod-registry";
import { Embed, ModManifest, fetchUrlMetadata, handleAddEmbed, handleOpenFile, handleSetInput } from "@mod-protocol/core";
import {
Embed,
ModManifest,
fetchUrlMetadata,
handleAddEmbed,
handleOpenFile,
handleSetInput,
} from "@mod-protocol/core";
// UI implementation
import { createRenderMentionsSuggestionConfig } from "@mod-protocol/react-ui-shadcn/dist/lib/mentions";
import { ModsSearch } from "@mod-protocol/react-ui-shadcn/dist/components/creation-mods-search";
import { CastLengthUIIndicator } from "@mod-protocol/react-ui-shadcn/dist/components/cast-length-ui-indicator";
import { ChannelPicker } from "@mod-protocol/react-ui-shadcn/dist/components/channel-picker";
import { EmbedsEditor } from "@mod-protocol/react-ui-shadcn/dist/lib/embeds";
import { Button } from "@mod-protocol/react-ui-shadcn/dist/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@mod-protocol/react-ui-shadcn/dist/components/ui/popover";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@mod-protocol/react-ui-shadcn/dist/components/ui/popover";
import { renderers } from "@mod-protocol/react-ui-shadcn/dist/renderers";

// Optionally replace with your API_URL here if you want to self host by running your own instance of https://github.com/mod-protocol/mod/tree/main/examples/api
Expand Down Expand Up @@ -112,8 +123,7 @@ export default function EditorExample() {
}),
});

const [currentMod, setCurrentMod] =
React.useState<ModManifest | null>(null);
const [currentMod, setCurrentMod] = React.useState<ModManifest | null>(null);

return (
<form onSubmit={handleSubmit}>
Expand All @@ -138,15 +148,10 @@ export default function EditorExample() {
}}
>
<PopoverTrigger></PopoverTrigger>
<ModsSearch
mods={creationMods}
onSelect={setCurrentMod}
/>
<ModsSearch mods={creationMods} onSelect={setCurrentMod} />
<PopoverContent className="w-[400px] ml-2" align="start">
<div className="space-y-4">
<h4 className="font-medium leading-none">
{currentMod?.name}
</h4>
<h4 className="font-medium leading-none">{currentMod?.name}</h4>
<hr />
<CreationMod
input={getText()}
Expand Down Expand Up @@ -192,7 +197,7 @@ You can choose to filter or select which Mods are available
```tsx
...
<ModsSearch
mods={creationMods.filter(mod => ['infura-ipfs-upload', 'livepeer-video'].includes(mod.slug)}
mods={creationMods.filter(mod => ['infura-ipfs-upload', 'livepeer-video'].includes(mod.slug))}
onSelect={setCurrentMod}
/>
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export async function POST(request: NextRequest) {
"nft_collections.name as collection_name",
"nft_collections.open_sea_url as collection_open_sea_url",
"nft_collections.owner_count as collection_owner_count",
"nft_collections.creator_fid as collection_creator_fid",

// NFT metadata
"nft_metadata.token_id as nft_token_id",
Expand Down
5 changes: 4 additions & 1 deletion examples/api/src/app/api/cast-embeds-metadata/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ function formatRow(row): EmbedWithCastHash {
name: row.collection_name,
openSeaUrl: row.collection_open_sea_url || undefined,
ownerCount: row.collection_owner_count || undefined,
creator: undefined, // TODO: Look up farcaster user by FID
creator: row.collection_creator_fid
? { fid: row.collection_creator_fid }
: undefined,
},
};
}
Expand Down Expand Up @@ -132,6 +134,7 @@ export async function POST(request: NextRequest) {
"nft_collections.name as collection_name",
"nft_collections.open_sea_url as collection_open_sea_url",
"nft_collections.owner_count as collection_owner_count",
"nft_collections.creator_fid as collection_creator_fid",
Comment thread
stephancill marked this conversation as resolved.

// NFT metadata
"nft_metadata.token_id as nft_token_id",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ async function localFetchHandler(url: string): Promise<UrlMetadata> {
return null;
}

if (!(urlMetadata.image || urlMetadata.logo)) {
console.error(`[Local Fetch] No image or logo found for ${url}`);
return null;
}

return urlMetadata;
}

Expand Down
9 changes: 6 additions & 3 deletions examples/api/src/app/api/open-graph/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,18 @@ async function fetchUserData(address: string): Promise<FarcasterUser | null> {
}

export function toUrlMetadata(nftMetadata: NFTMetadata): UrlMetadata {
// If the creator is an object, it's a FarcasterUser
const creator = nftMetadata?.collection?.creator;

return {
image: nftMetadata.mediaUrl ? { url: nftMetadata.mediaUrl } : undefined,
description: nftMetadata.collection.description,
alt: nftMetadata.collection.name,
title: nftMetadata.collection.name,
publisher: nftMetadata.collection.creator?.displayName,
logo: nftMetadata.collection.creator
publisher: creator?.displayName,
logo: creator
? {
url: nftMetadata.collection.creator.pfp.url,
url: creator.pfp.url,
}
: undefined,
nft: nftMetadata,
Expand Down
18 changes: 15 additions & 3 deletions examples/nextjs-shadcn/src/app/editor-example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
getFarcasterMentions,
getMentionFidsByUsernames,
} from "@mod-protocol/farcaster";
import { CreationMod } from "@mod-protocol/react";
import { CreationMod, RichEmbed } from "@mod-protocol/react";
import { useEditor, EditorContent } from "@mod-protocol/react-editor";
import { creationMods } from "@mod-protocol/mod-registry";
import { creationMods, defaultRichEmbedMod } from "@mod-protocol/mod-registry";
import {
Embed,
EthPersonalSignActionResolverInit,
Expand Down Expand Up @@ -164,7 +164,19 @@ export default function EditorExample() {
autoFocus
className="w-full h-full min-h-[200px]"
/>
<EmbedsEditor embeds={getEmbeds()} setEmbeds={setEmbeds} />
<EmbedsEditor
embeds={getEmbeds()}
setEmbeds={setEmbeds}
RichEmbed={({ embed }) => (
<RichEmbed
api={API_URL}
defaultRichEmbedMod={defaultRichEmbedMod}
mods={[defaultRichEmbedMod]}
embed={embed}
renderers={renderers}
/>
)}
/>
</div>
<div className="flex flex-row pt-2 gap-1">
<ChannelPicker
Expand Down
49 changes: 29 additions & 20 deletions examples/nextjs-shadcn/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,76 +1,85 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;

--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;

--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;

--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;

--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;

--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;

--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;

--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;

--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;

--radius: 0.5rem;
}

.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;

--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;

--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;

--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;

--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;

--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;

--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;

--destructive: 0 42.8% 40.6%;
--destructive-foreground: 210 40% 98%;

--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: hsl(212.7,26.8%,83.9);
--ring: hsl(212.7, 26.8%, 83.9);
}
}

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

/* https://github.com/ueberdosis/tiptap/issues/2659#issuecomment-1230954941 */
.ProseMirror p.is-editor-empty:first-child::before {
Comment thread
stephancill marked this conversation as resolved.
content: attr(data-placeholder);
float: left;
pointer-events: none;
height: 0;
@apply text-muted-foreground;
}
}
11 changes: 11 additions & 0 deletions examples/nextjs-shadcn/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ module.exports = {
// make sure tailwind doesn't tree shake classes used by the library
"../../packages/react-ui-shadcn/dist/**/*.{ts,tsx,css,js}",
],
// Placeholder styles are not included in bundle by default because
// the element is rendered dynamically in the editor, so must be manually
// added here
safelist: [
"cursor-text",
"before:content-[attr(data-placeholder)]",
"before:absolute",
"before:text-foreground-secondary",
"before:opacity-50",
"before:pointer-events-none",
],
theme: {
container: {
center: true,
Expand Down
9 changes: 8 additions & 1 deletion mods/url-render/src/fullimage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ const fullimage: ModElement[] = [
onsuccess: "#fullimage",
},
elements: [
{
type: "text",
label: "{{embed.metadata.publisher}}",
variant: "secondary",
},
{
type: "text",
label: "{{embed.metadata.title}}",
variant: "regular",
},
{
type: "text",
label: "{{embed.metadata.publisher}}",
label: "{{embed.metadata.description}}",
variant: "secondary",
},
],
},
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/embeds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type FarcasterUser = {
};

export type NFTMetadata = {
owner?: FarcasterUser;
owner?: Partial<FarcasterUser>;
tokenId?: string;
mediaUrl?: string;
mintUrl?: string;
Expand All @@ -47,7 +47,7 @@ export type NFTMetadata = {
description?: string;
imageUrl?: string;
openSeaUrl?: string;
creator?: FarcasterUser;
creator?: Partial<FarcasterUser>;
};
};

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export type ModElement =
| {
type: "text";
label: string;
variant?: "bold" | "secondary" | "regular";
}
| {
type: "image";
Expand Down
Loading