{"id":27232891,"url":"https://github.com/byteflipper-58/markdown-compose","last_synced_at":"2026-05-10T06:43:19.130Z","repository":{"id":283809781,"uuid":"952982565","full_name":"ByteFlipper-58/Markdown-Compose","owner":"ByteFlipper-58","description":"Markdown Compose Lib — это удобная библиотека для рендеринга Markdown в Jetpack Compose. Она позволяет легко интегрировать поддержку Markdown в Android-приложения, обеспечивая гибкость и простоту использования.","archived":false,"fork":false,"pushed_at":"2025-03-29T13:14:48.000Z","size":257,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-29T14:24:44.950Z","etag":null,"topics":["android","android-library","compose-library","jetpack-compose","markdown"],"latest_commit_sha":null,"homepage":"https://byteflipper.web.app/","language":"Kotlin","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/ByteFlipper-58.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}},"created_at":"2025-03-22T09:48:44.000Z","updated_at":"2025-03-29T13:14:51.000Z","dependencies_parsed_at":"2025-03-22T10:40:06.028Z","dependency_job_id":null,"html_url":"https://github.com/ByteFlipper-58/Markdown-Compose","commit_stats":null,"previous_names":["byteflipper-58/markdown-compose"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteFlipper-58%2FMarkdown-Compose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteFlipper-58%2FMarkdown-Compose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteFlipper-58%2FMarkdown-Compose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteFlipper-58%2FMarkdown-Compose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ByteFlipper-58","download_url":"https://codeload.github.com/ByteFlipper-58/Markdown-Compose/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248232380,"owners_count":21069487,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["android","android-library","compose-library","jetpack-compose","markdown"],"created_at":"2025-04-10T14:10:27.662Z","updated_at":"2026-05-10T06:43:19.125Z","avatar_url":"https://github.com/ByteFlipper-58.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Markdown Compose Lib\n\n![Jetpack Compose](https://img.shields.io/badge/Jetpack%20Compose-%2300C853.svg?style=for-the-badge\u0026logo=jetpackcompose\u0026logoColor=white)\n![Kotlin](https://img.shields.io/badge/Kotlin-%230095D5.svg?style=for-the-badge\u0026logo=kotlin\u0026logoColor=white)\n![Coil](https://img.shields.io/badge/Coil-%23FFA500.svg?style=for-the-badge\u0026logo=coil\u0026logoColor=white)\n\n**Markdown Compose Lib** — это удобная библиотека для рендеринга Markdown в Jetpack Compose. Она позволяет легко интегрировать поддержку Markdown в Android-приложения, обеспечивая гибкость, простоту использования и широкие возможности кастомизации.\n\n## 📌 Возможности\n\n✅ Поддержка базовых элементов Markdown:\n- Заголовки (H1-H6)\n- Списки:\n  - Маркированные (`-`, `*`, `+`)\n  - Нумерованные (`1.`, `2.`, ...)\n  - ✅ **Списки задач** (`- [ ] текст`, `- [x] текст`) с **интерактивными чекбоксами**, поддержкой ссылок/сносок и настраиваемым стилем.\n  - ✅ **Списки определений** (термин на одной строке, `:` + определение на следующей).\n  - Вложенные списки\n- Ссылки (`[текст](URL)`)\n- **Жирный текст** (`**текст**`)\n- *Курсив* (`*текст*`)\n- ~~Зачеркнутый текст~~ (`~~текст~~`)\n- `Inline code` (`` `код` ``)\n- Блоки кода с отображением языка, кнопкой копирования, счетчиками строк/символов и настройками стилей (подсветка синтаксиса в планах).\n- \u003e Блочные цитаты (`\u003e текст`)\n- 📊 Таблицы с различными стилями и настраиваемыми границами.\n- Разделители (`---`, `***`, `___`)\n- ✅ **Изображения** (`![alt текст](URL)`) с возможностью кастомизации рендеринга (например, для интеграции Coil/Glide).\n- ✅ **Ссылки-изображения** (`[![alt текст](img URL)](link URL)`).\n- ✅ **Сноски (Footnotes)** (`[^id]` и `[^id]: Text`) с настраиваемым стилем и **прокруткой к определению** при клике.\n- Настраиваемое расстояние между блоками и строками.\n- ✅ **Расширяемый рендеринг:** Возможность передать собственные Composable-функции для отображения конкретных элементов Markdown (заголовков, изображений, блоков кода, списков определений и т.д.).\n- ✅ **Совместимость:** Не требует высокого уровня API Android.\n- 🎨 **Темы оформления** (светлая, тёмная, кастомные стили через `MarkdownStyleSheet`).\n- ⚡ **Оптимизированный рендеринг** (базовый).\n\n*В планах:*\n- Подсветка синтаксиса для блоков кода.\n- Базовая поддержка HTML тегов.\n- Улучшенная поддержка GFM (GitHub Flavored Markdown).\n\n## 🚀 Установка\n\nДобавьте зависимость в `build.gradle.kts` вашего модуля:\n\n```kotlin\ndependencies {\n    // Убедитесь, что путь к проекту ':markdown-compose' корректен\n    implementation(project(\":markdown-compose\"))\n}\n```\n\n## 📖 Пример использования\n\n```kotlin\n// ... внутри вашего Composable\n\n// Состояния для скролла и позиций сносок\nval scrollState = rememberScrollState()\nval footnotePositions = remember { mutableStateMapOf\u003cString, Float\u003e() }\nval coroutineScope = rememberCoroutineScope()\n\n// Пример состояния для отслеживания изменений в Task List\nvar markdownContent by remember { mutableStateOf(\"\"\"\n    # Пример Markdown\n    Это **жирный** текст и *курсив*.\n\n    - [x] Выполненная задача со [ссылкой](https://example.com)\n    - [ ] Невыполненная задача со сноской[^task]\n    - [ ] Еще одна задача\n\n    Вот еще одна сноска[^1].\n\n    [^1]: Описание первой сноски.\n    [^task]: Сноска для задачи.\n\"\"\".trimIndent()) }\n\n// Оберните MarkdownText в скроллируемый контейнер\nColumn(modifier = Modifier.verticalScroll(scrollState)) {\n    MarkdownText(\n        markdown = markdownContent, // Используем состояние\n        modifier = Modifier.fillMaxWidth().padding(16.dp),\n        footnotePositions = footnotePositions, // Передаем карту для записи позиций\n        onFootnoteReferenceClick = { identifier -\u003e\n            // Ищем позицию по ID сноски\n            footnotePositions[identifier]?.let { position -\u003e\n                // Запускаем плавный скролл\n                coroutineScope.launch {\n                    scrollState.animateScrollTo(position.toInt())\n                }\n            }\n        },\n        onTaskCheckedChange = { taskNode: TaskListItemNode, isChecked: Boolean -\u003e\n            // Обработка изменения состояния чекбокса\n            // ВАЖНО: Эта лямбда только уведомляет. Вам нужно обновить исходный markdownContent.\n            // Это ПРОСТОЙ пример обновления строки. В реальном приложении может потребоваться\n            // более сложная логика для поиска и замены строки задачи.\n            val taskText = taskNode.content.joinToString(\"\") { node -\u003e\n                 // Упрощенное получение текста узла (может быть неточным для сложных inline)\n                 when(node) {\n                     is TextNode -\u003e node.text\n                     is BoldTextNode -\u003e node.text\n                     is ItalicTextNode -\u003e node.text\n                     is StrikethroughTextNode -\u003e node.text\n                     is CodeNode -\u003e \"`\" + node.code + \"`\"\n                     is LinkNode -\u003e \"[${node.text}](${node.url})\"\n                     is FootnoteReferenceNode -\u003e \"[^${node.identifier}]\"\n                     else -\u003e \"\"\n                 }\n            }\n            val oldTaskLine = \"- [${if (isChecked) \" \" else \"x\"}] $taskText\"\n            val newTaskLine = \"- [${if (isChecked) \"x\" else \" \"}] $taskText\"\n\n            if (markdownContent.contains(oldTaskLine)) {\n                 markdownContent = markdownContent.replace(oldTaskLine, newTaskLine)\n                 println(\"Task state changed: $newTaskLine\")\n            } else {\n                 println(\"Warning: Could not find task line to update: $oldTaskLine\")\n            }\n        }\n        // Можно передать кастомные renderers и styleSheet здесь\n        // renderers = customRenderers,\n        // styleSheet = customStyleSheet\n    )\n}\n```\n\n### Пример списка определений\n\n```markdown\nТермин 1\n: Определение 1\n\nТермин 2 с `кодом`\n: Определение 2а\n: Определение 2б\n```\n\n## 🎨 Кастомизация стилей (`MarkdownStyleSheet`)\n\nВы можете легко настроить внешний вид элементов, передав собственный `MarkdownStyleSheet`. Используйте `defaultMarkdownStyleSheet()` как основу и модифицируйте нужные стили с помощью `copy()`:\n\n```kotlin\n// ...\n\nval customStyleSheet = defaultMarkdownStyleSheet().copy(\n    textStyle = TextStyle(fontSize = 15.sp, color = MaterialTheme.colorScheme.onBackground),\n    headerStyle = defaultMarkdownStyleSheet().headerStyle.copy(\n        h1 = TextStyle(fontSize = 30.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.primary),\n        bottomPadding = 12.dp\n    ),\n    codeBlockStyle = defaultMarkdownStyleSheet().codeBlockStyle.copy(\n        textStyle = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 13.sp),\n        codeBackground = Color.DarkGray.copy(alpha = 0.1f)\n    ),\n    inlineCodeStyle = SpanStyle(\n        fontFamily = FontFamily.Monospace,\n        background = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),\n        fontSize = 13.sp\n    ),\n    taskListItemStyle = defaultMarkdownStyleSheet().taskListItemStyle.copy(\n        checkedTextStyle = SpanStyle(textDecoration = TextDecoration.LineThrough, color = Color.Gray)\n    ),\n    footnoteReferenceStyle = SpanStyle( // Стиль для ссылки [1]\n        color = MaterialTheme.colorScheme.secondary,\n        baselineShift = BaselineShift.Superscript, // Делаем верхним индексом\n        fontSize = 12.sp // Меньше размер\n    ),\n    footnoteDefinitionStyle = TextStyle( // Стиль для текста определения [1]: ...\n        fontStyle = FontStyle.Italic,\n        color = MaterialTheme.colorScheme.onSurfaceVariant,\n        fontSize = 14.sp\n    ),\n    definitionListStyle = defaultMarkdownStyleSheet().definitionListStyle.copy(\n        termTextStyle = TextStyle(fontWeight = FontWeight.Bold),\n        detailsIndent = 20.dp,\n        itemSpacing = 10.dp\n    )\n)\n\n// Затем передайте его в MarkdownText:\n// MarkdownText(..., styleSheet = customStyleSheet)\n```\n\n## 🔧 Расширяемый рендеринг (`MarkdownRenderers`)\n\nДля полной кастомизации отображения конкретных элементов (например, для использования Coil для загрузки изображений или добавления своей подсветки кода), вы можете передать собственный объект `MarkdownRenderers`.\n\n```kotlin\n// ...\n\nval customRenderers = defaultMarkdownRenderers().copy(\n    // Переопределяем рендеринг изображений для использования Coil\n    renderImage = { node: ImageNode, styleSheet: MarkdownStyleSheet, modifier: Modifier -\u003e\n        AsyncImage(\n            model = node.url,\n            contentDescription = node.altText,\n            modifier = modifier\n                .fillMaxWidth() // Пример модификатора\n                .then(styleSheet.imageStyle.modifier), // Применяем стили из StyleSheet\n            contentScale = styleSheet.imageStyle.contentScale,\n            placeholder = styleSheet.imageStyle.placeholder,\n            error = styleSheet.imageStyle.error\n        )\n    },\n    // Можно переопределить и другие рендереры, например, renderCodeBlock, renderDefinitionList\n    // renderCodeBlock = { node, styleSheet, modifier -\u003e /* Ваша реализация */ },\n    // renderDefinitionList = { node, styleSheet, modifier, footnoteMap, linkHandler, footnoteClickHandler -\u003e /* Ваша реализация */ }\n)\n\n// Затем передайте его в MarkdownText:\n// MarkdownText(..., renderers = customRenderers)\n\n```\n\n## 🤝 Вклад в проект\n\n1. Форкните репозиторий\n2. Создайте новую ветку (`git checkout -b feature/your-feature-name`)\n3. Внесите изменения и закоммитьте их (`git commit -m 'Add feature'`)\n4. Отправьте изменения (`git push origin feature/your-feature-name`)\n5. Откройте пулл-реквест\n\n⭐ Если вам понравился проект, поддержите его звездочкой на GitHub!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyteflipper-58%2Fmarkdown-compose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyteflipper-58%2Fmarkdown-compose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyteflipper-58%2Fmarkdown-compose/lists"}