Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c48b56e
Port core leaf modules to pure OCaml (drop lg pipeline)
tiensonqin Sep 22, 2026
96988ac
Port rpc/rpc_session/pending_pump/session_outliner ops layer to OCaml
tiensonqin Sep 22, 2026
79f2284
Port graph runtime, sync session, and mobile hosts to OCaml
tiensonqin Sep 22, 2026
29442c1
Port live_sync and e2e seed modules, completing the core/ port
tiensonqin Sep 22, 2026
ed1485e
Port model and view_base to pure OCaml
tiensonqin Sep 22, 2026
6e90788
Port view_rows and view_sidebar to pure OCaml
tiensonqin Sep 22, 2026
1f37728
Port view_composer, view_flashcards, view_outliner to pure OCaml
tiensonqin Sep 22, 2026
a3dbe7e
Port view_graphs, fix sidebar/outliner reactive signal plumbing
tiensonqin Sep 22, 2026
2fcee78
Port app, views, native bridge; OCaml test harness with 7 suites green
tiensonqin Sep 22, 2026
6870b4c
Port 6 more test suites (markup, mobile_session, fractional_order, fl…
tiensonqin Sep 23, 2026
c7b4d76
Port remaining test suites to OCaml; fix projection/graph_read bugs f…
tiensonqin Sep 23, 2026
01a6264
Port graph_runtime tests to OCaml; expose normalize_expected_title
tiensonqin Sep 23, 2026
19488c9
Finish test port: app_test.ml (174 tests green), drop cljc sources
tiensonqin Sep 23, 2026
9eedf18
Pin lui to the dynamic-segment drop fix
tiensonqin Sep 23, 2026
8502447
Fix iOS build: link libffi, resolve Swift redeclarations
tiensonqin Sep 23, 2026
8f6b3e9
Pin ocaml-signal to the melange dependency fix
tiensonqin Sep 23, 2026
28f5a2f
Regenerate monorepo lockfile after dropping the lg toolchain
tiensonqin Sep 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ jobs:
with:
path: |
~/.cache/dune
.lg-cache
key: build-cache-${{ github.ref_name }}-${{ github.sha }}
restore-keys: |
build-cache-${{ github.ref_name }}-
Expand All @@ -112,5 +111,4 @@ jobs:
with:
path: |
~/.cache/dune
.lg-cache
key: build-cache-${{ github.ref_name }}-${{ github.sha }}
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ default.profraw
.build/
_build/
_coverage/
.lg-cache/
.clj-kondo/.cache/
.lsp/.cache/
/*.install
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ Logseq Chat uses native SwiftUI on iOS and Flutter Material on Android, backed b

- Use `dune build @shared/native/runtest` for core validation. The mobile entry object
also needs platform libraries and FFI symbols supplied by the mobile build.
Crypto protocol handling lives in `shared/src/logseq_chat/core/platform_crypto.cljc`.
Crypto protocol handling lives in `shared/src/logseq_chat/core/platform_crypto.ml`.
The mobile-only `logseq_chat_crypto_call` FFI declaration lives in
`shared/src/logseq_chat/native_crypto.cljc` so CLI builds do not require mobile symbols.
`shared/src/logseq_chat/native_crypto.ml` so CLI builds do not require mobile symbols.
- Mobile OCaml builds use the thin `scripts/build-mobile-ocaml.sh` Dune wrapper.
Native module membership lives in `shared/native/dune_modules/*.sexp`. Core LG sources
are discovered from `shared/src/logseq_chat/core`; adding one needs no per-file rule.
Native module membership lives in `shared/native/dune_modules/*.sexp`. Core sources
live in `shared/src/logseq_chat/core`; adding one needs no per-file rule.

## Cursor Cloud specific instructions

Expand Down Expand Up @@ -52,7 +52,7 @@ opam install . --deps-only --yes --with-test

`opam init` on this VM must use `--disable-sandboxing`. New shells need `eval $(opam env --switch=5.5.0)` unless `.bashrc` already loads it.

A representative core action is the Swift FFI RPC `dispatch` / `send`, which optimistic-captures a journal block (see `shared/test/logseq_chat/rpc_test.cljc` and `dune build @shared/native/runtest`).
A representative core action is the Swift FFI RPC `dispatch` / `send`, which optimistic-captures a journal block (see `shared/test/logseq_chat/rpc_test.ml` and `dune build @shared/native/runtest`).

### iOS / Android

Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
# Logseq Chat

Logseq Chat is a native SwiftUI app for iOS and a Flutter Material app for
Android. Both clients share an LG application model and core, compiled to
OCaml and backed by the OCaml DataScript library.
Android. Both clients share an OCaml application model and core, backed by
the OCaml DataScript library and the lui reactive UI layer.

Shared core sources live in `shared/src/logseq_chat/core`, with executable entry points
in `shared/src/logseq_chat/entry`. The `shared/native` directory contains Dune configuration and
native platform bridges; generated OCaml stays under `_build`. Core tests live
in `shared/test/logseq_chat`, retaining one LG test module for each original OCaml
test module. The core test gate also builds the seed and live-sync executables.
in `shared/test/logseq_chat`, one alcotest suite per core module. The core test gate also builds the seed and live-sync executables.

## Repository layout

- `apple/`: Swift package, app, extensions, Xcode workspace, and Apple tests.
- `flutter/`: Flutter Android app and its platform integration.
- `shared/src/`: LG application and core sources.
- `shared/test/`: LG tests.
- `shared/src/`: OCaml application and core sources.
- `shared/test/`: OCaml test suites.
- `shared/native/`: Dune configuration and native bridges.
- `tests/e2e/`: Maestro flows and fixtures.
- `scripts/`: build and validation commands, run from the repository root.
Expand Down
3 changes: 2 additions & 1 deletion apple/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ let logseqChatLinkerSettings: [LinkerSetting] = logseqChatNativeLinkInputs.isEmp
.unsafeFlags(logseqChatNativeLinkInputs, .when(platforms: [.iOS])),
.linkedFramework("Foundation", .when(platforms: [.iOS])),
.linkedFramework("Security", .when(platforms: [.iOS])),
.linkedLibrary("sqlite3", .when(platforms: [.iOS]))
.linkedLibrary("sqlite3", .when(platforms: [.iOS])),
.linkedLibrary("ffi", .when(platforms: [.iOS]))
]
let logseqChatCoreSwiftSettings: [SwiftSetting] = logseqChatNativeLinkInputs.isEmpty ? [] : [
.define("LOGSEQ_CHAT_CORE", .when(platforms: [.iOS]))
Expand Down
11 changes: 1 addition & 10 deletions apple/Sources/LogseqChat/LGChatEffects.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,6 @@ public struct LGChatEffectResolution: Equatable, Sendable {
}
}

public struct LGChatSettingsPayload: Decodable, Equatable, Sendable {
public let appearance: String
public let language: String
public let spellCheck: Bool
public let autoCorrection: Bool
public let sidebarTabs: [String]
public let baseURL: String
}


public struct LGChatSettingsPayload: Decodable, Equatable, Sendable {
public let appearance: String
public let language: String
Expand Down Expand Up @@ -378,6 +368,7 @@ public final class LGChatPlatformEffectHandler: LGChatEffectExecuting {
}
}

@MainActor
public protocol LGChatPlatformCommandHandling: AnyObject {
func handle(_ batch: LGChatPlatformCommandBatch)
}
Expand Down
6 changes: 1 addition & 5 deletions apple/Sources/LogseqChat/LGChatRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ private struct LGChatPendingHostUpdate {
}


public protocol LGChatPlatformCommandHandling: AnyObject {
func handle(_ batch: LGChatPlatformCommandBatch)
}


private struct LGCoreResponseEnvelope: Decodable {
let result: Result?

Expand Down Expand Up @@ -73,6 +68,7 @@ public enum LGChatRuntimeError: Error, Equatable {
}


@MainActor
public final class LGChatRuntime {
public let renderer: LGChatRenderer
public private(set) var lastError: String?
Expand Down
15 changes: 6 additions & 9 deletions logseq_chat.opam
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ depends: [
"dune" {>= "3.17"}
"datascript_ocaml"
"datascript-ocaml-native"
"datascript-ocaml-lg"
"melange-edn-native"
"melange-transit-native"
"lg"
"lg-test" {with-test}
"lui"
"signal-lg"
"ocaml-signal"
"ctypes"
"ctypes-foreign"
"alcotest" {with-test}
"mldoc"
"ocaml-fsrs"
"yojson"
Expand All @@ -23,18 +23,15 @@ depends: [
pin-depends: [
["datascript_ocaml.dev" "git+https://github.com/logseq/datascript-ocaml.git#4c1ea15772e0a3d551c4bd793346b380fb12dc72"]
["datascript-ocaml-native.dev" "git+https://github.com/logseq/datascript-ocaml.git#4c1ea15772e0a3d551c4bd793346b380fb12dc72"]
["datascript-ocaml-lg.dev" "git+https://github.com/logseq/datascript-ocaml.git#4c1ea15772e0a3d551c4bd793346b380fb12dc72"]
["persistent_sorted_set_ocaml.dev" "git+https://github.com/logseq/persistent-sorted-set-ocaml.git#4016dae1cdf4304207d8277ff9957656cdd8b177"]
["melange-edn-core.0.5.0" "git+https://github.com/RCmerci/melange-edn.git#3cb79f278e972388a0a2b2ea1caec7a008a0b956"]
["melange-edn-native.0.5.0" "git+https://github.com/RCmerci/melange-edn.git#3cb79f278e972388a0a2b2ea1caec7a008a0b956"]
["melange-edn-melange.0.5.0" "git+https://github.com/RCmerci/melange-edn.git#3cb79f278e972388a0a2b2ea1caec7a008a0b956"]
["melange-transit-core.0.1.2" "git+https://github.com/RCmerci/melange-transit.git#950a5786e343f893a6e0396819383db341d5aa97"]
["melange-transit-native.0.1.2" "git+https://github.com/RCmerci/melange-transit.git#950a5786e343f893a6e0396819383db341d5aa97"]
["melange-transit-melange.0.1.2" "git+https://github.com/RCmerci/melange-transit.git#950a5786e343f893a6e0396819383db341d5aa97"]
["lg.dev" "git+ssh://git@github.com/logseq/lg.git#aef853a7d1d3ddf0bb4ce6fcf7af5aa5dd8da1ed"]
["lg-test.dev" "git+ssh://git@github.com/logseq/lg.git#aef853a7d1d3ddf0bb4ce6fcf7af5aa5dd8da1ed"]
["lui.0.1.0" "git+ssh://git@github.com/logseq/lui.git#0a83b2087be9a5112cf63e7a935f4cb040751a35"]
["signal-lg.0.1.0" "git+https://github.com/logseq/signal-lg.git#7ce3d12df004c7fd93fc9f978521dc503fe1e0ba"]
["lui.0.1.0" "git+ssh://git@github.com/logseq/lui.git#21d5c9ead9b9a34005ce070575e9ddde4a420c6a"]
["ocaml-signal.0.1.0" "git+https://github.com/logseq/ocaml-signal.git#b975304facce9e1e5e3f19286db1ce5d857325b6"]
["mldoc.dev" "git+https://github.com/logseq/mldoc.git#fd1e83ed72faf8aa83b9d9a5cec2b87943403c0b"]
["angstrom.dev" "git+https://github.com/logseq/angstrom.git#3be9b966dc2bc9ccf9948d17a7b0df1cb526de15"]
["xmlm.dev" "git+https://github.com/logseq/xmlm.git#eb469d536e98c98f2754c0ff8813c92b3a17fe9f"]
Expand Down
47 changes: 23 additions & 24 deletions logseq_chat.opam.locked
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ depends: [
"bin_prot" {= "v0.17.0-1" & ?vendor}
"capitalization" {= "v0.17.0" & ?vendor}
"cmdliner" {= "2.1.1+dune" & ?vendor}
"compiler-cloning" {= "enabled"}
"compiler-cloning" {= "disabled"}
"conf-libffi" {= "2.0.0"}
"conf-pkg-config" {= "5"}
"conf-sqlite3" {= "1"}
Expand All @@ -38,7 +38,6 @@ depends: [
"cstruct" {= "6.3.0" & ?vendor}
"ctypes" {= "0.24.0" & ?vendor}
"ctypes-foreign" {= "0.24.0" & ?vendor}
"datascript-ocaml-lg" {= "dev" & ?vendor}
"datascript-ocaml-native" {= "dev" & ?vendor}
"datascript_ocaml" {= "dev" & ?vendor}
"delimited_parsing" {= "v0.17.0" & ?vendor}
Expand All @@ -64,17 +63,13 @@ depends: [
"js_of_ocaml-ppx" {= "6.4.1" & ?vendor}
"jsonm" {= "1.0.2+dune" & ?vendor}
"jst-config" {= "v0.17.0" & ?vendor}
"lg" {= "dev" & ?vendor}
"lg-test" {= "dev" & ?vendor}
"lui" {= "0.1.0" & ?vendor}
"lwt" {= "6.1.2" & ?vendor}
"melange" {= "7.0.1-55" & ?vendor}
"melange-edn-core" {= "0.5.0" & ?vendor}
"melange-edn-melange" {= "0.5.0" & ?vendor}
"melange-edn-native" {= "0.5.0" & ?vendor}
"melange-fetch" {= "0.2.0" & ?vendor}
"melange-transit-core" {= "0.1.2" & ?vendor}
"melange-transit-melange" {= "0.1.2" & ?vendor}
"melange-transit-native" {= "0.1.2" & ?vendor}
"melange-webapi" {= "0.22.0" & ?vendor}
"menhir" {= "20260209" & ?vendor}
Expand All @@ -85,12 +80,13 @@ depends: [
"merlin-extend" {= "0.6.2" & ?vendor}
"mldoc" {= "dev" & ?vendor}
"num" {= "1.4+dune2" & ?vendor}
"ocaml" {= "5.5.1"}
"ocaml-base-compiler" {= "5.5.1"}
"ocaml-compiler" {= "5.5.1"}
"ocaml" {= "5.5.0"}
"ocaml-base-compiler" {= "5.5.0"}
"ocaml-compiler" {= "5.5.0"}
"ocaml-compiler-libs" {= "v0.17.0" & ?vendor}
"ocaml-fsrs" {= "0.1.0-alpha3" & ?vendor}
"ocaml-options-vanilla" {= "1"}
"ocaml-signal" {= "0.1.0" & ?vendor}
"ocaml-syntax-shims" {= "1.0.0" & ?vendor}
"ocaml_intrinsics_kernel" {= "v0.17.2" & ?vendor}
"ocamlfind" {= "1.9.5+dune" & ?vendor}
Expand Down Expand Up @@ -148,7 +144,6 @@ depends: [
"sexp_pretty" {= "v0.17.0" & ?vendor}
"sexplib" {= "v0.17.0" & ?vendor}
"sexplib0" {= "v0.17.0" & ?vendor}
"signal-lg" {= "0.1.0" & ?vendor}
"spawn" {= "v0.17.0" & ?vendor}
"splittable_random" {= "v0.17.0" & ?vendor}
"sqlite3" {= "5.4.2" & ?vendor}
Expand Down Expand Up @@ -223,7 +218,7 @@ pin-depends: [
]
[
"angstrom.dev"
"git+https://github.com/logseq/angstrom#3be9b966dc2bc9ccf9948d17a7b0df1cb526de15"
"git+https://github.com/logseq/angstrom.git#3be9b966dc2bc9ccf9948d17a7b0df1cb526de15"
]
[
"astring.0.8.5+dune"
Expand Down Expand Up @@ -418,10 +413,6 @@ pin-depends: [
"melange-edn-core.0.5.0"
"git+https://github.com/RCmerci/melange-edn.git#3cb79f278e972388a0a2b2ea1caec7a008a0b956"
]
[
"melange-edn-melange.0.5.0"
"git+https://github.com/RCmerci/melange-edn.git#3cb79f278e972388a0a2b2ea1caec7a008a0b956"
]
[
"melange-edn-native.0.5.0"
"git+https://github.com/RCmerci/melange-edn.git#3cb79f278e972388a0a2b2ea1caec7a008a0b956"
Expand All @@ -434,10 +425,6 @@ pin-depends: [
"melange-transit-core.0.1.2"
"git+https://github.com/RCmerci/melange-transit.git#950a5786e343f893a6e0396819383db341d5aa97"
]
[
"melange-transit-melange.0.1.2"
"git+https://github.com/RCmerci/melange-transit.git#950a5786e343f893a6e0396819383db341d5aa97"
]
[
"melange-transit-native.0.1.2"
"git+https://github.com/RCmerci/melange-transit.git#950a5786e343f893a6e0396819383db341d5aa97"
Expand Down Expand Up @@ -480,7 +467,11 @@ pin-depends: [
]
[
"ocaml-fsrs.0.1.0-alpha3"
"git+https://github.com/logseq/ocaml-fsrs.git#0ac7779062df50e7a9760d926ee215ed52c73194"
"git+https://github.com/logseq/ocaml-fsrs.git#54d30651b01081d9d7bcfc6d473b7338bfb6c278"
]
[
"ocaml-signal.0.1.0"
"git+https://github.com/logseq/ocaml-signal.git#b975304facce9e1e5e3f19286db1ce5d857325b6"
]
[
"ocaml-syntax-shims.1.0.0"
Expand Down Expand Up @@ -773,13 +764,16 @@ pin-depends: [
]
[
"xmlm.dev"
"git+https://github.com/logseq/xmlm#eb469d536e98c98f2754c0ff8813c92b3a17fe9f"
"git+https://github.com/logseq/xmlm.git#eb469d536e98c98f2754c0ff8813c92b3a17fe9f"
]
[
"yojson.3.0.0"
"https://github.com/ocaml-community/yojson/releases/download/3.0.0/yojson-3.0.0.tbz"
]
]
x-opam-monorepo-cli-args: [
"--ocaml-version=5.5.0" "--opam-provided=ocaml-compiler" "logseq_chat"
]
x-opam-monorepo-duniverse-dirs: [
[
"https://github.com/OCamlPro/ocplib-endian/archive/refs/tags/1.2.tar.gz"
Expand Down Expand Up @@ -1479,15 +1473,19 @@ x-opam-monorepo-duniverse-dirs: [
]
]
[
"git+https://github.com/logseq/angstrom#3be9b966dc2bc9ccf9948d17a7b0df1cb526de15"
"git+https://github.com/logseq/angstrom.git#3be9b966dc2bc9ccf9948d17a7b0df1cb526de15"
"angstrom"
]
[
"git+https://github.com/logseq/ocaml-fsrs.git#0ac7779062df50e7a9760d926ee215ed52c73194"
"git+https://github.com/logseq/ocaml-fsrs.git#54d30651b01081d9d7bcfc6d473b7338bfb6c278"
"ocaml-fsrs"
]
[
"git+https://github.com/logseq/xmlm#eb469d536e98c98f2754c0ff8813c92b3a17fe9f"
"git+https://github.com/logseq/ocaml-signal.git#b975304facce9e1e5e3f19286db1ce5d857325b6"
"ocaml-signal"
]
[
"git+https://github.com/logseq/xmlm.git#eb469d536e98c98f2754c0ff8813c92b3a17fe9f"
"xmlm"
]
[
Expand Down Expand Up @@ -1723,5 +1721,6 @@ x-opam-monorepo-duniverse-dirs: [
]
]
]
x-opam-monorepo-opam-provided: ["ocaml-compiler"]
x-opam-monorepo-root-packages: ["logseq_chat"]
x-opam-monorepo-version: "0.3"
2 changes: 1 addition & 1 deletion scripts/lg-coverage.ml → scripts/coverage_check.ml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ let check source filename prefix floor coverage =
let () =
try
if Array.length Sys.argv <> 6 then
failwith "usage: lg-coverage SOURCE COVERAGE_FILENAME PREFIX BASIS_POINTS COVERAGE";
failwith "usage: coverage_check SOURCE COVERAGE_FILENAME PREFIX BASIS_POINTS COVERAGE";
check Sys.argv.(1) Sys.argv.(2) Sys.argv.(3)
(int_of_string Sys.argv.(4)) Sys.argv.(5)
with error ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
set -euo pipefail

root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
tmp=$(mktemp -d "${TMPDIR:-/tmp}/lg-coverage-test.XXXXXX")
tmp=$(mktemp -d "${TMPDIR:-/tmp}/coverage-check-test.XXXXXX")
trap 'rm -rf "$tmp"' EXIT

opam exec --switch=5.5.0 -- ocamlfind ocamlopt -w -24 \
-package compiler-libs.common -c \
-o "$tmp/check.cmx" -impl "$root/scripts/lg-coverage.ml"
-o "$tmp/check.cmx" -impl "$root/scripts/coverage_check.ml"
opam exec --switch=5.5.0 -- ocamlfind ocamlopt \
-package compiler-libs.common -linkpkg -o "$tmp/check.exe" "$tmp/check.cmx"

Expand Down Expand Up @@ -43,4 +43,4 @@ if "$tmp/check.exe" "$tmp/generated.ml" generated.ml feature_ 0 "$tmp/version.co
echo 'error: accepted an unsupported coverage format' >&2
exit 1
fi
echo 'LG generated-code coverage checks passed'
echo 'coverage checks passed'
8 changes: 4 additions & 4 deletions scripts/test-ios-sidebar-control-shape.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
control_source="$(rtk sed -n '/^(defui main-header-leading /,/^(defui main-header-title /p' \
"${repo_root}/shared/src/logseq_chat/view.cljc")"
if printf '%s\n' "${control_source}" | rtk rg -q ':corner-radius'; then
control_source="$(rtk sed -n '/^let main_header_leading /,/^let main_header_title /p' \
"${repo_root}/shared/src/logseq_chat/view_screens.ml")"
if printf '%s\n' "${control_source}" | rtk rg -q '~corner_radius'; then
echo "main-header-leading overrides the native toolbar button shape" >&2
exit 1
fi
if printf '%s\n' "${control_source}" \
| rtk rg -q ':(width|height)[[:space:]]+[0-9]'; then
| rtk rg -q '~(width|height):[0-9]'; then
echo "main-header-leading overrides the native toolbar control size" >&2
exit 1
fi
Expand Down
6 changes: 3 additions & 3 deletions scripts/test-no-skip-support.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ if grep -REn '#(if|elseif).*(^|[^A-Za-z])!?SKIP([^A-Za-z]|$)' \
die "Swift sources still contain Skip conditional compilation"
fi

if grep -Fq '(proto/profile proto/AndroidOS proto/SwiftUIHost)' \
"$repo_root/shared/src/logseq_chat/view.cljc"; then
die "LG still registers the removed Android SwiftUI host"
if grep -Eq 'profile[[:space:]]+AndroidOS[[:space:]]+SwiftUIHost' \
"$repo_root/shared/src/logseq_chat/view.ml"; then
die "the extension registry still registers the removed Android SwiftUI host"
fi

if grep -Eq 'SkipStone|Run skip gradle|Skip\.env|skip gradle' \
Expand Down
Loading
Loading