Compare commits

...

2 commits

Author SHA1 Message Date
anthony@rabine.fr
59921fe9fd Multiple fixes on project management, fix resource copy bugs
Some checks failed
build-story-editor / build_linux (push) Has been cancelled
build-story-editor / build_win32 (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
2025-01-13 17:52:53 +01:00
anthony@rabine.fr
c23176796f fetch stories list from libraryes ok 2025-01-13 11:58:53 +01:00
12 changed files with 176 additions and 51 deletions

View file

@ -104,7 +104,7 @@ public:
} }
int OutputsCount(const std::string &nodeId) const int OutputsCount(const std::string &nodeId) const
{ {
int count = 0; int count = 0;
for (const auto & l : m_links) for (const auto & l : m_links)
{ {

View file

@ -72,6 +72,8 @@ void StoryProject::New(const std::string &uuid, const std::string &library_path)
std::filesystem::create_directories(m_assetsPath); std::filesystem::create_directories(m_assetsPath);
CreatePage(MainUuid());
m_initialized = true; m_initialized = true;
} }
@ -422,7 +424,7 @@ bool StoryProject::Load(ResourceManager &manager)
if (j.contains("pages")) if (j.contains("pages"))
{ {
ModelFromJson(j["pages"]); ModelFromJson(j);
m_initialized = true; m_initialized = true;
} }
} }
@ -433,6 +435,11 @@ bool StoryProject::Load(ResourceManager &manager)
std::cout << e.what() << std::endl; std::cout << e.what() << std::endl;
} }
if (m_pages.size() == 0)
{
CreatePage(MainUuid());
}
return m_initialized; return m_initialized;
} }

View file

@ -66,6 +66,11 @@ public:
StoryNode *m_tree; StoryNode *m_tree;
*/ */
std::string MainUuid() const {
return "490745ab-df4d-476d-ae27-027e94b8ee0a";
}
void New(const std::string &uuid, const std::string &library_path); void New(const std::string &uuid, const std::string &library_path);
std::filesystem::path BinaryFileName() const; std::filesystem::path BinaryFileName() const;
bool GenerateScript(std::string &codeStr); bool GenerateScript(std::string &codeStr);

View file

