{"id":41499574,"url":"https://github.com/fatemehkarimi/theodore","last_synced_at":"2026-02-21T19:25:20.850Z","repository":{"id":281544197,"uuid":"944640729","full_name":"fatemehkarimi/theodore","owner":"fatemehkarimi","description":"Emoji-friendly content editable editor 🤪🥰👌","archived":false,"fork":false,"pushed_at":"2025-12-26T17:44:26.000Z","size":22989,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-28T06:57:49.088Z","etag":null,"topics":["contenteditable","editor","emoji","react"],"latest_commit_sha":null,"homepage":"https://playground.theodore-js.dev/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fatemehkarimi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-07T17:50:44.000Z","updated_at":"2025-12-12T11:27:55.000Z","dependencies_parsed_at":"2025-10-17T12:37:58.839Z","dependency_job_id":"502409bb-0f44-4cc9-9323-0c98f59e1180","html_url":"https://github.com/fatemehkarimi/theodore","commit_stats":null,"previous_names":["fatemehkarimi/theodore"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/fatemehkarimi/theodore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatemehkarimi%2Ftheodore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatemehkarimi%2Ftheodore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatemehkarimi%2Ftheodore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatemehkarimi%2Ftheodore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fatemehkarimi","download_url":"https://codeload.github.com/fatemehkarimi/theodore/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fatemehkarimi%2Ftheodore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28698344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T17:25:48.045Z","status":"ssl_error","status_checked_at":"2026-01-23T17:25:47.153Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["contenteditable","editor","emoji","react"],"created_at":"2026-01-23T19:03:26.389Z","updated_at":"2026-01-23T19:03:28.728Z","avatar_url":"https://github.com/fatemehkarimi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Theodore\n\nTheodore is an emoji‑friendly, content‑editable text input for React. It replaces native emoji characters with your own images so they render consistently across browsers.\n\n- Consistent emoji rendering across platforms and browsers\n- Bring your own emoji assets (e.g., Apple/Google/Twitter sets) and rendering\n- Ctrl+Z works very well after inserting text or emoji\n\nTry it in the playground: [Playground demo](https://playground.theodore-js.dev).\n\n### Installation\n\n```\nnpm install theodore-js\n```\n\n**Important:** You must import the CSS file for Theodore to work correctly:\n\n```tsx\nimport 'theodore-js/style.css';\n```\n\n### Quick start\n\nBelow is a minimal example showing how to:\n\n- Provide a `renderEmoji` function that maps a native emoji to your image asset\n- Use a ref to call `insertEmoji` when an emoji is selected\n\n```tsx\nimport React, { useRef } from 'react';\nimport { Theodore, TheodoreHandle, useEditorState } from 'theodore-js';\nimport 'theodore-js/style.css';\n\nconst renderEmoji = (emoji: string) =\u003e {\n  if (emoji === '') return \u003c\u003e\u003c/\u003e;\n  const unified = nativeToUnified(emoji);\n  const path = `/img-apple-64/${unified}.png`;\n  return \u003cimg src={path} width={22} height={22} alt={emoji} /\u003e;\n};\n\nexport const TheodoreTextInput: React.FC = () =\u003e {\n  const theodoreRef = useRef\u003cTheodoreHandle\u003e(null);\n  const editorState = useEditorState();\n\n  const handleSelectEmoji = (emoji: { native: string }) =\u003e {\n    theodoreRef.current?.insertEmoji(emoji.native);\n  };\n\n  return (\n    \u003c\u003e\n      \u003cTheodore\n        ref={theodoreRef}\n        editorState={editorState}\n        renderEmoji={renderEmoji}\n        placeholder=\"Write something...\"\n        maxLines={5}\n      /\u003e\n      {/* Your emoji picker should call handleSelectEmoji with a native emoji */}\n      {/* \u003cEmojiPicker onSelectEmoji={handleSelectEmoji} /\u003e */}\n    \u003c/\u003e\n  );\n};\n```\n\nNotes:\n\n- `renderEmoji` is required and tells Theodore how to render each emoji you type/paste.\n- Theodore does not ship emoji images; point `renderEmoji` to your own assets.\n- Emojis passed to `renderEmoji` and returned from your emoji picker are native characters (e.g., \"😀\"). If your assets are named using unified codepoints (e.g., `1f600`), convert native to unified yourself and map to your asset paths however you prefer. The example uses a `nativeToUnified(emoji)` helper for illustration; Theodore only provides the native string.\n- Use the ref (`TheodoreHandle`) to programmatically insert emojis with `insertEmoji(native)`.\n- For correct copy/cut behavior: set the image `alt` attribute to the native emoji (e.g., `alt=\"😀\"`). Browsers use an image's `alt` text when producing plain‑text clipboard data, so this ensures copying the editor content yields the emoji character.\n\n### Props\n\n- **editorState (required, `EditorState`)**: The editor state returned by `useEditorState([onSelectionChange])`. Holds the tree, history, and selection. Create once and pass the same instance to `\u003cTheodore /\u003e`.\n- **renderEmoji (required, `RenderEmoji`)**: `(emoji: string) =\u003e ReactElement`. Receives a native emoji character and returns the React element to render (e.g., an `\u003cimg /\u003e`). Used whenever emojis are typed, pasted, or inserted programmatically.\n- **placeholder (`string | React.ReactNode`)**: Content shown only when the editor is empty. If a string, it is wrapped in a styled placeholder container; if a React node, it is rendered as-is when empty.\n- **className (`string`)**: Extra class for the contentEditable div.\n- **wrapperClassName (`string`)**: Extra class for the contentEditable wrapper `div`.\n- **placeholderClassName (`string`)**: Extra class applied to the placeholder element.\n- **maxLines (`number`)**: Maximum visible line count before the editor scrolls.\n- **onSelectionChange (`(selection: EditorSelection) =\u003e void`)**: Optional selection change callback. Provide it to `useEditorState(onSelectionChange)` when creating `editorState` to receive updates.\n- **...div props**: All other `div` props (except `contentEditable`) are forwarded to the contentEditable element (e.g., `aria-*`, `data-*`, `onFocus`, `onBlur`).\n\n### Converting to plain text\n\nIf you need the plain text representation of the editor state:\n\n```tsx\nimport { convertTreeToText } from 'theodore-js';\n\nconst text = convertTreeToText(editorState.tree);\n```\n\nThat will produce a string with the emoji characters restored.\nSee a working example of the component in the source: [Playground source on GitHub](https://github.com/fatemehkarimi/theodore/tree/master/playground).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffatemehkarimi%2Ftheodore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffatemehkarimi%2Ftheodore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffatemehkarimi%2Ftheodore/lists"}