Compare commits
5
Commits
195dc2cc59
...
cc27a860e5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc27a860e5
|
||
|
|
d2e213b516
|
||
|
|
226d6fb748
|
||
|
|
44a2aff050
|
||
|
|
d44e4dd4ec
|
@@ -1,3 +1,6 @@
|
|||||||
[submodule "docs/reference/outline-openapi"]
|
[submodule "docs/reference/outline-openapi"]
|
||||||
path = docs/reference/outline-openapi
|
path = docs/reference/outline-openapi
|
||||||
url = https://github.com/outline/openapi.git
|
url = https://github.com/outline/openapi.git
|
||||||
|
[submodule "Vendor/swift-markdown-engine"]
|
||||||
|
path = Vendor/swift-markdown-engine
|
||||||
|
url = https://github.com/nodes-app/swift-markdown-engine.git
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
FA7596B230366A1D0000167E /* MarkdownEngine in Frameworks */ = {isa = PBXBuildFile; productRef = FA7596B130366A1D0000167E /* MarkdownEngine */; };
|
||||||
|
FA7596B430366A1D0000167E /* MarkdownEngineCodeBlocks in Frameworks */ = {isa = PBXBuildFile; productRef = FA7596B330366A1D0000167E /* MarkdownEngineCodeBlocks */; };
|
||||||
|
FA7596B630366A1D0000167E /* MarkdownEngineLatex in Frameworks */ = {isa = PBXBuildFile; productRef = FA7596B530366A1D0000167E /* MarkdownEngineLatex */; };
|
||||||
FAF99C44302CF1BD00C9949F /* OutlineKit in Frameworks */ = {isa = PBXBuildFile; productRef = FAF99C43302CF1BD00C9949F /* OutlineKit */; };
|
FAF99C44302CF1BD00C9949F /* OutlineKit in Frameworks */ = {isa = PBXBuildFile; productRef = FAF99C43302CF1BD00C9949F /* OutlineKit */; };
|
||||||
FAF99CAF302D120500C9949F /* MarkdownEngine in Frameworks */ = {isa = PBXBuildFile; productRef = FAF99CAE302D120500C9949F /* MarkdownEngine */; };
|
|
||||||
FAF99CB1302D120500C9949F /* MarkdownEngineCodeBlocks in Frameworks */ = {isa = PBXBuildFile; productRef = FAF99CB0302D120500C9949F /* MarkdownEngineCodeBlocks */; };
|
|
||||||
FAF99CB3302D120500C9949F /* MarkdownEngineLatex in Frameworks */ = {isa = PBXBuildFile; productRef = FAF99CB2302D120500C9949F /* MarkdownEngineLatex */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -59,9 +59,9 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
files = (
|
files = (
|
||||||
FAF99C44302CF1BD00C9949F /* OutlineKit in Frameworks */,
|
FAF99C44302CF1BD00C9949F /* OutlineKit in Frameworks */,
|
||||||
FAF99CB1302D120500C9949F /* MarkdownEngineCodeBlocks in Frameworks */,
|
FA7596B430366A1D0000167E /* MarkdownEngineCodeBlocks in Frameworks */,
|
||||||
FAF99CB3302D120500C9949F /* MarkdownEngineLatex in Frameworks */,
|
FA7596B630366A1D0000167E /* MarkdownEngineLatex in Frameworks */,
|
||||||
FAF99CAF302D120500C9949F /* MarkdownEngine in Frameworks */,
|
FA7596B230366A1D0000167E /* MarkdownEngine in Frameworks */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
FAF99C24302CE96200C9949F /* Frameworks */ = {
|
FAF99C24302CE96200C9949F /* Frameworks */ = {
|
||||||
@@ -116,9 +116,9 @@
|
|||||||
name = Outpost;
|
name = Outpost;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
FAF99C43302CF1BD00C9949F /* OutlineKit */,
|
FAF99C43302CF1BD00C9949F /* OutlineKit */,
|
||||||
FAF99CAE302D120500C9949F /* MarkdownEngine */,
|
FA7596B130366A1D0000167E /* MarkdownEngine */,
|
||||||
FAF99CB0302D120500C9949F /* MarkdownEngineCodeBlocks */,
|
FA7596B330366A1D0000167E /* MarkdownEngineCodeBlocks */,
|
||||||
FAF99CB2302D120500C9949F /* MarkdownEngineLatex */,
|
FA7596B530366A1D0000167E /* MarkdownEngineLatex */,
|
||||||
);
|
);
|
||||||
productName = Outpost;
|
productName = Outpost;
|
||||||
productReference = FAF99C18302CE96100C9949F /* Outpost.app */;
|
productReference = FAF99C18302CE96100C9949F /* Outpost.app */;
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
minimizedProjectReferenceProxies = 1;
|
minimizedProjectReferenceProxies = 1;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
FAF99C42302CF1BD00C9949F /* XCLocalSwiftPackageReference "OutlineKit" */,
|
FAF99C42302CF1BD00C9949F /* XCLocalSwiftPackageReference "OutlineKit" */,
|
||||||
FAF99CAD302D120500C9949F /* XCRemoteSwiftPackageReference "swift-markdown-engine" */,
|
FA7596B030366A1D0000167E /* XCLocalSwiftPackageReference "Vendor/swift-markdown-engine" */,
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 77;
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = FAF99C19302CE96100C9949F /* Products */;
|
productRefGroup = FAF99C19302CE96100C9949F /* Products */;
|
||||||
@@ -622,43 +622,33 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCLocalSwiftPackageReference section */
|
/* Begin XCLocalSwiftPackageReference section */
|
||||||
|
FA7596B030366A1D0000167E /* XCLocalSwiftPackageReference "Vendor/swift-markdown-engine" */ = {
|
||||||
|
isa = XCLocalSwiftPackageReference;
|
||||||
|
relativePath = "Vendor/swift-markdown-engine";
|
||||||
|
};
|
||||||
FAF99C42302CF1BD00C9949F /* XCLocalSwiftPackageReference "OutlineKit" */ = {
|
FAF99C42302CF1BD00C9949F /* XCLocalSwiftPackageReference "OutlineKit" */ = {
|
||||||
isa = XCLocalSwiftPackageReference;
|
isa = XCLocalSwiftPackageReference;
|
||||||
relativePath = OutlineKit;
|
relativePath = OutlineKit;
|
||||||
};
|
};
|
||||||
/* End XCLocalSwiftPackageReference section */
|
/* End XCLocalSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
|
||||||
FAF99CAD302D120500C9949F /* XCRemoteSwiftPackageReference "swift-markdown-engine" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/nodes-app/swift-markdown-engine";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 0.12.0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
FA7596B130366A1D0000167E /* MarkdownEngine */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = MarkdownEngine;
|
||||||
|
};
|
||||||
|
FA7596B330366A1D0000167E /* MarkdownEngineCodeBlocks */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = MarkdownEngineCodeBlocks;
|
||||||
|
};
|
||||||
|
FA7596B530366A1D0000167E /* MarkdownEngineLatex */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = MarkdownEngineLatex;
|
||||||
|
};
|
||||||
FAF99C43302CF1BD00C9949F /* OutlineKit */ = {
|
FAF99C43302CF1BD00C9949F /* OutlineKit */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = OutlineKit;
|
productName = OutlineKit;
|
||||||
};
|
};
|
||||||
FAF99CAE302D120500C9949F /* MarkdownEngine */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = FAF99CAD302D120500C9949F /* XCRemoteSwiftPackageReference "swift-markdown-engine" */;
|
|
||||||
productName = MarkdownEngine;
|
|
||||||
};
|
|
||||||
FAF99CB0302D120500C9949F /* MarkdownEngineCodeBlocks */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = FAF99CAD302D120500C9949F /* XCRemoteSwiftPackageReference "swift-markdown-engine" */;
|
|
||||||
productName = MarkdownEngineCodeBlocks;
|
|
||||||
};
|
|
||||||
FAF99CB2302D120500C9949F /* MarkdownEngineLatex */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = FAF99CAD302D120500C9949F /* XCRemoteSwiftPackageReference "swift-markdown-engine" */;
|
|
||||||
productName = MarkdownEngineLatex;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = FAF99C10302CE96100C9949F /* Project object */;
|
rootObject = FAF99C10302CE96100C9949F /* Project object */;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "f233fa96f0c6bdcdbf87f726af38f25704f6d46a156a27dfac47541baa63bf97",
|
"originHash" : "4127e8224149bef00a33500e8db49748a735e1c07626f2255faea52554afef5e",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "highlighterswift",
|
"identity" : "highlighterswift",
|
||||||
@@ -10,15 +10,6 @@
|
|||||||
"version" : "3.1.0"
|
"version" : "3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"identity" : "swift-markdown-engine",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/nodes-app/swift-markdown-engine",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "e5f7607fc4021181056ef7a09dbb7573dc0237d9",
|
|
||||||
"version" : "0.12.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "swiftmath",
|
"identity" : "swiftmath",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MarkdownEngine
|
import MarkdownEngine
|
||||||
|
import MarkdownEngineCodeBlocks
|
||||||
import OutlineKit
|
import OutlineKit
|
||||||
|
|
||||||
/// Read-only for now — document/overview editing isn't wired up yet. Uses
|
/// Read-only for now — document/overview editing isn't wired up yet. Uses
|
||||||
@@ -18,7 +19,10 @@ struct CollectionOverviewContent: View {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
NativeTextViewWrapper(
|
NativeTextViewWrapper(
|
||||||
text: $markdown,
|
text: $markdown,
|
||||||
configuration: .init(heightBehavior: .fitsContent),
|
configuration: .init(
|
||||||
|
services: .init(syntaxHighlighter: CodeSyntaxHighlighting.shared),
|
||||||
|
heightBehavior: .fitsContent
|
||||||
|
),
|
||||||
isEditable: false
|
isEditable: false
|
||||||
)
|
)
|
||||||
.padding()
|
.padding()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import MarkdownEngine
|
import MarkdownEngine
|
||||||
|
import MarkdownEngineCodeBlocks
|
||||||
import OutlineKit
|
import OutlineKit
|
||||||
|
|
||||||
/// Distraction-free reading view — no toolbar/sidebar chrome, larger type.
|
/// Distraction-free reading view — no toolbar/sidebar chrome, larger type.
|
||||||
@@ -44,7 +45,10 @@ struct DocumentPresentSheet: View {
|
|||||||
.padding(.bottom, 8)
|
.padding(.bottom, 8)
|
||||||
NativeTextViewWrapper(
|
NativeTextViewWrapper(
|
||||||
text: $text,
|
text: $text,
|
||||||
configuration: .init(heightBehavior: .fitsContent),
|
configuration: .init(
|
||||||
|
services: .init(syntaxHighlighter: CodeSyntaxHighlighting.shared),
|
||||||
|
heightBehavior: .fitsContent
|
||||||
|
),
|
||||||
isEditable: false
|
isEditable: false
|
||||||
)
|
)
|
||||||
.font(.system(size: 18))
|
.font(.system(size: 18))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import AppKit
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
import MarkdownEngine
|
import MarkdownEngine
|
||||||
|
import MarkdownEngineCodeBlocks
|
||||||
import OutlineKit
|
import OutlineKit
|
||||||
|
|
||||||
/// `NSSavePanel`/`NSPrintOperation`/`NSPasteboard` in the action functions
|
/// `NSSavePanel`/`NSPrintOperation`/`NSPasteboard` in the action functions
|
||||||
@@ -17,6 +18,21 @@ struct DocumentReaderView: View {
|
|||||||
/// Outline — see `SettingsView.editorDetail`.
|
/// Outline — see `SettingsView.editorDetail`.
|
||||||
@AppStorage("outpost.splitViewEnabled") private var isSplitViewEnabled = false
|
@AppStorage("outpost.splitViewEnabled") private var isSplitViewEnabled = false
|
||||||
|
|
||||||
|
/// Outline's own "Show line numbers" preference (synced, read via
|
||||||
|
/// `session.userPreferences`, not `@AppStorage` — this one's the
|
||||||
|
/// server's, not a local-only Outpost setting). No `@Environment`-in-`init`
|
||||||
|
/// problem here since this is read directly in the view, not the
|
||||||
|
/// view model.
|
||||||
|
private var showCodeBlockLineNumbers: Bool {
|
||||||
|
session.userPreferences?.codeBlockLineNumbers ?? false
|
||||||
|
}
|
||||||
|
/// Widened left indent reserved for the number gutter when line numbers
|
||||||
|
/// are on (default is 12pt, just enough margin, no room for digits).
|
||||||
|
private static let lineNumberGutterWidth: CGFloat = 32
|
||||||
|
private var editorCodeBlockStyle: CodeBlockStyle {
|
||||||
|
showCodeBlockLineNumbers ? .init(horizontalIndent: Self.lineNumberGutterWidth) : .default
|
||||||
|
}
|
||||||
|
|
||||||
@State private var viewModel: DocumentReaderViewModel
|
@State private var viewModel: DocumentReaderViewModel
|
||||||
let apiClient: OutlineAPIClient
|
let apiClient: OutlineAPIClient
|
||||||
let document: OutlineDocument
|
let document: OutlineDocument
|
||||||
@@ -44,6 +60,14 @@ struct DocumentReaderView: View {
|
|||||||
@State private var isShowingShareSheet = false
|
@State private var isShowingShareSheet = false
|
||||||
@State private var isShowingNewDocumentSheet = false
|
@State private var isShowingNewDocumentSheet = false
|
||||||
@State private var actionErrorMessage: String?
|
@State private var actionErrorMessage: String?
|
||||||
|
/// Populated live by `NativeTextViewWrapper`'s `onCodeBlockSelectionChange` —
|
||||||
|
/// one array per instance (main pane, split-view preview pane), since
|
||||||
|
/// each lays the same text out at a different width and gets different
|
||||||
|
/// rects. Only non-empty when `showCodeBlockLineNumbers` is on (see its
|
||||||
|
/// doc comment for why the gutter needs `codeBlock.horizontalIndent`
|
||||||
|
/// widened, which is gated on the same flag).
|
||||||
|
@State private var readerCodeBlocks: [CodeBlockSelection] = []
|
||||||
|
@State private var previewCodeBlocks: [CodeBlockSelection] = []
|
||||||
|
|
||||||
init(
|
init(
|
||||||
apiClient: OutlineAPIClient,
|
apiClient: OutlineAPIClient,
|
||||||
@@ -307,12 +331,24 @@ struct DocumentReaderView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
NativeTextViewWrapper(
|
ZStack(alignment: .topLeading) {
|
||||||
text: $viewModel.text,
|
NativeTextViewWrapper(
|
||||||
configuration: .init(heightBehavior: .fitsContent),
|
text: $viewModel.text,
|
||||||
documentId: viewModel.documentId,
|
configuration: .init(
|
||||||
isEditable: viewModel.isEffectivelyEditable
|
services: .init(syntaxHighlighter: CodeSyntaxHighlighting.shared),
|
||||||
)
|
codeBlock: editorCodeBlockStyle,
|
||||||
|
heightBehavior: .fitsContent
|
||||||
|
),
|
||||||
|
documentId: viewModel.documentId,
|
||||||
|
isEditable: viewModel.isEffectivelyEditable,
|
||||||
|
onCodeBlockSelectionChange: { readerCodeBlocks = $0 }
|
||||||
|
)
|
||||||
|
if showCodeBlockLineNumbers {
|
||||||
|
ForEach(readerCodeBlocks) { selection in
|
||||||
|
CodeBlockLineNumberGutter(selection: selection, gutterWidth: Self.lineNumberGutterWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
@@ -360,12 +396,24 @@ struct DocumentReaderView: View {
|
|||||||
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: .infinity)
|
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
NativeTextViewWrapper(
|
ZStack(alignment: .topLeading) {
|
||||||
text: $viewModel.text,
|
NativeTextViewWrapper(
|
||||||
configuration: .init(heightBehavior: .fitsContent),
|
text: $viewModel.text,
|
||||||
documentId: viewModel.documentId,
|
configuration: .init(
|
||||||
isEditable: false
|
services: .init(syntaxHighlighter: CodeSyntaxHighlighting.shared),
|
||||||
)
|
codeBlock: editorCodeBlockStyle,
|
||||||
|
heightBehavior: .fitsContent
|
||||||
|
),
|
||||||
|
documentId: viewModel.documentId,
|
||||||
|
isEditable: false,
|
||||||
|
onCodeBlockSelectionChange: { previewCodeBlocks = $0 }
|
||||||
|
)
|
||||||
|
if showCodeBlockLineNumbers {
|
||||||
|
ForEach(previewCodeBlocks) { selection in
|
||||||
|
CodeBlockLineNumberGutter(selection: selection, gutterWidth: Self.lineNumberGutterWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.padding(8)
|
.padding(8)
|
||||||
.frame(maxWidth: .infinity, alignment: .topLeading)
|
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||||
}
|
}
|
||||||
@@ -610,4 +658,58 @@ struct DocumentReaderView: View {
|
|||||||
operation.run()
|
operation.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One code block's number gutter, positioned absolutely over a
|
||||||
|
/// `NativeTextViewWrapper` via `CodeBlockSelection.rect` — same overlay
|
||||||
|
/// pattern MarkdownEngine's own `CodeBlockButton` uses.
|
||||||
|
///
|
||||||
|
/// `selection.rect` spans the WHOLE fenced block (open fence line + content
|
||||||
|
/// + close fence line), matching what the engine actually lays out — the
|
||||||
|
/// fence lines render with invisible (`.clear`) text once the caret leaves
|
||||||
|
/// the block, but they don't collapse to zero height, so the block is
|
||||||
|
/// always exactly `content line count + 2` rows tall. `selection.code` is
|
||||||
|
/// content only, so the row height and number positions below both account
|
||||||
|
/// for that phantom top/bottom row explicitly instead of dividing by the
|
||||||
|
/// content line count alone (which would drift the numbers upward, more so
|
||||||
|
/// per line, the taller the block).
|
||||||
|
///
|
||||||
|
/// Known limitation, accepted rather than fixable app-side: a content line
|
||||||
|
/// that soft-wraps onto a second visual row (MarkdownEngine always
|
||||||
|
/// char-wraps code blocks, no way to opt out without forking the package)
|
||||||
|
/// throws this off — every row below it reads one line low. Documented in
|
||||||
|
/// TODO.local.md alongside the same package's other gaps.
|
||||||
|
private struct CodeBlockLineNumberGutter: View {
|
||||||
|
let selection: CodeBlockSelection
|
||||||
|
let gutterWidth: CGFloat
|
||||||
|
|
||||||
|
/// `selection.code` (`token.contentRange`) always ends with exactly one
|
||||||
|
/// trailing `\n` per content line — the range runs right up to the
|
||||||
|
/// start of the closing fence's own line, so the newline that ends the
|
||||||
|
/// last content line is included, but there's never an unterminated
|
||||||
|
/// final line to add one more for. Counting `\n` characters directly
|
||||||
|
/// (not `.components(separatedBy:).count`, which is one too many
|
||||||
|
/// whenever the string ends in the separator) is what makes a
|
||||||
|
/// single-line block read "1", not "2".
|
||||||
|
private var contentLineCount: Int {
|
||||||
|
max(1, selection.code.reduce(into: 0) { count, char in if char == "\n" { count += 1 } })
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let totalRows = CGFloat(contentLineCount + 2)
|
||||||
|
let rowHeight = selection.rect.height / totalRows
|
||||||
|
ForEach(0..<contentLineCount, id: \.self) { line in
|
||||||
|
Text("\(line + 1)")
|
||||||
|
.font(.system(size: 10, design: .monospaced))
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.frame(width: gutterWidth - 6, alignment: .trailing)
|
||||||
|
.position(
|
||||||
|
x: selection.rect.minX + (gutterWidth - 6) / 2,
|
||||||
|
// +1.5 rows: skip the invisible open-fence row, then
|
||||||
|
// center within this content row.
|
||||||
|
y: selection.rect.minY + rowHeight * (CGFloat(line) + 1.5)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#if os(macOS)
|
||||||
|
import MarkdownEngineCodeBlocks
|
||||||
|
|
||||||
|
/// One `HighlighterSwiftBridge` for the whole app. It owns a JavaScriptCore
|
||||||
|
/// context (expensive to spin up) plus its own highlight cache, so every
|
||||||
|
/// `NativeTextViewWrapper` should share this instance rather than each
|
||||||
|
/// constructing its own.
|
||||||
|
enum CodeSyntaxHighlighting {
|
||||||
|
static let shared = HighlighterSwiftBridge()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+1
Submodule Vendor/swift-markdown-engine added at 05c1720f44
Reference in New Issue
Block a user