@ -210,7 +210,7 @@ $MyArray DV8 10 ; array of 10 bytes
StoryProject proj(m_log); StoryProject proj(m_log);
ResourceManager res(m_log); ResourceManager res(m_log);
std::shared_ptr<StoryPage> page = proj.CreatePage("main"); std::shared_ptr<StoryPage> page = proj.CreatePage(proj.MainUuid());
proj.New(uuid, outputDir); proj.New(uuid, outputDir);
@ -396,7 +396,7 @@ bool PackArchive::ConvertJsonStudioToOst(const std::string &basePath, const std:
StoryProject proj(m_log); StoryProject proj(m_log);
ResourceManager res(m_log); ResourceManager res(m_log);
std::shared_ptr<StoryPage> page = proj.CreatePage("main"); std::shared_ptr<StoryPage> page = proj.CreatePage(proj.MainUuid());
if (j.contains("title")) if (j.contains("title"))
{ {
@ -428,7 +428,7 @@ bool PackArchive::ConvertJsonStudioToOst(const std::string &basePath, const std:
for (const auto & n : j["stageNodes"]) for (const auto & n : j["stageNodes"])
{ {
auto node = proj.CreateNode("main", "media-node"); auto node = proj.CreateNode(proj.MainUuid(), "media-node");
if (node) if (node)
{ {

View file

@ -37,14 +37,14 @@ NodeEditorWindow::~NodeEditorWindow()
m_story.reset(); m_story.reset();
} }
static const std::string gMainUuid = "490745ab-df4d-476d-ae27-027e94b8ee0a";
void NodeEditorWindow::Initialize() void NodeEditorWindow::Initialize()
{ {
m_pages.clear(); m_pages.clear();
m_callStack.clear(); m_callStack.clear();
m_currentPage = std::make_shared<NodeEditorPage>(gMainUuid, "Main"); m_currentPage = std::make_shared<NodeEditorPage>(m_story->MainUuid(), "Main");
m_pages.push_back(m_currentPage); m_pages.push_back(m_currentPage);
m_callStack.push_back(m_currentPage); m_callStack.push_back(m_currentPage);

View file

@ -41,7 +41,7 @@ private:
bool m_loaded{false}; bool m_loaded{false};
// "main" is the entry point editor context. You always need to create one. // "MainUuid" is the entry point editor context. You always need to create one.
// Then each function can have its own editor context, for example if you want to create multiple graphs. // Then each function can have its own editor context, for example if you want to create multiple graphs.
// the key is main, or the UUID of the function // the key is main, or the UUID of the function
std::list<std::shared_ptr<NodeEditorPage>> m_pages; std::list<std::shared_ptr<NodeEditorPage>> m_pages;

View file

@ -29,7 +29,7 @@ void ResourcesWindow::ChooseFile()
m_showImportDialog = false; m_showImportDialog = false;
// open Dialog Simple // open Dialog Simple
IGFD::FileDialogConfig config; IGFD::FileDialogConfig config;
config.path = "."; config.path = m_story.BuildFullAssetsPath("");
config.countSelectionMax = 1; config.countSelectionMax = 1;
config.sidePaneWidth = 350.0f; config.sidePaneWidth = 350.0f;
config.flags = ImGuiFileDialogFlags_Modal; config.flags = ImGuiFileDialogFlags_Modal;
@ -50,7 +50,18 @@ void ResourcesWindow::ChooseFile()
std::filesystem::path p(filePathName); std::filesystem::path p(filePathName);
std::filesystem::path p2 = m_story.BuildFullAssetsPath( p.filename().generic_string()); std::filesystem::path p2 = m_story.BuildFullAssetsPath( p.filename().generic_string());
std::filesystem::copy(p, p2, std::filesystem::copy_options::overwrite_existing);
bool allowCopy = true;
// On ne copie pas le fichier sur lui-même
if (std::filesystem::exists(p) && std::filesystem::exists(p2))
{
allowCopy = !std::filesystem::equivalent(p, p2);
}
if (allowCopy)
{
std::filesystem::copy(p, p2, std::filesystem::copy_options::overwrite_existing);
}
auto res = std::make_shared<Resource>(); auto res = std::make_shared<Resource>();

View file

@ -0,0 +1,47 @@
import { createContext } from "preact";
import { useState } from 'preact/hooks';
import { html } from 'htm/preact';
const AppContext = createContext();
const AppProvider = ({ children }) => {
const [state, setState] = useState({
stories: [
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
{ uuid: "9339d121-ea93-4cc4-9738-9979d43505d0", title: 'Lorem Ipsum', cover: "https://picsum.photos/200/300", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mattis est lectus, scelerisque lobortis neque cursus id. Vestibulum id mollis magna. Etiam vitae est ut mi rutrum congue. Vestibulum a finibus orci. Nunc mattis, risus quis venenatis sagittis, nulla sem sagittis lectus, nec luctus eros neque ut lorem. Nam porttitor a augue vitae venenatis. Vivamus orci nunc, scelerisque ut lacus a, lobortis efficitur nunc. " },
]
});
const setStories = (stories) => {
setState(prevState => ({
...prevState,
stories: [...stories ],
}));
};
// // Fonction pour changer les paramètres
// const updateSettings = (newSettings) => {
// setState(prevState => ({
// ...prevState,
// settings: { ...prevState.settings, ...newSettings },
// }));
// };
return html`
<${AppContext.Provider} value=${{ state, setStories }}>
${children}
</${AppContext.Provider}>
`;
};
export { AppProvider, AppContext };

View file

@ -1,46 +1,46 @@
// Preact
import { useContext, useEffect } from 'preact/hooks';
import { render } from 'preact';
import { html } from 'htm/preact';
// Classes
import apiClient from './classes/api-client.js' import apiClient from './classes/api-client.js'
import eventBus from './classes/event-bus.js'; import eventBus from './classes/event-bus.js';
import storage from './classes/storage.js'; import storage from './classes/storage.js';
// Components
import { render } from 'preact';
import { createContext } from "preact";
import { html } from 'htm/preact';
import TopMenu from './components/TopMenu.js' import TopMenu from './components/TopMenu.js'
import ParametersDialog from './components/ParametersDialog.js'; import ParametersDialog from './components/ParametersDialog.js';
import StoryPlayer from './components/StoryPlayer.js'; import StoryPlayer from './components/StoryPlayer.js';
import StoriesList from './components/StoriesList.js'; import StoriesList from './components/StoriesList.js';
export const AppContext = createContext("appContext"); // Project
import { AppProvider, AppContext } from './app-context.js';
export function App() { export function App() {
// const { setStories } = useContext(AppContext);
const DefaultContext = {
stories: []
};
this.params = storage.getItem('server') || { this.params = storage.getItem('server') || {
serverUrl: '127.0.0.1', serverUrl: '127.0.0.1',
serverPort: 8081, serverPort: 8081,
}; };
storage.setItem('server', this.params); // The useEffect hook with an empty dependency array simulates the componentDidMount lifecycle method.
// It runs the provided function after the component is first rendered.
// try to connect to the server // This is a good place to perform data fetching or initial setup.
apiClient.setBaseUrl(`http://${this.params.serverUrl}:${this.params.serverPort}/api/v1`); useEffect(() => {
apiClient.get('/library/list') console.log("App ready");
.then(data => { return () => {
console.log('Server is up and running', data); console.log("App unmount");
eventBus.publish('server-state-changed', {connected: true}); }
}) }, []);
.catch(error => {
console.log('Server is down');
eventBus.publish('server-state-changed', {connected: false});
});
storage.setItem('server', this.params);
apiClient.setBaseUrl(`http://${this.params.serverUrl}:${this.params.serverPort}/api/v1`);
return html` return html`
<AppContext.Provider value="${DefaultContext}"> <${AppProvider}>
<${TopMenu} /> <${TopMenu} />
<${ParametersDialog} /> <${ParametersDialog} />
@ -48,7 +48,7 @@ export function App() {
<${StoryPlayer} /> <${StoryPlayer} />
<${StoriesList} /> <${StoriesList} />
</div> </div>
</AppContext.Provider> </${AppProvider}>
`; `;
} }

View file

@ -1,22 +1,60 @@
import { html } from 'htm/preact'; import { html } from 'htm/preact';
// Classes
import eventBus from '../classes/event-bus.js'; import eventBus from '../classes/event-bus.js';
import { useState } from 'preact/hooks'; import apiClient from '../classes/api-client.js';
import { AppContext } from '../app-context.js';
import { useContext, useEffect } from 'preact/hooks'
function StoriesList() { function StoriesList() {
const { state, setStories } = useContext(AppContext);
useEffect(() => {
console.log("Fetch stories");
getStories();
return () => {
console.log("Component will unmount");
}
}, []);
function getStories() {
// try to connect to the server
apiClient.get('/library/list')
.then(data => {
console.log('Server is up and running', data);
eventBus.publish('server-state-changed', {connected: true});
setStories(data);
})
.catch(error => {
console.log('Api error: ' + error);
eventBus.publish('server-state-changed', {connected: false});
});
}
function handleClickOnStory() {
console.log("Clicked")
}
return html` return html`
<style> <style>
.card-container { .card-container {
max-height: 80vh; max-height: 80vh;
overflow-y: auto; overflow-y: auto;
padding: 20px; padding: 4px;
width: 400px;
} }
.card { .card {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.card-content { .card-content {
@ -33,6 +71,12 @@ function StoriesList() {
.card-description { .card-description {
font-size: 0.9em; font-size: 0.9em;
margin: 0; margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 220px;
} }
.card-image { .card-image {
@ -42,16 +86,18 @@ function StoriesList() {
border-radius: 10px; border-radius: 10px;
} }
</style> </style>
<div> <div style="max-height: 400px; overflow-y: auto; border: 1px solid #ccc; ">
<div class="card-container"> ${state.stories.map(story => html`
<div class="block card"> <div class="card-container" onClick="${handleClickOnStory}">
<div class="card-content"> <div class="block card">
<h2 class="card-title">Titre de la Carte 1</h2> <div class="card-content">
<p class="card-description">Description de la carte 1. Voici une brève description pour cette carte.</p> <h2 class="card-title">${story.title}</h2>
<p class="card-description">${story.description}</p>
</div>
<img src="https://placehold.co/100x75" alt="Image de la carte 1" class="card-image" />
</div> </div>
<img src="https://placehold.co/100x75" alt="Image de la carte 1" class="card-image" />
</div> </div>
</div> `)}
</div> </div>
`; `;

View file

@ -33,6 +33,18 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
line-height: 1.6; line-height: 1.6;
--block-accent-color: #3a5ec5;
/* --block-text-color: #f8f8f8;
--block-background-color: #222;
--block-accent-color: #f2d12e;
--block-shadow-color: #555; */
background: #abcdef;
/* margin: 4px auto;
max-width: 800px;
color: #222;
--block-accent-color: #3a5ec5; */
} }
</style> </style>
</head> </head>
@ -42,4 +54,4 @@
<script src="storyvm.js"></script> <script src="storyvm.js"></script>
<script src="app.js" type="module"></script> <script src="app.js" type="module"></script>
</body> </body>
</html> </html>

View file

@ -1,11 +1,6 @@
/* Extra styles to make the demo look nicer. /* Extra styles to make the demo look nicer.
* Feel free to look around! */ * Feel free to look around! */
main {
margin: 4px auto;
max-width: 800px;
color: #222;
}
@media only screen and (max-width: 824px) { @media only screen and (max-width: 824px) {
main { main {
@ -52,3 +47,5 @@ a {
input { input {
border: 0; border: 0;
} }