Skip to content

Commit 2d8e624

Browse files
committed
feat(backend): add tip of day
1 parent 4bdad75 commit 2d8e624

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

packages/backend/src/CoreModule.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ const install = async ({ services, app }) => {
222222

223223
const { DetailProviderService } = require('./services/DetailProviderService');
224224
services.registerService('whoami', DetailProviderService);
225+
226+
const { DevTODService } = require('./services/DevTODService');
227+
services.registerService('__dev-tod', DevTODService);
225228
}
226229

227230
const install_legacy = async ({ services }) => {

packages/backend/src/services/DevConsoleService.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class DevConsoleService extends BaseService {
5757
// if a widget throws an error we MUST remove it;
5858
// it's probably a stack overflow because it's printing.
5959
const to_remove = [];
60+
let positions = [];
6061
for ( const w of this.widgets ) {
6162
let output; try {
6263
output = w();
@@ -66,8 +67,43 @@ class DevConsoleService extends BaseService {
6667
continue;
6768
}
6869
output = Array.isArray(output) ? output : [output];
70+
positions.push([this.static_lines.length, output.length]);
6971
this.static_lines.push(...output);
7072
}
73+
74+
const DESIRED_MIN_OUT = 10;
75+
const size_ok = () =>
76+
process.stdout.rows - DESIRED_MIN_OUT > this.static_lines.length;
77+
let n_hidden = 0;
78+
for ( let i = this.widgets.length-1 ; i >= 0 ; i-- ) {
79+
if ( size_ok() ) break;
80+
const w = this.widgets[i];
81+
if ( ! w.unimportant ) continue;
82+
n_hidden++;
83+
const [start, length] = positions[i];
84+
this.static_lines.splice(start, length);
85+
// update positions
86+
for ( let j = i ; j < positions.length ; j++ ) {
87+
positions[j][0] -= length;
88+
}
89+
}
90+
for ( let i = this.widgets.length-1 ; i >= 0 ; i-- ) {
91+
if ( size_ok() ) break;
92+
n_hidden++;
93+
const w = this.widgets[i];
94+
const [start, length] = positions[i];
95+
this.static_lines.splice(start, length);
96+
}
97+
if ( n_hidden && size_ok() ) {
98+
this.static_lines.push(
99+
`\x1B[33m` +
100+
this.generateEnd(
101+
`[ ${n_hidden} widget${n_hidden === 1 ? '' : 's'} hidden ]`
102+
) +
103+
`\x1B[0m`
104+
);
105+
}
106+
71107
if (!this.arrays_equal(initialOutput, this.static_lines)) {
72108
this.mark_updated(); // Update only if outputs have changed
73109
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
const { surrounding_box } = require("../fun/dev-console-ui-utils");
2+
const BaseService = require("./BaseService");
3+
4+
const SOURCE_CODE_TIPS = `
5+
Most services are registered in CoreModule.js
6+
Boot sequence events are different from service events
7+
ExpectationService exists to ensure Puter doesn't miss a step
8+
Services are composable; StrategyService is a good example
9+
API endpoints should be on a separate origin in production
10+
There is some limited query-building in packages/backend/src/om
11+
`;
12+
13+
const tips = (
14+
// CLI tips
15+
`
16+
Type \`help\` to see a list of commands
17+
\`logs:show\` toggles log output; useful when typing long commands
18+
\`logs:indent \` toggles indentation for some logs
19+
\`lock:locks \` will list any active mutex locks
20+
`,
21+
// Source code tips
22+
`
23+
Most services are registered in CoreModule.js
24+
Boot sequence events are different from service events
25+
ExpectationService exists to ensure Puter doesn't miss a step
26+
Services are composable; StrategyService is a good example
27+
API endpoints should be on a separate origin in production
28+
These messages come from DevTODService.js
29+
`
30+
).split('\n').map((line) => line.trim())
31+
.filter((line) => line.length)
32+
.map(tip => {
33+
const lines = [];
34+
const WRAP = process.stdout.columns || 50;
35+
while ( tip.length ) {
36+
lines.push(tip.substring(0, WRAP));
37+
tip = tip.substring(WRAP);
38+
}
39+
return lines;
40+
})
41+
42+
class DevTODService extends BaseService {
43+
async _init () {
44+
const svc_commands = this.services.get('commands');
45+
this._register_commands(svc_commands);
46+
}
47+
async ['__on_boot.consolidation'] () {
48+
const random_tip = tips[Math.floor(Math.random() * tips.length)];
49+
this.tod_widget = () => {
50+
const lines = [
51+
"",
52+
"\x1B[1mTip of the Day\x1B[0m",
53+
...random_tip,
54+
"Type tod:dismiss to un-stick this message",
55+
"",
56+
];
57+
surrounding_box('33;1', lines);
58+
return lines;
59+
}
60+
61+
this.tod_widget.unimportant = true;
62+
63+
const svc_devConsole = this.services.get('dev-console', { optional: true });
64+
if ( ! svc_devConsole ) return;
65+
svc_devConsole.add_widget(this.tod_widget);
66+
}
67+
68+
_register_commands (commands) {
69+
commands.registerCommands('tod', [
70+
{
71+
id: 'dismiss',
72+
description: 'Dismiss the startup message',
73+
handler: async (_, log) => {
74+
const svc_devConsole = this.services.get('dev-console', { optional: true });
75+
if ( ! svc_devConsole ) return;
76+
svc_devConsole.remove_widget(this.tod_widget);
77+
const lines = this.tod_widget();
78+
for ( const line of lines ) log.log(line);
79+
this.tod_widget = null;
80+
}
81+
}
82+
]);
83+
}
84+
}
85+
86+
module.exports = { DevTODService };

0 commit comments

Comments
 (0)