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
3 changes: 1 addition & 2 deletions .agents/roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ The receiving model must start by identifying its active role and must end with
| --- | --- | --- |
| Issue planning | Planner | Add GitHub/CI Analyst when live issue or PR state is the source of truth. |
| Swift implementation | Planner, Implementer, Code Reviewer, Verification Runner | Add Architecture Watcher when boundary or dependency risk exists. |
| Module, DI, SDK, Widget, StorePattern, or architecture docs | Planner, Architecture Watcher, Implementer, Code Reviewer, Verification Runner | Architecture Watcher must read `AGENTS.md`, `.gemini/styleguide.md`, `README.md`, and `.agents/rules/architecture.md`. |
| Module, DI, SDK, Widget, StorePattern, or architecture docs | Planner, Architecture Watcher, Implementer, Code Reviewer, Verification Runner | Architecture Watcher must read `AGENTS.md`, `README.md`, and `.agents/rules/architecture.md`. |
| Review feedback | GitHub/CI Analyst, Planner, Implementer, Code Reviewer, Verification Runner | Use thread-aware review inspection when unresolved review threads matter. |
| CI failure | GitHub/CI Analyst, Planner, Verification Runner | Add Implementer only after the failure source is identified. |
| PR or release text | Documentation Writer | Add Code Reviewer when text must match actual diff. |
Expand Down Expand Up @@ -250,7 +250,6 @@ Use it when a task touches module boundaries, file ownership, layer dependencies
Must read before reviewing:

- `AGENTS.md`
- `.gemini/styleguide.md`
- `README.md`
- `.agents/rules/architecture.md`
- `.agents/rules/project-workflows.md` when PR, commit, Xcode project, CI, widget, Store, localization, release, or build tooling is involved
Expand Down
16 changes: 14 additions & 2 deletions .agents/rules/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ This repository is a Tuist-generated, workspace-based modular iOS app. There is

Read this file before work that changes any of these areas:

- Module boundaries or file ownership across `Application/*` and `Widget/*` targets.
- Module boundaries or file ownership across `Application/*`, `Libraries/*`, and `Widget/*` targets.
- Swift imports or Tuist target dependencies.
- DI assembler wiring or same-layer dependency injection.
- Repository, service, store, or use case contracts.
- Firebase, social login, network, link metadata, notification, or WidgetKit dependency placement.
- Widget snapshot, App Group, or widget deep-link data flow.
- Architecture diagrams, README architecture text, or PR architecture explanations.

Before editing, also read `.gemini/styleguide.md` and `README.md`. Read `.agents/rules/project-workflows.md` when the task involves PR review, commits, Xcode project files, CI, widgets, Store reducers, localization, release, or build tooling.
Before editing, also read `README.md`. Read `.agents/rules/project-workflows.md` when the task involves PR review, commits, Xcode project files, CI, widgets, Store reducers, localization, release, or build tooling.

Then inspect the concrete files, Swift imports, and Tuist target dependencies related to the requested change. Do not rely on layer names alone.

Expand Down Expand Up @@ -118,6 +118,7 @@ flowchart TD
Core["Core\nDI\nLogger\nShared value/query types\nLightweight widget values"]
WidgetCore["WidgetCore\nWidget snapshot models\nFactories\nApp Group constants"]
WidgetExtension["WidgetExtension\nWidgetKit UI\nProviders\nTimelines"]
MarkdownRenderer["MarkdownRenderer\nSwiftUI renderer API\nInternal WebKit bridge\nRenderer resources and Tooling"]

App --> Presentation
App --> Domain
Expand All @@ -131,6 +132,7 @@ flowchart TD

Presentation --> Domain
Presentation --> Core
Presentation --> MarkdownRenderer

Domain --> Core

