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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Add explicit home, page, and article roles plus static alias redirects.
- Add bounded Pelican inspection and import commands for portable Markdown metadata.
- Expand the documented Pelican subset with YAML front matter, static and filename link normalization, pre-import reporting of empty image alternative text, and opt-in deterministic filename labels for those images.

## 0.1.0 — 2026-09-28

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The generated publication is written to `output/` only after a successful build.
ray new → ray dev → ray check → ray build
```

For the documented Pelican subset, use `ray migrate inspect <source>` before `ray migrate import <source> <destination>`. Inspection never changes the source; import creates an ordinary Raymatic project in a new or empty destination.
For the documented Pelican subset, use `ray migrate inspect <source>` before `ray migrate import <source> <destination>`. Inspection never changes the source and reports review items such as plugins, themes, URL rules, and missing image alternative text; import creates an ordinary validated Raymatic project in a new or empty destination.

- `new` creates a valid starter publication.
- `dev` serves the latest valid preview, watches `content/` and `presentation/`, rebuilds the small publication, and refreshes the browser when a valid revision is available.
Expand Down
12 changes: 11 additions & 1 deletion docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,17 @@ ray migrate inspect path/to/pelican-project
ray migrate import path/to/pelican-project path/to/new-raymatic-project
```

`inspect` is non-destructive. `import` requires a new or empty destination, writes a normal Raymatic project there, and leaves the source untouched. The supported subset is Markdown articles with portable Pelican header metadata: title, date, category, tags, summary, author, and slug. Themes, plugins, static-path selection, template behavior, redirects, arbitrary configuration, and generated output remain review items.
When a source has empty image alternative text and an explicitly mechanical label is acceptable, opt in with:

```sh
ray migrate import --generate-alt-text path/to/pelican-project path/to/new-raymatic-project
```

`inspect` is non-destructive. `import` requires a new or empty destination, writes a normal Raymatic project there, validates it with Raymatic before publishing the destination, and leaves the source untouched. The supported subset is conventional Markdown under `content/` with Pelican header metadata or YAML front matter. It maps title, date, category, tags, summary, a first author, slug, draft status, and slash-delimited aliases. Markdown under `content/pages/` becomes native `kind = "page"`; other Markdown becomes `kind = "article"`. Non-Markdown files under `content/` are copied to `assets/`, except the source `theme/` tree.

The importer converts `{static}/` links to `/assets/`, normalizes Pelican `{filename}` references, and rewrites local Markdown and `.html` links to Raymatic addresses. It statically maps literal `SITENAME`, `AUTHOR`, `SITESUBTITLE`, `DEFAULT_LANG`, and `SITEURL` settings to `site.toml`; it never executes either configuration file. Inspection reports detected plugins, themes, URL patterns, static-path configuration, and image references without alternative text before import. `--generate-alt-text` changes only empty image descriptions to the deterministic form `Image: filename`; retain the default strict mode when editorial accessibility review is required.

Themes, plugins, static-path selection, template behavior, generated output, arbitrary configuration, non-slash legacy URL preservation, and visual parity remain review items. Empty image alternative text is a validation error in the default strict mode; the importer generates mechanical labels only with the explicit flag above.

The repository includes an advisory inventory script:

Expand Down
19 changes: 18 additions & 1 deletion site/content/reference/docs-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,24 @@ title = "MIGRATION"
+++


Raymatic migrates publication intent, not the incidental machinery of another generator. Start with a clean project:
Raymatic provides a deliberately bounded Pelican workflow. It never executes Pelican configuration, plugins, templates, themes, or hooks:

```sh
ray migrate inspect path/to/pelican-project
ray migrate import path/to/pelican-project path/to/new-raymatic-project
```

For a source with empty image alternative text, use `--generate-alt-text` only when a deterministic filename label is acceptable:

```sh
ray migrate import --generate-alt-text path/to/pelican-project path/to/new-raymatic-project
```

`inspect` is non-destructive. `import` requires a new or empty destination, validates the generated project before publishing it, and leaves the source untouched. It supports conventional Markdown under `content/` with Pelican header metadata or YAML front matter; `content/pages/` becomes native pages and other Markdown becomes articles. Portable non-Markdown files become `assets/`, excluding the source `theme/` tree.

