slidict 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +22 -0
- data/.github/workflows/version-check.yml +70 -0
- data/CHANGELOG.md +5 -0
- data/README.md +31 -1
- data/data/slidict/methods/aidma.yml +35 -0
- data/data/slidict/methods/desc.yml +32 -0
- data/data/slidict/methods/fabe.yml +32 -0
- data/data/slidict/methods/sds.yml +28 -0
- data/data/slidict/methods/taps.yml +32 -0
- data/docs/presentation-methods.md +8 -1
- data/lib/slidict/cli/app.rb +42 -7
- data/lib/slidict/deck.rb +4 -2
- data/lib/slidict/generator.rb +45 -0
- data/lib/slidict/llm/client.rb +14 -0
- data/lib/slidict/version.rb +1 -1
- data/lib/slidict.rb +1 -0
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab47129a915cbf21d2a573c0f1d3f9f9b654eb86a6a4226261a94fc44a60ba16
|
|
4
|
+
data.tar.gz: f0ab819d3077c1bfa36f268dc06939c7f02bd483649be86d360d5558a2dded34
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 173dc71c06cf303b382fe7527ec27c8ddb61a9c3ced94563914fe2b56121af308b8d3f6290597800c32086d6ba6b51e8dc91dc1128a6776abf8358e0e1a113dc
|
|
7
|
+
data.tar.gz: e76f87bdd14452ce271dfa20cf5f8af65c5f82a3b687b53958d8e8a2cf45f022b79ba0ee2dd2dc753a02b1dcb44c4032bee8bfcebb2adbaf7512e77dcba7d68a
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this PR change, and why? -->
|
|
4
|
+
|
|
5
|
+
## Version bump
|
|
6
|
+
|
|
7
|
+
This repository follows [Semantic Versioning](https://semver.org/) (see `AGENTS.md`).
|
|
8
|
+
Every PR is expected to update `lib/slidict/version.rb`, and CI fails when it does not.
|
|
9
|
+
|
|
10
|
+
- [ ] <!-- skip-version-check --> This PR does not require a version bump
|
|
11
|
+
|
|
12
|
+
<!--
|
|
13
|
+
Leave the box above unchecked and bump `lib/slidict/version.rb` in this PR.
|
|
14
|
+
Check it only when no release is needed (docs-only, CI-only, test-only changes, etc.).
|
|
15
|
+
Keep the `skip-version-check` comment intact — the CI check looks for it.
|
|
16
|
+
-->
|
|
17
|
+
|
|
18
|
+
## Checklist
|
|
19
|
+
|
|
20
|
+
- [ ] `bundle exec rspec` passes
|
|
21
|
+
- [ ] `bundle exec rubocop` passes
|
|
22
|
+
- [ ] `README.md` updated if user-facing behavior changed
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: Version Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types:
|
|
6
|
+
- opened
|
|
7
|
+
- reopened
|
|
8
|
+
- synchronize
|
|
9
|
+
- edited
|
|
10
|
+
- ready_for_review
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pull-requests: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
version-file:
|
|
18
|
+
name: Require version.rb update
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- name: Require version.rb update or an explicit skip
|
|
23
|
+
env:
|
|
24
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
25
|
+
REPO: ${{ github.repository }}
|
|
26
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
27
|
+
PR_BODY: ${{ github.event.pull_request.body }}
|
|
28
|
+
PR_ACTOR: ${{ github.event.pull_request.user.login }}
|
|
29
|
+
VERSION_FILE: lib/slidict/version.rb
|
|
30
|
+
run: |
|
|
31
|
+
set -euo pipefail
|
|
32
|
+
|
|
33
|
+
# Dependency update PRs are opened by bots that cannot fill in the
|
|
34
|
+
# pull request template, so they are exempt from this check.
|
|
35
|
+
case "$PR_ACTOR" in
|
|
36
|
+
'dependabot[bot]' | 'github-actions[bot]')
|
|
37
|
+
echo "::notice::Skipping version check for bot author '${PR_ACTOR}'."
|
|
38
|
+
exit 0
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
# The pull request template ships a checkbox carrying the
|
|
43
|
+
# `skip-version-check` marker, unchecked by default.
|
|
44
|
+
if printf '%s\n' "${PR_BODY:-}" |
|
|
45
|
+
grep -qiE '^[[:space:]]*[-*+][[:space:]]+\[[[:space:]]*x[[:space:]]*\].*skip-version-check'; then
|
|
46
|
+
echo "::notice::'${VERSION_FILE}' is not required: the skip-version-check box is checked."
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
changed_files="$(gh api --paginate "repos/${REPO}/pulls/${PR_NUMBER}/files" --jq '.[].filename')"
|
|
51
|
+
|
|
52
|
+
if printf '%s\n' "$changed_files" | grep -qxF "$VERSION_FILE"; then
|
|
53
|
+
echo "::notice::'${VERSION_FILE}' is updated in this pull request."
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
echo "::error::'${VERSION_FILE}' is not updated in this pull request."
|
|
58
|
+
{
|
|
59
|
+
echo "### Version check failed"
|
|
60
|
+
echo
|
|
61
|
+
echo "This pull request does not update \`${VERSION_FILE}\`."
|
|
62
|
+
echo
|
|
63
|
+
echo "Do one of the following:"
|
|
64
|
+
echo
|
|
65
|
+
echo "- Bump \`VERSION\` in \`${VERSION_FILE}\` following [SemVer](https://semver.org/) (see \`AGENTS.md\`), or"
|
|
66
|
+
echo "- Check the \"This PR does not require a version bump\" box in the pull request description."
|
|
67
|
+
echo
|
|
68
|
+
echo "Editing the description re-runs this check automatically."
|
|
69
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
70
|
+
exit 1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.7.0] - 2026-07-30
|
|
4
|
+
|
|
5
|
+
- Add built-in SDS, DESC, AIDMA, TAPS, and FABE presentation methods.
|
|
6
|
+
- Add source-text slide generation through `--text`, `--text-file`, and the reusable
|
|
7
|
+
`Slidict::Generator` integration API.
|
|
3
8
|
- Add `slidict init` to create a `.env` file (for `SLIDICT_LLM_*`, `SLIDICT_FRAMEWORK`,
|
|
4
9
|
and `SLIDICT_METHOD`) and add it to `.gitignore`. `.env` is loaded automatically and
|
|
5
10
|
fills in unset environment variables; CLI flags still take precedence.
|
data/README.md
CHANGED
|
@@ -63,6 +63,34 @@ bin/slidict \
|
|
|
63
63
|
--output slides.adoc
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
To create a presentation from an existing article, memo, or other prose, pass the
|
|
67
|
+
text directly or read it from a file. Source-text generation requires an LLM endpoint;
|
|
68
|
+
the first non-empty line is used as the topic when `--topic` is omitted. Slidict asks
|
|
69
|
+
the model to preserve the source's facts and examples instead of inventing claims.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
bin/slidict --text-file proposal.md \
|
|
73
|
+
--llm-base-url https://api.openai.com/v1 \
|
|
74
|
+
--llm-api-key "$OPENAI_API_KEY" --llm-model gpt-4o-mini
|
|
75
|
+
|
|
76
|
+
bin/slidict --text "Why our team should adopt automated accessibility testing ..." \
|
|
77
|
+
--llm-base-url http://localhost:11434/v1 --llm-api-key ollama --llm-model llama3
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Applications can use the same pipeline without CLI or filesystem side effects. The
|
|
81
|
+
result contains both the structured `deck` and rendered presentation `content`:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
client = Slidict::Llm::Client.new(base_url: url, api_key: key, model: model)
|
|
85
|
+
result = Slidict::Generator.new(client: client).generate(
|
|
86
|
+
text: article,
|
|
87
|
+
framework: "slidev",
|
|
88
|
+
language: "Japanese"
|
|
89
|
+
)
|
|
90
|
+
result.deck # => Slidict::Deck
|
|
91
|
+
result.content # => Slidev source
|
|
92
|
+
```
|
|
93
|
+
|
|
66
94
|
Add `--publish` to also save the generated slides to slidict.io as a draft (requires
|
|
67
95
|
`slidict auth` first). Pass `--slide-id` to edit an existing draft instead of creating a
|
|
68
96
|
new one:
|
|
@@ -271,10 +299,12 @@ We optimize for communication, not decoration.
|
|
|
271
299
|
|
|
272
300
|
## Presentation methods
|
|
273
301
|
|
|
274
|
-
Use `--method` to generate slides with a specific narrative structure
|
|
302
|
+
Use `--method` to generate slides with a specific narrative structure. Built-in methods include SDS, PREP, DESC,
|
|
303
|
+
AIDMA, TAPS, FABE, SCQA, and Pyramid Principle.
|
|
275
304
|
|
|
276
305
|
```bash
|
|
277
306
|
bin/slidict --method scqa --topic "Database migration plan"
|
|
307
|
+
bin/slidict --method aidma --topic "Product launch"
|
|
278
308
|
bin/slidict list-methods
|
|
279
309
|
bin/slidict show-method scqa
|
|
280
310
|
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
id: aidma
|
|
2
|
+
name: AIDMA
|
|
3
|
+
category: marketing
|
|
4
|
+
description: Attention, Interest, Desire, Memory, Action structure for guiding an audience from awareness to action.
|
|
5
|
+
locale: en
|
|
6
|
+
suitable_for:
|
|
7
|
+
- Marketing presentations
|
|
8
|
+
- Product and service pitches
|
|
9
|
+
- Campaign or launch proposals
|
|
10
|
+
slides:
|
|
11
|
+
- title: Attention
|
|
12
|
+
role: Capture attention with an audience-relevant hook.
|
|
13
|
+
instructions: Open with a striking fact, question, or situation connected to the audience's needs.
|
|
14
|
+
- title: Interest
|
|
15
|
+
role: Build interest by showing why the subject is relevant.
|
|
16
|
+
instructions: Connect the offer or idea to a recognizable problem, opportunity, or aspiration.
|
|
17
|
+
- title: Desire
|
|
18
|
+
role: Turn interest into a preference for the proposed solution.
|
|
19
|
+
instructions: Show concrete benefits and help the audience imagine the improved outcome.
|
|
20
|
+
- title: Memory
|
|
21
|
+
role: Make the key message easy to recall.
|
|
22
|
+
instructions: Reinforce one distinctive promise with memorable evidence, phrasing, or imagery.
|
|
23
|
+
- title: Action
|
|
24
|
+
role: Prompt the audience to take a clear next step.
|
|
25
|
+
instructions: State one specific action, remove ambiguity, and explain what happens next.
|
|
26
|
+
ai_instructions:
|
|
27
|
+
- Move through the AIDMA stages in order without asking for action too early.
|
|
28
|
+
- Frame benefits from the audience's point of view.
|
|
29
|
+
- Keep the memorable promise consistent with the evidence and call to action.
|
|
30
|
+
review_checklist:
|
|
31
|
+
- Does the opening earn the target audience's attention?
|
|
32
|
+
- Does interest develop into a concrete desire?
|
|
33
|
+
- Is the central promise distinctive and memorable?
|
|
34
|
+
- Is the requested action specific and easy to understand?
|
|
35
|
+
references: []
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
id: desc
|
|
2
|
+
name: DESC
|
|
3
|
+
category: assertive-communication
|
|
4
|
+
description: Describe, Express, Specify, Consequences structure for proposing a constructive change or resolving conflict.
|
|
5
|
+
locale: en
|
|
6
|
+
suitable_for:
|
|
7
|
+
- Feedback and difficult conversations
|
|
8
|
+
- Conflict resolution
|
|
9
|
+
- Requests for behavioral or process change
|
|
10
|
+
slides:
|
|
11
|
+
- title: Describe
|
|
12
|
+
role: Describe the situation or behavior objectively.
|
|
13
|
+
instructions: State observable facts without blame, assumptions, or judgment.
|
|
14
|
+
- title: Express
|
|
15
|
+
role: Explain the impact and the speaker's perspective or feelings.
|
|
16
|
+
instructions: Use specific first-person language to show why the situation matters.
|
|
17
|
+
- title: Specify
|
|
18
|
+
role: Request a concrete and achievable change.
|
|
19
|
+
instructions: Define the desired behavior, owner, and timing so the request is actionable.
|
|
20
|
+
- title: Consequences
|
|
21
|
+
role: Clarify the positive outcomes of the change and the implications of inaction.
|
|
22
|
+
instructions: Emphasize realistic benefits and consequences rather than threats.
|
|
23
|
+
ai_instructions:
|
|
24
|
+
- Separate observable facts from interpretations and emotions.
|
|
25
|
+
- Keep the tone respectful, direct, and solution-oriented.
|
|
26
|
+
- Make the requested change specific enough to act on.
|
|
27
|
+
review_checklist:
|
|
28
|
+
- Is the description factual and nonjudgmental?
|
|
29
|
+
- Is the impact expressed clearly from the speaker's perspective?
|
|
30
|
+
- Is the requested change concrete and achievable?
|
|
31
|
+
- Are the consequences realistic and constructive?
|
|
32
|
+
references: []
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
id: fabe
|
|
2
|
+
name: FABE
|
|
3
|
+
category: sales
|
|
4
|
+
description: Features, Advantages, Benefits, Evidence structure for connecting an offering's capabilities to credible customer value.
|
|
5
|
+
locale: en
|
|
6
|
+
suitable_for:
|
|
7
|
+
- Sales presentations
|
|
8
|
+
- Product demonstrations
|
|
9
|
+
- Solution comparisons
|
|
10
|
+
slides:
|
|
11
|
+
- title: Features
|
|
12
|
+
role: Introduce the relevant capabilities or attributes of the offering.
|
|
13
|
+
instructions: Select only features connected to the audience's needs and describe them precisely.
|
|
14
|
+
- title: Advantages
|
|
15
|
+
role: Explain how those features perform better or differently.
|
|
16
|
+
instructions: Compare the capabilities with the current approach or alternatives without unsupported claims.
|
|
17
|
+
- title: Benefits
|
|
18
|
+
role: Translate the advantages into outcomes the audience values.
|
|
19
|
+
instructions: State tangible functional or emotional value in the audience's terms.
|
|
20
|
+
- title: Evidence
|
|
21
|
+
role: Prove the promised benefits with credible support.
|
|
22
|
+
instructions: Use relevant data, demonstrations, testimonials, or case results and identify their context.
|
|
23
|
+
ai_instructions:
|
|
24
|
+
- Maintain a clear chain from each feature to its advantage, benefit, and evidence.
|
|
25
|
+
- Prioritize audience value over a long inventory of specifications.
|
|
26
|
+
- Avoid claims that the evidence does not substantiate.
|
|
27
|
+
review_checklist:
|
|
28
|
+
- Are the selected features relevant to the audience's needs?
|
|
29
|
+
- Are advantages differentiated clearly and fairly?
|
|
30
|
+
- Are benefits expressed as audience outcomes?
|
|
31
|
+
- Is every important claim supported by credible evidence?
|
|
32
|
+
references: []
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
id: sds
|
|
2
|
+
name: SDS
|
|
3
|
+
category: concise-communication
|
|
4
|
+
description: Summary, Details, Summary structure for communicating a message clearly and memorably.
|
|
5
|
+
locale: en
|
|
6
|
+
suitable_for:
|
|
7
|
+
- Short presentations and status updates
|
|
8
|
+
- Explanations that need a clear takeaway
|
|
9
|
+
- Messages for time-constrained audiences
|
|
10
|
+
slides:
|
|
11
|
+
- title: Summary
|
|
12
|
+
role: State the central message or conclusion at the outset.
|
|
13
|
+
instructions: Give the audience a concise overview of what they should understand or remember.
|
|
14
|
+
- title: Details
|
|
15
|
+
role: Support the opening summary with the essential facts, reasoning, or examples.
|
|
16
|
+
instructions: Present only the details needed to substantiate the central message, in a logical order.
|
|
17
|
+
- title: Summary Revisited
|
|
18
|
+
role: Reinforce the central message after the supporting detail.
|
|
19
|
+
instructions: Restate the takeaway in fresh, concise language and connect it to the desired response.
|
|
20
|
+
ai_instructions:
|
|
21
|
+
- Preserve the Summary, Details, Summary sequence.
|
|
22
|
+
- Keep the opening and closing summaries consistent while avoiding word-for-word repetition.
|
|
23
|
+
- Include only details that directly support the central message.
|
|
24
|
+
review_checklist:
|
|
25
|
+
- Is the central message clear in the opening summary?
|
|
26
|
+
- Do all details support that message?
|
|
27
|
+
- Does the closing summary reinforce the intended takeaway?
|
|
28
|
+
references: []
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
id: taps
|
|
2
|
+
name: TAPS
|
|
3
|
+
category: problem-solving
|
|
4
|
+
description: To Be, As Is, Problem, Solution structure for defining a desired state and the change needed to reach it.
|
|
5
|
+
locale: en
|
|
6
|
+
suitable_for:
|
|
7
|
+
- Improvement proposals
|
|
8
|
+
- Transformation roadmaps
|
|
9
|
+
- Problem-solving presentations
|
|
10
|
+
slides:
|
|
11
|
+
- title: To Be
|
|
12
|
+
role: Define the desired future state and its success criteria.
|
|
13
|
+
instructions: Describe a concrete, audience-relevant outcome rather than a vague aspiration.
|
|
14
|
+
- title: As Is
|
|
15
|
+
role: Establish the current state with objective evidence.
|
|
16
|
+
instructions: Show the present performance or behavior using facts that can be compared with the desired state.
|
|
17
|
+
- title: Problem
|
|
18
|
+
role: Identify the gap and the factors preventing progress.
|
|
19
|
+
instructions: Explain the most important root cause or barrier, not merely its symptoms.
|
|
20
|
+
- title: Solution
|
|
21
|
+
role: Present the actions that will close the gap.
|
|
22
|
+
instructions: Connect each proposed action to the identified problem and specify the immediate next step.
|
|
23
|
+
ai_instructions:
|
|
24
|
+
- Make the gap between To Be and As Is explicit and measurable where possible.
|
|
25
|
+
- Distinguish root causes from symptoms.
|
|
26
|
+
- Ensure the solution directly addresses the stated problem.
|
|
27
|
+
review_checklist:
|
|
28
|
+
- Is the desired state concrete and relevant?
|
|
29
|
+
- Is the current state supported by evidence?
|
|
30
|
+
- Does the problem explain the gap between the two states?
|
|
31
|
+
- Does the solution address the root cause and define a next step?
|
|
32
|
+
references: []
|
|
@@ -10,11 +10,13 @@ gem only loads, validates, and applies that data.
|
|
|
10
10
|
bin/slidict new --method prep --topic "New onboarding flow"
|
|
11
11
|
bin/slidict --method scqa --topic "Database migration plan"
|
|
12
12
|
bin/slidict --method pyramid --topic "FY roadmap recommendation"
|
|
13
|
+
bin/slidict --method fabe --topic "Customer analytics platform"
|
|
13
14
|
bin/slidict list-methods
|
|
14
15
|
bin/slidict show-method scqa
|
|
15
16
|
```
|
|
16
17
|
|
|
17
|
-
Built-in methods currently include
|
|
18
|
+
Built-in methods currently include SDS, PREP, DESC, AIDMA, TAPS, FABE, SCQA,
|
|
19
|
+
and Pyramid Principle.
|
|
18
20
|
|
|
19
21
|
## Architecture
|
|
20
22
|
|
|
@@ -32,9 +34,14 @@ This keeps contribution review small: a new built-in method is usually one YAML
|
|
|
32
34
|
|
|
33
35
|
```text
|
|
34
36
|
data/slidict/methods/
|
|
37
|
+
aidma.yml
|
|
38
|
+
desc.yml
|
|
39
|
+
fabe.yml
|
|
35
40
|
prep.yml
|
|
36
41
|
pyramid.yml
|
|
37
42
|
scqa.yml
|
|
43
|
+
sds.yml
|
|
44
|
+
taps.yml
|
|
38
45
|
lib/slidict/presentation_method.rb
|
|
39
46
|
```
|
|
40
47
|
|
data/lib/slidict/cli/app.rb
CHANGED
|
@@ -32,6 +32,8 @@ module Slidict
|
|
|
32
32
|
flag "--duration", arg: "TEXT", desc: 'Presentation length, for example "5 minutes"'
|
|
33
33
|
flag "--audience", arg: "TEXT", desc: "Target audience"
|
|
34
34
|
flag "--goal", arg: "TEXT", desc: "Desired audience takeaway or action"
|
|
35
|
+
flag "--text", arg: "TEXT", desc: "Source text to turn into slides"
|
|
36
|
+
flag "--text-file", arg: "PATH", desc: "Read source text from a file"
|
|
35
37
|
flag "--framework", arg: "NAME",
|
|
36
38
|
desc: -> { "#{Output::Format.names.join(", ")} (default: slidev)\n(env: SLIDICT_FRAMEWORK)" }
|
|
37
39
|
flag "--method", arg: "ID", desc: "Presentation method, for example scqa, prep, or pyramid\n" \
|
|
@@ -73,17 +75,23 @@ module Slidict
|
|
|
73
75
|
config = build_config(options)
|
|
74
76
|
return print_available_models(config) if config.llm_enabled? && config.model.nil?
|
|
75
77
|
|
|
78
|
+
# Validate and read local input before making a network request. This
|
|
79
|
+
# keeps missing/empty source-file errors fast and deterministic.
|
|
80
|
+
options[:text] = read_source_text(options)
|
|
76
81
|
client = llm_client_for(config)
|
|
77
82
|
return FAILURE if client && !verify_connection(client)
|
|
78
83
|
|
|
84
|
+
raise ArgumentError, "--text and --text-file require an LLM endpoint" if options[:text] && !client
|
|
85
|
+
|
|
79
86
|
questions = questions_for(client, options)
|
|
80
87
|
deck = Deck.new(
|
|
81
|
-
topic: ask(questions[:topic], options[:topic]),
|
|
82
|
-
duration: ask(questions[:duration], options[:duration]),
|
|
83
|
-
audience: ask(questions[:audience], options[:audience]),
|
|
84
|
-
goal: ask(questions[:goal], options[:goal]),
|
|
88
|
+
topic: source_topic(options) || ask(questions[:topic], options[:topic]),
|
|
89
|
+
duration: options[:text] ? options[:duration] : ask(questions[:duration], options[:duration]),
|
|
90
|
+
audience: options[:text] ? options[:audience] : ask(questions[:audience], options[:audience]),
|
|
91
|
+
goal: options[:text] ? options[:goal] : ask(questions[:goal], options[:goal]),
|
|
85
92
|
framework: options[:framework],
|
|
86
|
-
presentation_method: options[:presentation_method]
|
|
93
|
+
presentation_method: options[:presentation_method],
|
|
94
|
+
source: options[:text]
|
|
87
95
|
)
|
|
88
96
|
|
|
89
97
|
if client
|
|
@@ -95,7 +103,8 @@ module Slidict
|
|
|
95
103
|
end
|
|
96
104
|
deck = Deck.new(
|
|
97
105
|
topic: deck.topic, duration: deck.duration, audience: deck.audience, goal: deck.goal,
|
|
98
|
-
framework: deck.framework, slides: slides, presentation_method: deck.presentation_method
|
|
106
|
+
framework: deck.framework, slides: slides, presentation_method: deck.presentation_method,
|
|
107
|
+
source: deck.source
|
|
99
108
|
)
|
|
100
109
|
end
|
|
101
110
|
|
|
@@ -117,6 +126,28 @@ module Slidict
|
|
|
117
126
|
|
|
118
127
|
private
|
|
119
128
|
|
|
129
|
+
def read_source_text(options)
|
|
130
|
+
raise ArgumentError, "specify only one of --text or --text-file" if options[:text] && options[:text_file]
|
|
131
|
+
unless options[:text_file]
|
|
132
|
+
raise ArgumentError, "source text must not be empty" if options.key?(:text) && options[:text].to_s.strip.empty?
|
|
133
|
+
|
|
134
|
+
return options[:text]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
File.read(options[:text_file]).tap do |text|
|
|
138
|
+
raise ArgumentError, "source text must not be empty" if text.strip.empty?
|
|
139
|
+
end
|
|
140
|
+
rescue Errno::ENOENT, Errno::EACCES => e
|
|
141
|
+
raise ArgumentError, "could not read #{options[:text_file]}: #{e.message}"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def source_topic(options)
|
|
145
|
+
return options[:topic] unless options[:topic].to_s.strip.empty?
|
|
146
|
+
return unless options[:text]
|
|
147
|
+
|
|
148
|
+
options[:text].each_line.map(&:strip).find { |line| !line.empty? }&.sub(/\A#+\s*/, "")
|
|
149
|
+
end
|
|
150
|
+
|
|
120
151
|
def parse(argv)
|
|
121
152
|
options = { framework: ENV["SLIDICT_FRAMEWORK"] || "slidev", method: ENV["SLIDICT_METHOD"] }
|
|
122
153
|
args = argv.dup
|
|
@@ -370,7 +401,10 @@ module Slidict
|
|
|
370
401
|
|
|
371
402
|
def fetch_value!(args, option)
|
|
372
403
|
value = args.shift
|
|
373
|
-
|
|
404
|
+
# Source prose can legitimately start with Markdown frontmatter or a
|
|
405
|
+
# list item. Other options retain the typo-friendly flag check.
|
|
406
|
+
missing = value.nil? || (value.start_with?("-") && option != "--text")
|
|
407
|
+
raise ArgumentError, "#{option} requires a value" if missing
|
|
374
408
|
|
|
375
409
|
value
|
|
376
410
|
end
|
|
@@ -389,6 +423,7 @@ module Slidict
|
|
|
389
423
|
# left to ask, or a translation call fails -- this is a nicety, not
|
|
390
424
|
# something worth aborting slide generation over.
|
|
391
425
|
def questions_for(client, options)
|
|
426
|
+
return QUESTIONS if options[:text]
|
|
392
427
|
return QUESTIONS unless client && options[:language]
|
|
393
428
|
|
|
394
429
|
missing = QUESTIONS.select { |key, _| options[key].to_s.strip.empty? }
|
data/lib/slidict/deck.rb
CHANGED
|
@@ -4,15 +4,17 @@ module Slidict
|
|
|
4
4
|
Slide = Struct.new(:title, :bullets, keyword_init: true)
|
|
5
5
|
|
|
6
6
|
class Deck
|
|
7
|
-
attr_reader :topic, :duration, :audience, :goal, :framework, :presentation_method
|
|
7
|
+
attr_reader :topic, :duration, :audience, :goal, :framework, :presentation_method, :source
|
|
8
8
|
|
|
9
|
-
def initialize(topic:, duration:, audience:, goal:, framework: "slidev", slides: nil, presentation_method: nil
|
|
9
|
+
def initialize(topic:, duration:, audience:, goal:, framework: "slidev", slides: nil, presentation_method: nil,
|
|
10
|
+
source: nil)
|
|
10
11
|
@topic = normalize(topic, fallback: "Untitled presentation")
|
|
11
12
|
@duration = normalize(duration, fallback: "5 minutes")
|
|
12
13
|
@audience = normalize(audience, fallback: "general audience")
|
|
13
14
|
@goal = normalize(goal, fallback: "understand the key message")
|
|
14
15
|
@framework = normalize(framework, fallback: "slidev").downcase
|
|
15
16
|
@presentation_method = presentation_method
|
|
17
|
+
@source = source.to_s.strip
|
|
16
18
|
@slides = slides
|
|
17
19
|
end
|
|
18
20
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slidict
|
|
4
|
+
# Framework-independent application API for integrations such as slidict.io.
|
|
5
|
+
# It turns source prose into both a Deck and presentation source without
|
|
6
|
+
# involving CLI input/output or the filesystem.
|
|
7
|
+
class Generator
|
|
8
|
+
Result = Struct.new(:deck, :content, keyword_init: true)
|
|
9
|
+
|
|
10
|
+
def initialize(client:, renderer: Output::Renderer.new)
|
|
11
|
+
@client = client
|
|
12
|
+
@renderer = renderer
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def generate(text:, topic: nil, duration: nil, audience: nil, goal: nil,
|
|
16
|
+
framework: "slidev", language: nil, presentation_method: nil)
|
|
17
|
+
source = text.to_s.strip
|
|
18
|
+
raise ArgumentError, "text must not be empty" if source.empty?
|
|
19
|
+
|
|
20
|
+
deck = Deck.new(
|
|
21
|
+
topic: topic || title_from(source),
|
|
22
|
+
duration: duration,
|
|
23
|
+
audience: audience,
|
|
24
|
+
goal: goal || "communicate the source text clearly",
|
|
25
|
+
framework: framework,
|
|
26
|
+
presentation_method: presentation_method,
|
|
27
|
+
source: source
|
|
28
|
+
)
|
|
29
|
+
slides = @client.generate_slides(deck, language: language)
|
|
30
|
+
generated_deck = Deck.new(
|
|
31
|
+
topic: deck.topic, duration: deck.duration, audience: deck.audience, goal: deck.goal,
|
|
32
|
+
framework: deck.framework, slides: slides, presentation_method: deck.presentation_method,
|
|
33
|
+
source: source
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
Result.new(deck: generated_deck, content: @renderer.render(generated_deck))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def title_from(source)
|
|
42
|
+
source.each_line.map(&:strip).find { |line| !line.empty? }&.sub(/\A#+\s*/, "")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/slidict/llm/client.rb
CHANGED
|
@@ -75,6 +75,7 @@ module Slidict
|
|
|
75
75
|
Audience: #{deck.audience}
|
|
76
76
|
Goal: #{deck.goal}
|
|
77
77
|
#{method_prompt_for(deck)}
|
|
78
|
+
#{source_prompt_for(deck)}
|
|
78
79
|
|
|
79
80
|
Return one slide for each required slide role when a presentation method is
|
|
80
81
|
provided; otherwise return exactly 5 slides. Each item must be an object with
|
|
@@ -84,6 +85,19 @@ module Slidict
|
|
|
84
85
|
PROMPT
|
|
85
86
|
end
|
|
86
87
|
|
|
88
|
+
def source_prompt_for(deck)
|
|
89
|
+
return "" if deck.source.empty?
|
|
90
|
+
|
|
91
|
+
<<~SOURCE
|
|
92
|
+
Source text:
|
|
93
|
+
<source>
|
|
94
|
+
#{deck.source}
|
|
95
|
+
</source>
|
|
96
|
+
Base the presentation on the source text. Identify its central message,
|
|
97
|
+
preserve important facts and examples, and do not invent unsupported claims.
|
|
98
|
+
SOURCE
|
|
99
|
+
end
|
|
100
|
+
|
|
87
101
|
def language_instruction_for(language)
|
|
88
102
|
return "" unless language
|
|
89
103
|
|
data/lib/slidict/version.rb
CHANGED
data/lib/slidict.rb
CHANGED
|
@@ -20,6 +20,7 @@ require_relative "slidict/lint/slide_parser"
|
|
|
20
20
|
require_relative "slidict/llm/client"
|
|
21
21
|
require_relative "slidict/output/format"
|
|
22
22
|
require_relative "slidict/output/renderer"
|
|
23
|
+
require_relative "slidict/generator"
|
|
23
24
|
require_relative "slidict/presentation_method"
|
|
24
25
|
require_relative "slidict/version"
|
|
25
26
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: slidict
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yusuke Abe
|
|
@@ -60,11 +60,13 @@ extensions: []
|
|
|
60
60
|
extra_rdoc_files: []
|
|
61
61
|
files:
|
|
62
62
|
- ".github/dependabot.yml"
|
|
63
|
+
- ".github/pull_request_template.md"
|
|
63
64
|
- ".github/release-drafter.yml"
|
|
64
65
|
- ".github/workflows/bump-version.yml"
|
|
65
66
|
- ".github/workflows/changelog.yml"
|
|
66
67
|
- ".github/workflows/gem-push.yml"
|
|
67
68
|
- ".github/workflows/test.yml"
|
|
69
|
+
- ".github/workflows/version-check.yml"
|
|
68
70
|
- AGENTS.md
|
|
69
71
|
- CHANGELOG.md
|
|
70
72
|
- CODE_OF_CONDUCT.md
|
|
@@ -72,9 +74,14 @@ files:
|
|
|
72
74
|
- LICENSE.txt
|
|
73
75
|
- README.md
|
|
74
76
|
- Rakefile
|
|
77
|
+
- data/slidict/methods/aidma.yml
|
|
78
|
+
- data/slidict/methods/desc.yml
|
|
79
|
+
- data/slidict/methods/fabe.yml
|
|
75
80
|
- data/slidict/methods/prep.yml
|
|
76
81
|
- data/slidict/methods/pyramid.yml
|
|
77
82
|
- data/slidict/methods/scqa.yml
|
|
83
|
+
- data/slidict/methods/sds.yml
|
|
84
|
+
- data/slidict/methods/taps.yml
|
|
78
85
|
- docs/demo-methods.gif
|
|
79
86
|
- docs/demo-methods.tape
|
|
80
87
|
- docs/demo.gif
|
|
@@ -92,6 +99,7 @@ files:
|
|
|
92
99
|
- lib/slidict/external/slidict_io/auth.rb
|
|
93
100
|
- lib/slidict/external/slidict_io/client.rb
|
|
94
101
|
- lib/slidict/external/slidict_io/credentials.rb
|
|
102
|
+
- lib/slidict/generator.rb
|
|
95
103
|
- lib/slidict/lint/finding.rb
|
|
96
104
|
- lib/slidict/lint/linter.rb
|
|
97
105
|
- lib/slidict/lint/renderer.rb
|