Expand Down Expand Up @@ -161,6 +163,7 @@ flowchart TD
| `Infra` | Firebase, social login, network, metadata, messaging implementations | Data, Core | Moving SDK-specific behavior out of Infra; adding any Domain dependency, source import, or SDK service contract coupling |
| `Persistence` | local stores, image cache, non-widget app persistence | Data, Core | Adding WidgetCore, WidgetKit reload, Widget, widget snapshot generation, or widget bridge ownership |
| `Presentation` | UI, view models, coordinators, presentation state, narrow presentation-scoped platform side effects | Domain, Core | Adding Data, Infra, Persistence, or App dependency; expanding platform service ownership beyond UI-side effects |
| `MarkdownRenderer` | public SwiftUI renderer and reference value, internal WebKit bridge, renderer resources, TypeScript Tooling, renderer tests | system frameworks only | Adding a DevLog application layer dependency, exposing WebKit bridge types, adding another Presentation importer, or re-exporting the module |
| `Widget` | app-side widget bridge, sync bus implementation, sync/session handlers, snapshot generation/persistence orchestration, WidgetKit reload bridge, widget assembler | Data, Core, WidgetCore | Adding Domain, Infra, Persistence, Presentation, or App dependency |
| `App` | composition root, lifecycle, assembler wiring, app target ownership for widget extension embedding | Concrete app layers | Moving feature logic into App |
| `WidgetCore` | widget snapshot models, factories, app-group keys/defaults store, deep links, pure snapshot logic | Core | Adding Domain, Data, Infra, Persistence, Presentation, App, or Widget dependency |
Expand All @@ -175,6 +178,15 @@ flowchart TD
- `PresentationShared` owns shared Todo, Search, Loading UI, and presentation contracts.
- `App` owns composition root, lifecycle, and assembler wiring. It must not take ownership of presentation feature or root flows.

## MarkdownRenderer module boundary

- `Libraries/MarkdownRenderer` owns `MarkdownRendererView`, `MarkdownRendererReference`, the internal `MarkdownWebView` and Coordinator, URL/message policy, renderer resources, renderer tests, and TypeScript Tooling.
- `MarkdownRenderer` may depend on system frameworks such as `SwiftUI`, `WebKit`, `Foundation`, and `CoreGraphics`. It must not import `Core`, `Domain`, `Data`, `Infra`, `Persistence`, `Presentation`, `App`, `Widget`, or `WidgetCore`.
- `PresentationShared` depends on `MarkdownRenderer`. `Application/Presentation/PresentationShared/Sources/Common/TodoMarkdownContentView.swift` is the only direct Presentation importer and must not use `@_exported import MarkdownRenderer`.
- `TodoMarkdownContentView` owns `TodoReferenceItem` conversion, symbol image data URL creation, tab bar and safe-area adaptation, and `onOpenTodoID` callback adaptation. These DevLog concerns must not move into `MarkdownRenderer`.
- `MarkdownRendererView` owns color scheme, locale, external URL opening, scaled font size, and the public renderer input. The internal `MarkdownWebView` keeps `WKWebView` lifecycle, message handling, internal scroll ownership, and `obscuredContentInsets.bottom` handling out of the public API.
- `Libraries/MarkdownRenderer/Tooling` generates the tracked files under `Libraries/MarkdownRenderer/Resources/MarkdownRenderer`. CI must verify that generated and tracked resources remain synchronized.

## Layer-internal dependency injection

Do not inject dependencies between types that belong to the same layer.
Expand Down
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# 컴파일된 JavaScript 문자열의 공백을 보존
Application/Presentation/PresentationShared/Resources/MarkdownRenderer/renderer.js -whitespace
Libraries/MarkdownRenderer/Resources/MarkdownRenderer/renderer.js -whitespace
24 changes: 14 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version-file: Tools/MarkdownRenderer/.node-version
node-version-file: Libraries/MarkdownRenderer/Tooling/.node-version
cache: npm
cache-dependency-path: Tools/MarkdownRenderer/package-lock.json
cache-dependency-path: Libraries/MarkdownRenderer/Tooling/package-lock.json

- name: Check Markdown renderer
working-directory: Tools/MarkdownRenderer
working-directory: Libraries/MarkdownRenderer/Tooling
shell: bash
run: |
set -euo pipefail

npm ci
npm run verify

RENDERER_OUTPUT="../../Application/Presentation/PresentationShared/Resources/MarkdownRenderer"
RENDERER_OUTPUT="../Resources/MarkdownRenderer"

node --check "$RENDERER_OUTPUT/renderer.js"
git -C ../.. diff --exit-code -- \
Application/Presentation/PresentationShared/Resources/MarkdownRenderer/index.html \
Application/Presentation/PresentationShared/Resources/MarkdownRenderer/renderer.css \
Application/Presentation/PresentationShared/Resources/MarkdownRenderer/renderer.js
git -C ../../.. diff --exit-code -- \
Libraries/MarkdownRenderer/Resources/MarkdownRenderer/index.html \
Libraries/MarkdownRenderer/Resources/MarkdownRenderer/renderer.css \
Libraries/MarkdownRenderer/Resources/MarkdownRenderer/renderer.js