The importer normalizes `{static}` and `{filename}` links plus local Markdown and `.html` references. Inspection reports plugins, themes, URL rules, static-path configuration, and empty image alternative text. `--generate-alt-text` fills only empty descriptions with `Image: filename`; strict mode remains the default. Themes, plugins, generated output, arbitrary configuration, non-slash legacy URLs, and visual parity remain review items.

Raymatic migrates publication intent, not the incidental machinery of another generator. For manual work, start with a clean project:

```sh
ray new my-publication
Expand Down
2 changes: 2 additions & 0 deletions site/content/reference/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ The generated publication is written to `output/` only after a successful build.
ray new → ray dev → ray check → ray build
```

For the documented Pelican subset, run `ray migrate inspect <source>` before `ray migrate import <source> <destination>`. Inspection never changes the source and reports review items such as plugins, themes, URL rules, and missing image alternative text; import creates an ordinary validated Raymatic project in a new or empty destination. See the [migration guide](docs-migration/).

- `new` creates a valid starter publication.
- `dev` serves the latest valid preview, watches `content/` and `presentation/`, rebuilds the small publication, and refreshes the browser when a valid revision is available.
- `check` validates content and presentation without replacing production output.
Expand Down
25 changes: 21 additions & 4 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
//! Command syntax only; publication semantics belong to the shared pipeline.
use std::path::PathBuf;

use clap::{Arg, Command as ClapCommand, value_parser};
use clap::{Arg, ArgAction, Command as ClapCommand, value_parser};

#[derive(Debug, PartialEq, Eq)]
pub enum Command {
New { path: PathBuf },
New {
path: PathBuf,
},
Dev,
Check,
Build,
MigrateInspect { path: PathBuf },
MigrateImport { path: PathBuf, destination: PathBuf },
MigrateInspect {
path: PathBuf,
},
MigrateImport {
path: PathBuf,
destination: PathBuf,
generate_alt_text: bool,
},
}

pub fn definition() -> ClapCommand {
Expand Down Expand Up @@ -46,6 +54,14 @@ pub fn definition() -> ClapCommand {
.subcommand(
ClapCommand::new("import")
.about("Import into a new directory")
.arg(
Arg::new("generate-alt-text")
.long("generate-alt-text")
.action(ArgAction::SetTrue)
.help(
"Fill empty image alt text with a deterministic filename label",
),
)
.arg(
Arg::new("path")
.required(true)
Expand Down Expand Up @@ -88,6 +104,7 @@ pub fn parse() -> Command {
.get_one::<PathBuf>("destination")
.expect("required by clap")
.clone(),
generate_alt_text: values.get_flag("generate-alt-text"),
},
_ => unreachable!("clap requires a declared subcommand"),
},
Expand Down
12 changes: 9 additions & 3 deletions src/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,14 @@ fn references(
break;
};
let te = ts + c;
let target = &body[ts..te];
let target = body[ts..te].split_whitespace().next().unwrap_or("");
let image = body[..start + o]
.rfind('[')
.is_some_and(|index| index > 0 && body.as_bytes()[index - 1] == b'!');
if !image && let Some(target) = ReferenceTarget::parse(target, relative) {
if !image
&& !target.starts_with("/assets/")
&& let Some(target) = ReferenceTarget::parse(target, relative)
{
out.push(InternalReference {
target,
span: SourceSpan {
Expand Down Expand Up @@ -310,7 +313,10 @@ fn asset_references(source: &SourceFile, body_range: Range<usize>) -> Vec<AssetR
break;
};
let target_end = target_start + close;
let target = &body[target_start..target_end];
let target = body[target_start..target_end]
.split_whitespace()
.next()
.unwrap_or("");
let suffix_start = target.find(['?', '#']).unwrap_or(target.len());
let asset_path = &target[..suffix_start];
if asset_path.starts_with("/assets/") {
Expand Down
8 changes: 5 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ pub fn run(command: Command, cwd: &Path) -> Result<(), AppError> {
print!("{}", migration::inspect(&cwd.join(path))?);
Ok(())
}
Command::MigrateImport { path, destination } => {
migration::import(&cwd.join(path), &cwd.join(destination))
}
Command::MigrateImport {
path,
destination,
generate_alt_text,
} => migration::import(&cwd.join(path), &cwd.join(destination), generate_alt_text),
}
}
Loading
Loading