- name: Install private config files
uses: ./.github/actions/install-private-config
Expand Down Expand Up @@ -101,7 +101,7 @@ jobs:
~/Library/Caches/org.swift.swiftpm
~/Library/Developer/Xcode/SourcePackages
.spm
key: ${{ runner.os }}-spm-${{ hashFiles('.mise.toml', 'Tuist.swift', 'Workspace.swift', 'Tuist/ProjectDescriptionHelpers/*.swift', 'Application/**/Project.swift', 'Widget/**/Project.swift') }}
key: ${{ runner.os }}-spm-${{ hashFiles('.mise.toml', 'Tuist.swift', 'Workspace.swift', 'Tuist/ProjectDescriptionHelpers/*.swift', 'Application/**/Project.swift', 'Libraries/**/Project.swift', 'Widget/**/Project.swift') }}
restore-keys: |
${{ runner.os }}-spm-

Expand Down Expand Up @@ -255,6 +255,8 @@ jobs:
schemes: "Infra"
- name: Persistence
schemes: "Persistence"
- name: Libraries
schemes: "MarkdownRenderer"
- name: Presentation
schemes: "Entry PresentationShared HomeTab"
- name: Widget
Expand Down Expand Up @@ -315,7 +317,7 @@ jobs:
~/Library/Caches/org.swift.swiftpm
~/Library/Developer/Xcode/SourcePackages
.spm
key: ${{ runner.os }}-spm-${{ hashFiles('.mise.toml', 'Tuist.swift', 'Workspace.swift', 'Tuist/ProjectDescriptionHelpers/*.swift', 'Application/**/Project.swift', 'Widget/**/Project.swift') }}
key: ${{ runner.os }}-spm-${{ hashFiles('.mise.toml', 'Tuist.swift', 'Workspace.swift', 'Tuist/ProjectDescriptionHelpers/*.swift', 'Application/**/Project.swift', 'Libraries/**/Project.swift', 'Widget/**/Project.swift') }}
restore-keys: |
${{ runner.os }}-spm-

Expand Down Expand Up @@ -427,6 +429,8 @@ jobs:
LOG_PATH="$RESULT_DIR/${TEST_SCHEME}.log"
if [ "$TEST_SCHEME" = "WidgetCore" ]; then
TEST_SOURCE_DIR="Widget/WidgetCore/Tests"
elif [ -d "Libraries/${TEST_SCHEME}/Tests" ]; then
TEST_SOURCE_DIR="Libraries/${TEST_SCHEME}/Tests"
elif [ -d "Application/Presentation/${TEST_SCHEME}/Tests" ]; then
TEST_SOURCE_DIR="Application/Presentation/${TEST_SCHEME}/Tests"
else
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ node_modules/
*.log
*.js
# 배포 앱에 필요한 Markdown renderer 산출물이므로 Git에서 추적
!Application/Presentation/PresentationShared/Resources/MarkdownRenderer/renderer.js
!Libraries/MarkdownRenderer/Resources/MarkdownRenderer/renderer.js

# Gem
.bundle/
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
- `AGENTS.md` is the repository entrypoint and routing source.
- `.agents/rules/general.md` applies to every task.
- Read all matching task-specific documents before planning, editing, reviewing, or verifying.
- For architecture work, also read `.gemini/styleguide.md` and `README.md` before editing.
- For architecture work, also read `README.md` before editing.
Comment thread
opficdev marked this conversation as resolved.
- For a delegated role, read `.agents/roles.md` and follow the assigned role section and output format.
- Use `.agents/workflows.md` when the task matches one of its executable workflows.
- If repository-local instructions conflict with global memory, follow the repository-local instructions.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@

import SwiftUI
import Domain
import MarkdownRenderer

struct TodoMarkdownContentView: View {
@Environment(\.colorScheme) private var colorScheme
@Environment(\.locale) private var locale
@Environment(\.openURL) private var openURL
@ScaledMetric(relativeTo: .body) private var fontSize = 17
@State private var tabBarHeight = CGFloat.zero

let content: String
Expand All @@ -23,12 +20,8 @@ struct TodoMarkdownContentView: View {
MarkdownRendererView(
markdown: content,
references: rendererReferences,
colorScheme: colorScheme,
languageCode: locale.language.languageCode?.identifier ?? "und",
fontSize: fontSize,
obscuredBottomInset: tabBarHeight,
onOpenTodoID: onOpenTodoID,
onOpenURL: { openURL($0) }
onOpenReferenceID: onOpenTodoID
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea(.container, edges: ignoredSafeAreaEdges)
Expand All @@ -44,7 +37,7 @@ struct TodoMarkdownContentView: View {
private var rendererReferences: [Int: MarkdownRendererReference] {
referenceItems.mapValues { item in
MarkdownRendererReference(
todoID: item.id,
referenceID: item.id,
title: item.title,
colorHex: item.category.color.hexValue ?? "#808080",
iconDataURL: iconDataURL(for: item.category.symbolName)
Expand Down
9 changes: 4 additions & 5 deletions Application/Presentation/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ let project = Project(
bundleId: "com.opfic.DevLog.PresentationShared",
infoPlist: .file(path: frameworkInfoPlistPath),
sources: ["PresentationShared/Sources/**/*.swift"],
resources: [
.folderReference(
path: "PresentationShared/Resources/MarkdownRenderer"
)
],
scripts: [
DevLogScripts.swiftLint(
sourcePath: "PresentationShared/Sources",
Expand All @@ -43,6 +38,10 @@ let project = Project(
dependencies: [
.project(target: "Domain", path: "../Domain"),
.project(target: "Core", path: "../Core"),
.project(
target: "MarkdownRenderer",
path: "../../Libraries/MarkdownRenderer"
),
.package(product: "ComposableArchitecture"),
.package(product: "OrderedCollections")
],
Expand Down
75 changes: 75 additions & 0 deletions Libraries/MarkdownRenderer/Project.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import ProjectDescription
import ProjectDescriptionHelpers

let deploymentSettings: SettingsDictionary = [
"IPHONEOS_DEPLOYMENT_TARGET": "17.0",
"MARKETING_VERSION": "1.0.0",
]

let project = Project(
name: "MarkdownRenderer",
options: .options(
disableBundleAccessors: true,
disableSynthesizedResourceAccessors: true
),
packages: [],
settings: .devlogProject(additionalBase: deploymentSettings),
targets: [
.target(
name: "MarkdownRenderer",
destinations: .iOS,
product: .framework,
bundleId: "com.opfic.DevLog.MarkdownRenderer",
infoPlist: .extendingDefault(
with: ["CFBundlePackageType": "FMWK"]
),
sources: ["Sources/**/*.swift"],
resources: [
"Resources/MarkdownRenderer/index.html",
"Resources/MarkdownRenderer/renderer.css",
"Resources/MarkdownRenderer/renderer.js",
],
scripts: [
DevLogScripts.swiftLint(
sourcePath: "Sources",
configPath: "Sources/.swiftlint.yml"
),
],
dependencies: [],
settings: .devlog(
base: deploymentSettings.merging(
[
"ENABLE_USER_SCRIPT_SANDBOXING": "NO",
]
) { _, new in new }
)
),
.target(
name: "MarkdownRendererTests",
destinations: .iOS,
product: .unitTests,
bundleId: "com.opfic.DevLog.MarkdownRendererTests",
infoPlist: .extendingDefault(
with: ["CFBundlePackageType": "BNDL"]
),
sources: ["Tests/**/*.swift"],
scripts: [
DevLogScripts.swiftLint(
sourcePath: "Tests",
configPath: "Tests/.swiftlint.yml"
),
],
dependencies: [
.target(name: "MarkdownRenderer"),
],
settings: .devlog(
base: deploymentSettings.merging(
[
"ENABLE_USER_SCRIPT_SANDBOXING": "NO",
"TEST_TARGET_NAME": "MarkdownRenderer",
]
) { _, new in new }
)
),
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,7 @@ body {
color: var(--color-prettylights-syntax-markup-deleted-text);
background-color: var(--color-prettylights-syntax-markup-deleted-bg);
}
.markdown-body .todo-reference {
.markdown-body .markdown-reference {
display: flex;
width: 100%;
min-width: 0;
Expand All @@ -1196,11 +1196,11 @@ body {
text-align: left;
cursor: pointer;
}
.todo-reference-prefix,
.todo-reference-number {
.markdown-reference-prefix,
.markdown-reference-number {
flex: none;
}
.todo-reference-icon {
.markdown-reference-icon {
display: inline-flex;
width: 18px;
height: 18px;
Expand All @@ -1210,25 +1210,25 @@ body {
overflow: hidden;
border-radius: 5px;
}
.todo-reference-icon img {
.markdown-reference-icon img {
width: 12px;
height: 12px;
object-fit: contain;
}
.todo-reference-title {
.markdown-reference-title {
min-width: 0;
overflow: hidden;
color: #0969da;
text-decoration: underline;
text-overflow: ellipsis;
white-space: nowrap;
}
:root[data-color-scheme=dark] .todo-reference-title {
:root[data-color-scheme=dark] .markdown-reference-title {
color: #4493f8;
}
.todo-reference-number {
.markdown-reference-number {
color: #59636e;
}
:root[data-color-scheme=dark] .todo-reference-number {
:root[data-color-scheme=dark] .markdown-reference-number {
color: #9198a1;
}
Loading
Loading